1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2025-08-05 13:24:52 +00:00

Deploying to gh-pages from @ sehugg/8bitworkshop@3a960c3e6e 🚀

This commit is contained in:
sehugg
2022-02-21 15:35:52 +00:00
parent 4583cd1896
commit c2cec1701e
1873 changed files with 844165 additions and 0 deletions

255
gen/common/analysis.js Normal file
View File

@@ -0,0 +1,255 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeAnalyzer_apple2 = exports.CodeAnalyzer_nes = exports.CodeAnalyzer_vcs = void 0;
const util_1 = require("./util");
/// VCS TIMING ANALYSIS
// [taken, not taken]
const BRANCH_CONSTRAINTS = [
[{ N: 0 }, { N: 1 }],
[{ N: 1 }, { N: 0 }],
[{ V: 0 }, { V: 1 }],
[{ V: 1 }, { V: 0 }],
[{ C: 0 }, { C: 1 }],
[{ C: 1 }, { C: 0 }],
[{ Z: 0 }, { Z: 1 }],
[{ Z: 1 }, { Z: 0 }]
];
function constraintEquals(a, b) {
if (a == null || b == null)
return null;
for (var n in a) {
if (b[n] !== 'undefined')
return a[n] == b[n];
}
for (var n in b) {
if (a[n] !== 'undefined')
return a[n] == b[n];
}
return null;
}
class CodeAnalyzer6502 {
constructor(platform) {
this.pc2minclocks = {};
this.pc2maxclocks = {};
this.jsrresult = {};
this.MAX_CYCLES = 2000;
this.platform = platform;
}
getClockCountsAtPC(pc) {
var opcode = this.platform.readAddress(pc);
var meta = this.platform.getOpcodeMetadata(opcode, pc);
return meta; // minCycles, maxCycles
}
traceInstructions(pc, minclocks, maxclocks, subaddr, constraints) {
if (this.WRAP_CLOCKS) {
if (this.pc2minclocks[pc] !== undefined)
minclocks = Math.min(minclocks, this.pc2minclocks[pc]);
if (this.pc2maxclocks[pc] !== undefined)
maxclocks = Math.max(maxclocks, this.pc2maxclocks[pc]);
}
//console.log("trace", hex(pc), minclocks, maxclocks);
if (!constraints)
constraints = {};
var modified = true;
var abort = false;
for (var i = 0; modified && !abort; i++) {
if (i >= this.MAX_CYCLES) {
console.log("too many cycles @", (0, util_1.hex)(pc), "routine", (0, util_1.hex)(subaddr));
break;
}
modified = false;
if (this.WRAP_CLOCKS && minclocks >= this.MAX_CLOCKS) {
// wrap clocks
minclocks = minclocks % this.MAX_CLOCKS;
maxclocks = maxclocks % this.MAX_CLOCKS;
}
else {
// truncate clocks
minclocks = Math.min(this.MAX_CLOCKS, minclocks);
maxclocks = Math.min(this.MAX_CLOCKS, maxclocks);
}
var meta = this.getClockCountsAtPC(pc);
var lob = this.platform.readAddress(pc + 1);
var hib = this.platform.readAddress(pc + 2);
var addr = lob + (hib << 8);
var pc0 = pc;
if (!(minclocks >= this.pc2minclocks[pc0])) {
this.pc2minclocks[pc0] = minclocks;
modified = true;
}
if (!(maxclocks <= this.pc2maxclocks[pc0])) {
this.pc2maxclocks[pc0] = maxclocks;
modified = true;
}
//console.log(hex(pc),minclocks,maxclocks,modified,meta,constraints);
if (!meta.insnlength) {
console.log("Illegal instruction!", (0, util_1.hex)(pc), (0, util_1.hex)(meta.opcode), meta);
break;
}
pc += meta.insnlength;
var oldconstraints = constraints;
constraints = null;
// TODO: if jump to zero-page, maybe assume RTS?
switch (meta.opcode) {
case 0x19:
case 0x1d:
case 0x39:
case 0x3d:
case 0x59:
case 0x5d:
case 0x79:
case 0x7d:
case 0x99:
case 0x9d:
case 0xa9:
case 0xad:
case 0xb9:
case 0xbd:
case 0xbc:
case 0xbe:
case 0xd9:
case 0xdd:
case 0xf9:
case 0xfd:
if (lob == 0)
meta.maxCycles -= 1; // no page boundary crossed
break;
// TODO: only VCS
case 0x85:
if (lob == 0x2) { // STA WSYNC
minclocks = maxclocks = 0;
meta.minCycles = meta.maxCycles = 0;
}
break;
// TODO: only NES (sprite 0 poll)
case 0x2c:
if (lob == 0x02 && hib == 0x20) { // BIT $2002
minclocks = 0;
maxclocks = 4; // uncertainty b/c of assumed branch poll
meta.minCycles = meta.maxCycles = 0;
}
break;
// TODO: only Apple2 (vapor lock)
/*
case 0xad:
if (lob == 0x61 && hib == 0xc0) { // LDA $C061
minclocks = 0;
maxclocks = 4; // uncertainty?
meta.minCycles = meta.maxCycles = 0;
}
break;
*/
case 0x20: // JSR
// TODO: handle bare RTS case
minclocks += meta.minCycles;
maxclocks += meta.maxCycles;
this.traceInstructions(addr, minclocks, maxclocks, addr, constraints);
var result = this.jsrresult[addr];
if (result) {
minclocks = result.minclocks;
maxclocks = result.maxclocks;
}
else {
console.log("No JSR result!", (0, util_1.hex)(pc), (0, util_1.hex)(addr));
minclocks = maxclocks;
//return;
}
break;
case 0x4c: // JMP
pc = addr; // TODO: make sure in ROM space
break;
case 0x40: // RTI
abort = true;
break;
case 0x60: // RTS
if (subaddr) { // TODO: 0 doesn't work
// TODO: combine with previous result
var result = this.jsrresult[subaddr];
if (!result) {
result = { minclocks: minclocks, maxclocks: maxclocks };
}
else {
result = {
minclocks: Math.min(minclocks, result.minclocks),
maxclocks: Math.max(maxclocks, result.maxclocks)
};
}
this.jsrresult[subaddr] = result;
console.log("RTS", (0, util_1.hex)(pc), (0, util_1.hex)(subaddr), this.jsrresult[subaddr]);
}
return;
case 0x10:
case 0x30: // branch
case 0x50:
case 0x70:
case 0x90:
case 0xB0:
case 0xD0:
case 0xF0:
var newpc = pc + (0, util_1.byte2signed)(lob);
var crosspage = (pc >> 8) != (newpc >> 8);
if (!crosspage)
meta.maxCycles--;
// TODO: other instructions might modify flags too
var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode - 0x10) / 0x20)];
var cons0 = constraintEquals(oldconstraints, cons[0]);
var cons1 = constraintEquals(oldconstraints, cons[1]);
// recursively trace the taken branch
if (true || cons0 !== false) { // TODO?
this.traceInstructions(newpc, minclocks + meta.maxCycles, maxclocks + meta.maxCycles, subaddr, cons[0]);
}
// abort if we will always take the branch
if (cons1 === false) {
console.log("branch always taken", (0, util_1.hex)(pc), oldconstraints, cons[1]);
abort = true;
}
constraints = cons[1]; // not taken
meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
break;
case 0x6c:
console.log("Instruction not supported!", (0, util_1.hex)(pc), (0, util_1.hex)(meta.opcode), meta); // TODO
return;
}
// add min/max instruction time to min/max clocks bound
minclocks += meta.minCycles;
maxclocks += meta.maxCycles;
}
}
showLoopTimingForPC(pc) {
this.pc2minclocks = {};
this.pc2maxclocks = {};
this.jsrresult = {};
// recurse through all traces
this.traceInstructions(pc | this.platform.getOriginPC(), this.START_CLOCKS, this.MAX_CLOCKS, 0, {});
}
}
// 76 cycles * 2 (support two scanline kernels)
class CodeAnalyzer_vcs extends CodeAnalyzer6502 {
constructor(platform) {
super(platform);
this.MAX_CLOCKS = this.START_CLOCKS = 76 * 4; // 4 scanlines
this.WRAP_CLOCKS = false;
}
}
exports.CodeAnalyzer_vcs = CodeAnalyzer_vcs;
// https://wiki.nesdev.com/w/index.php/PPU_rendering#Line-by-line_timing
// TODO: sprite 0 hit, CPU stalls
class CodeAnalyzer_nes extends CodeAnalyzer6502 {
constructor(platform) {
super(platform);
this.MAX_CLOCKS = 114; // 341 clocks for 3 scanlines
this.START_CLOCKS = 0;
this.WRAP_CLOCKS = true;
}
}
exports.CodeAnalyzer_nes = CodeAnalyzer_nes;
class CodeAnalyzer_apple2 extends CodeAnalyzer6502 {
constructor(platform) {
super(platform);
this.MAX_CLOCKS = 65;
this.START_CLOCKS = 0;
this.WRAP_CLOCKS = true;
}
}
exports.CodeAnalyzer_apple2 = CodeAnalyzer_apple2;
//# sourceMappingURL=analysis.js.map

File diff suppressed because one or more lines are too long

502
gen/common/audio.js Normal file
View File

@@ -0,0 +1,502 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TssChannelAdapter = exports.SampledAudio = exports.SampleAudio = exports.WorkerSoundChannel = exports.POKEYDeviceChannel = exports.newPOKEYAudio = exports.SN76489_Audio = exports.AY38910_Audio = exports.MasterAudio = void 0;
class MasterAudio {
constructor() {
this.master = new MasterChannel();
}
start() {
if (!this.looper) {
this.looper = new AudioLooper(512);
this.looper.setChannel(this.master);
this.looper.activate();
}
}
stop() {
if (this.looper) {
this.looper.setChannel(null);
this.looper = null;
}
}
}
exports.MasterAudio = MasterAudio;
class AY38910_Audio {
constructor(master) {
this.psg = new PsgDeviceChannel();
this.curreg = 0;
this.master = master;
this.psg.setMode(PsgDeviceChannel.MODE_SIGNED);
this.psg.setDevice(PsgDeviceChannel.DEVICE_AY_3_8910);
master.master.addChannel(this.psg);
}
reset() {
for (var i = 15; i >= 0; i--) {
this.selectRegister(i);
this.setData(0);
}
}
selectRegister(val) {
this.curreg = val & 0xf;
}
setData(val) {
this.psg.writeRegisterAY(this.curreg, val & 0xff);
}
readData() {
return this.psg.readRegister(this.curreg);
}
currentRegister() { return this.curreg; }
}
exports.AY38910_Audio = AY38910_Audio;
class SN76489_Audio {
constructor(master) {
this.psg = new PsgDeviceChannel();
this.master = master;
this.psg.setMode(PsgDeviceChannel.MODE_SIGNED);
this.psg.setDevice(PsgDeviceChannel.DEVICE_SN76489);
master.master.addChannel(this.psg);
}
reset() {
// TODO
}
setData(val) {
this.psg.writeRegisterSN(0, val & 0xff);
}
}
exports.SN76489_Audio = SN76489_Audio;
// https://en.wikipedia.org/wiki/POKEY
// https://user.xmission.com/~trevin/atari/pokey_regs.html
// http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm
function newPOKEYAudio(count) {
var audio = new MasterAudio();
for (var i = 1; i <= count; i++) {
var pokey = new exports.POKEYDeviceChannel();
audio['pokey' + i] = pokey; // TODO: cheezy
audio.master.addChannel(pokey);
}
return audio;
}
exports.newPOKEYAudio = newPOKEYAudio;
function combinePolys(a, b) {
var arr = new Uint8Array(a.length * b.length);
var n = 0;
for (var i = 0; i < arr.length; i++) {
arr[i] = b[n % b.length];
if (a[i % a.length])
n++;
}
return arr;
}
function divideBy(n) {
var arr = new Uint8Array(n * 2);
arr.fill(1, 0, n);
return arr;
}
var POKEYDeviceChannel = function () {
/* definitions for AUDCx (D201, D203, D205, D207) */
var NOTPOLY5 = 0x80; /* selects POLY5 or direct CLOCK */
var POLY4 = 0x40; /* selects POLY4 or POLY17 */
var PURE = 0x20; /* selects POLY4/17 or PURE tone */
var VOL_ONLY = 0x10; /* selects VOLUME OUTPUT ONLY */
var VOLUME_MASK = 0x0f; /* volume mask */
/* definitions for AUDCTL (D208) */
var POLY9 = 0x80; /* selects POLY9 or POLY17 */
var CH1_179 = 0x40; /* selects 1.78979 MHz for Ch 1 */
var CH3_179 = 0x20; /* selects 1.78979 MHz for Ch 3 */
var CH1_CH2 = 0x10; /* clocks channel 1 w/channel 2 */
var CH3_CH4 = 0x08; /* clocks channel 3 w/channel 4 */
var CH1_FILTER = 0x04; /* selects channel 1 high pass filter */
var CH2_FILTER = 0x02; /* selects channel 2 high pass filter */
var CLOCK_15 = 0x01; /* selects 15.6999kHz or 63.9210kHz */
/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of
the 1.79MHz clock */
var DIV_64 = 28; /* divisor for 1.79MHz clock to 64 kHz */
var DIV_15 = 114; /* divisor for 1.79MHz clock to 15 kHz */
/* the size (in entries) of the 4 polynomial tables */
var POLY4_SIZE = 0x000f;
var POLY5_SIZE = 0x001f;
var POLY9_SIZE = 0x01ff;
var POLY17_SIZE = 0x0001ffff; /* else use the full 17 bits */
/* channel/chip definitions */
var CHAN1 = 0;
var CHAN2 = 1;
var CHAN3 = 2;
var CHAN4 = 3;
var CHIP1 = 0;
var CHIP2 = 4;
var CHIP3 = 8;
var CHIP4 = 12;
var SAMPLE = 127;
var FREQ_17_EXACT = 1789790.0; /* exact 1.79 MHz clock freq */
var FREQ_17_APPROX = 1787520.0; /* approximate 1.79 MHz clock freq */
// LFSR sequences
var bit1 = new Uint8Array([1]);
var bit2 = new Uint8Array([0, 1]); // TODO?
var bit4 = new Uint8Array([1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0]);
var bit5 = new Uint8Array([0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1]);
var bit9 = new Uint8Array([0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0]);
var bit15 = new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
var bit31 = new Uint8Array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
var bit17 = new Uint8Array(1 << 14);
for (var i = 0; i < bit17.length; i++) {
bit17[i] = Math.random() > 0.5 ? 1 : 0;
}
var bit17_5 = combinePolys(bit17, bit5);
var bit5_4 = combinePolys(bit5, bit4);
var wavetones = [
bit17_5, bit5, bit5_4, bit5,
bit17, bit2, bit4, bit2
];
// TIA
var div2 = divideBy(2);
var div6 = divideBy(6);
var div31 = divideBy(31);
var div93 = divideBy(93);
var bit15_4 = combinePolys(bit15, bit4);
var bit5_2 = combinePolys(bit5, div2);
var bit5_6 = combinePolys(bit5, div6);
var tiawavetones = [
bit1, bit4, bit15_4, bit5_4,
div2, div2, div31, bit5_2,
bit9, bit5, div31, bit1,
div6, div6, div93, bit5_6
];
// registers
var regs = new Uint8Array(16);
var counters = new Float32Array(4);
var deltas = new Float32Array(4);
var volume = new Float32Array(4);
var audc = new Uint8Array(4);
var waveforms = [bit1, bit1, bit1, bit1];
var buffer;
var sampleRate;
var clock, baseDelta;
var dirty = true;
//
this.setBufferLength = function (length) {
buffer = new Int32Array(length);
};
this.getBuffer = function () {
return buffer;
};
this.setSampleRate = function (rate) {
sampleRate = rate;
baseDelta = FREQ_17_EXACT / rate / 1.2; // TODO?
};
function updateValues(addr) {
var ctrl = regs[8];
var base = (ctrl & CLOCK_15) ? DIV_15 : DIV_64;
var div;
var i = addr & 4;
var j = i >> 1;
var k = i >> 2;
if (ctrl & (CH1_CH2 >> k)) {
if (ctrl & (CH1_179 >> k))
div = regs[i + 2] * 256 + regs[i + 0] + 7;
else
div = (regs[i + 2] * 256 + regs[i + 0] + 1) * base;
deltas[j + 1] = baseDelta / div;
deltas[j + 0] = 0;
}
else {
if (ctrl & (CH1_179 >> k)) {
div = regs[i + 0] + 4;
}
else {
div = (regs[i + 0] + 1) * base;
}
deltas[j + 0] = baseDelta / div;
div = (regs[i + 2] + 1) * base;
deltas[j + 1] = baseDelta / div;
}
//console.log(addr, ctrl.toString(16), div, deltas[j+0], deltas[j+1]);
}
this.setRegister = function (addr, value) {
addr &= 0xf;
value &= 0xff;
if (regs[addr] != value) {
regs[addr] = value;
switch (addr) {
case 0:
case 2:
case 4:
case 6: // AUDF
case 8: // ctrl
dirty = true;
break;
case 1:
case 3:
case 5:
case 7: // AUDC
volume[addr >> 1] = value & 0xf;
waveforms[addr >> 1] = wavetones[value >> 5];
break;
}
}
};
this.setTIARegister = function (addr, value) {
switch (addr) {
case 0x17:
case 0x18:
regs[(addr & 1) * 4] = value & 0x1f;
dirty = true;
break;
case 0x15:
case 0x16:
waveforms[(addr & 1) * 2] = tiawavetones[value & 0xf];
break;
case 0x19:
case 0x1a:
volume[(addr & 1) * 2] = value & 0xf;
break;
}
};
this.generate = function (length) {
if (dirty) {
updateValues(0);
updateValues(4);
dirty = false;
}
for (var s = 0; s < length; s += 2) {
var sample = 0;
for (var i = 0; i < 4; i++) {
var d = deltas[i];
var v = volume[i];
if (d > 0 && d < 1 && v > 0) {
var wav = waveforms[i];
var cnt = counters[i] += d;
if (cnt > wav.length) {
cnt = counters[i] = cnt - Math.floor(cnt / wav.length) * wav.length;
}
var on = wav[Math.floor(cnt)];
if (on) {
sample += v;
}
}
}
sample *= 64;
buffer[s] = sample;
buffer[s + 1] = sample;
}
};
};
exports.POKEYDeviceChannel = POKEYDeviceChannel;
////// Worker sound
var WorkerSoundChannel = function (worker) {
var sampleRate;
var output;
var pending = [];
var pendingLength = 0;
worker.onmessage = function (e) {
if (e && e.data && e.data.samples && output) {
pending.push(e.data.samples);
pendingLength += e.data.samples.length;
}
};
this.setBufferLength = function (length) {
output = new Int16Array(length);
//worker.postMessage({bufferLength:length,numChannels:2});
pendingLength = 0;
};
this.getBuffer = function () {
return output;
};
this.setSampleRate = function (rate) {
sampleRate = rate;
worker.postMessage({ sampleRate: rate });
};
this.generate = function (length) {
if (pendingLength < length * 3) {
//console.log(length, pendingLength);
output.fill(0);
return; // TODO: send sync msg?
}
for (var i = 0; i < output.length;) {
if (pending.length == 0)
break; // TODO?
var buf = pending.shift();
pendingLength -= buf.length;
var l = output.length - i;
if (buf.length < l) {
output.set(buf, i);
}
else {
output.set(buf.slice(0, l), i);
pending.unshift(buf.slice(l));
pendingLength += buf.length - l;
}
i += buf.length;
}
};
};
exports.WorkerSoundChannel = WorkerSoundChannel;
// SampleAudio
var SampleAudio = function (clockfreq) {
var self = this;
var sfrac, sinc, accum;
var buffer, bufpos, bufferlist;
var idrain, ifill;
var nbuffers = 4;
function mix(ape) {
var buflen = ape.outputBuffer.length;
var lbuf = ape.outputBuffer.getChannelData(0);
var m = this.module;
if (!m)
m = ape.srcElement.module;
if (!m)
return;
if (m.callback) {
m.callback(lbuf);
return;
}
else {
var buf = bufferlist[idrain];
for (var i = 0; i < lbuf.length; i++) {
lbuf[i] = buf[i];
//lbuf[i] = (i&128) ? 1.0 : 0.33;
}
idrain = (idrain + 1) % bufferlist.length;
}
}
function clearBuffers() {
if (bufferlist)
for (var buf of bufferlist)
buf.fill(0);
}
function createContext() {
var AudioContext = window['AudioContext'] || window['webkitAudioContext'] || window['mozAudioContext'];
if (!AudioContext) {
console.log("no web audio context");
return;
}
var ctx = new AudioContext();
self.context = ctx;
self.sr = self.context.sampleRate;
self.bufferlen = 2048;
// remove DC bias
self.filterNode = self.context.createBiquadFilter();
self.filterNode.type = 'lowshelf';
self.filterNode.frequency.value = 100;
self.filterNode.gain.value = -6;
// mixer
if (typeof self.context.createScriptProcessor === 'function') {
self.mixerNode = self.context.createScriptProcessor(self.bufferlen, 1, 1);
}
else {
self.mixerNode = self.context.createJavaScriptNode(self.bufferlen, 1, 1);
}
self.mixerNode.module = self;
self.mixerNode.onaudioprocess = mix;
// compressor for a bit of volume boost, helps with multich tunes
self.compressorNode = self.context.createDynamicsCompressor();
// patch up some cables :)
self.mixerNode.connect(self.filterNode);
self.filterNode.connect(self.compressorNode);
self.compressorNode.connect(self.context.destination);
}
this.start = function () {
if (this.context) {
// Chrome autoplay (https://goo.gl/7K7WLu)
if (this.context.state == 'suspended') {
this.context.resume();
}
return; // already created
}
createContext(); // create it
if (!this.context)
return; // not created?
sinc = this.sr * 1.0 / clockfreq;
sfrac = 0;
accum = 0;
bufpos = 0;
bufferlist = [];
idrain = 1;
ifill = 0;
for (var i = 0; i < nbuffers; i++) {
var arrbuf = new ArrayBuffer(self.bufferlen * 4);
bufferlist[i] = new Float32Array(arrbuf);
}
buffer = bufferlist[0];
};
this.stop = function () {
this.context && this.context.suspend && this.context.suspend();
clearBuffers(); // just in case it doesn't stop immediately
};
this.close = function () {
if (this.context) {
this.context.close();
this.context = null;
}
};
this.addSingleSample = function (value) {
if (!buffer)
return;
buffer[bufpos++] = value;
if (bufpos >= buffer.length) {
bufpos = 0;
bufferlist[ifill] = buffer;
var inext = (ifill + 1) % bufferlist.length;
if (inext == idrain) {
ifill = Math.floor(idrain + nbuffers / 2) % bufferlist.length;
//console.log('SampleAudio: skipped buffer', idrain, ifill); // TODO
}
else {
ifill = inext;
}
buffer = bufferlist[ifill];
}
};
this.feedSample = function (value, count) {
accum += value * count;
sfrac += sinc * count;
if (sfrac >= 1) {
accum /= sfrac;
while (sfrac >= 1) {
this.addSingleSample(accum * sinc);
sfrac -= 1;
}
accum *= sfrac;
}
};
};
exports.SampleAudio = SampleAudio;
class SampledAudio {
constructor(sampleRate) {
this.sa = new exports.SampleAudio(sampleRate);
}
feedSample(value, count) {
this.sa.feedSample(value, count);
}
start() {
this.sa.start();
}
stop() {
this.sa.stop();
}
}
exports.SampledAudio = SampledAudio;
class TssChannelAdapter {
constructor(chans, oversample, sampleRate) {
this.audioGain = 1.0 / 8192;
this.bufferLength = oversample * 2;
this.channels = chans.generate ? [chans] : chans; // array or single channel
this.channels.forEach((c) => {
c.setBufferLength(this.bufferLength);
c.setSampleRate(sampleRate);
});
}
generate(sink) {
var l = this.bufferLength;
var bufs = this.channels.map((ch) => ch.getBuffer());
this.channels.forEach((ch) => {
ch.generate(l);
});
for (let i = 0; i < l; i += 2) {
var total = 0;
bufs.forEach((buf) => total += buf[i]);
sink.feedSample(total * this.audioGain, 1);
}
;
}
}
exports.TssChannelAdapter = TssChannelAdapter;
//# sourceMappingURL=audio.js.map

1
gen/common/audio.js.map Normal file

File diff suppressed because one or more lines are too long

871
gen/common/baseplatform.js Normal file
View File

@@ -0,0 +1,871 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseZ80MachinePlatform = exports.Base6502MachinePlatform = exports.BaseMachinePlatform = exports.hasSerialIO = exports.hasBIOS = exports.hasProbe = exports.isRaster = exports.hasPaddleInput = exports.hasJoyInput = exports.hasKeyInput = exports.hasAudio = exports.hasVideo = exports.lookupSymbol = exports.dumpStackToString = exports.Base6809Platform = exports.getToolForFilename_6809 = exports.cpuStateToLongString_6809 = exports.getToolForFilename_z80 = exports.BaseZ80Platform = exports.cpuStateToLongString_Z80 = exports.getOpcodeMetadata_6502 = exports.cpuStateToLongString_6502 = exports.Base6502Platform = exports.getToolForFilename_6502 = exports.inspectSymbol = exports.BaseDebugPlatform = exports.BasePlatform = exports.BreakpointList = exports.isDebuggable = exports.DebugSymbols = void 0;
const emu_1 = require("./emu");
const util_1 = require("./util");
const disasm6502_1 = require("./cpu/disasm6502");
const disasmz80_1 = require("./cpu/disasmz80");
const ZilogZ80_1 = require("./cpu/ZilogZ80");
const audio_1 = require("./audio");
const recorder_1 = require("./recorder");
const wasmplatform_1 = require("./wasmplatform");
const _6809_1 = require("./cpu/6809");
;
;
class DebugSymbols {
constructor(symbolmap, debuginfo) {
this.symbolmap = symbolmap;
this.debuginfo = debuginfo;
this.addr2symbol = (0, util_1.invertMap)(symbolmap);
//// TODO: shouldn't be necc.
if (!this.addr2symbol[0x0])
this.addr2symbol[0x0] = '$00'; // needed for ...
this.addr2symbol[0x10000] = '__END__'; // ... dump memory to work
}
}
exports.DebugSymbols = DebugSymbols;
function isDebuggable(arg) {
return arg && typeof arg.getDebugCategories === 'function';
}
exports.isDebuggable = isDebuggable;
// for composite breakpoints w/ single debug function
class BreakpointList {
constructor() {
this.id2bp = {};
}
getDebugCondition() {
if (Object.keys(this.id2bp).length == 0) {
return null; // no breakpoints
}
else {
// evaluate all breakpoints
return () => {
var result = false;
for (var id in this.id2bp)
if (this.id2bp[id].cond())
result = true;
return result;
};
}
}
}
exports.BreakpointList = BreakpointList;
;
/////
class BasePlatform {
constructor() {
this.recorder = null;
this.internalFiles = {};
}
setRecorder(recorder) {
this.recorder = recorder;
}
updateRecorder() {
// are we recording and do we need to save a frame?
if (this.recorder && this.isRunning() && this.recorder.frameRequested()) {
this.recorder.recordFrame(this.saveState());
}
}
inspect(sym) {
return inspectSymbol(this, sym);
}
getDebugTree() {
return this.saveState();
}
readFile(path) {
return this.internalFiles[path];
}
writeFile(path, data) {
this.internalFiles[path] = data;
return true;
}
}
exports.BasePlatform = BasePlatform;
class BaseDebugPlatform extends BasePlatform {
constructor() {
super(...arguments);
this.debugSavedState = null;
this.debugBreakState = null;
this.debugTargetClock = 0;
this.debugClock = 0;
this.breakpoints = new BreakpointList();
this.frameCount = 0;
}
setBreakpoint(id, cond) {
if (cond) {
this.breakpoints.id2bp[id] = { cond: cond };
this.restartDebugging();
}
else {
this.clearBreakpoint(id);
}
}
clearBreakpoint(id) {
delete this.breakpoints.id2bp[id];
}
hasBreakpoint(id) {
return this.breakpoints.id2bp[id] != null;
}
getDebugCallback() {
return this.breakpoints.getDebugCondition();
}
setupDebug(callback) {
this.onBreakpointHit = callback;
}
clearDebug() {
this.debugSavedState = null;
this.debugBreakState = null;
this.debugTargetClock = -1;
this.debugClock = 0;
this.onBreakpointHit = null;
this.clearBreakpoint('debug');
this.frameCount = 0;
}
setDebugCondition(debugCond) {
this.setBreakpoint('debug', debugCond);
}
restartDebugging() {
if (this.debugSavedState) {
this.loadState(this.debugSavedState);
}
else {
this.debugSavedState = this.saveState();
}
this.debugClock = 0;
this.debugCallback = this.getDebugCallback();
this.debugBreakState = null;
this.resume();
}
preFrame() {
// save state before frame, to record any inputs that happened pre-frame
if (this.debugCallback && !this.debugBreakState) {
// save state every frame and rewind debug clocks
this.debugSavedState = this.saveState();
this.debugTargetClock -= this.debugClock;
this.debugClock = 0;
}
}
postFrame() {
// reload debug state at end of frame after breakpoint
if (this.debugCallback && this.debugBreakState) {
this.loadState(this.debugBreakState);
}
this.frameCount++;
}
pollControls() {
}
nextFrame(novideo) {
this.pollControls();
this.updateRecorder();
this.preFrame();
var steps = this.advance(novideo);
this.postFrame();
return steps;
}
evalDebugCondition() {
if (this.debugCallback && !this.debugBreakState) {
this.debugCallback();
}
}
wasBreakpointHit() {
return this.debugBreakState != null;
}
breakpointHit(targetClock, reason) {
console.log(this.debugTargetClock, targetClock, this.debugClock, this.isStable());
this.debugTargetClock = targetClock;
this.debugBreakState = this.saveState();
console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16));
this.pause();
if (this.onBreakpointHit) {
this.onBreakpointHit(this.debugBreakState, reason);
}
}
haltAndCatchFire(reason) {
this.breakpointHit(this.debugClock, reason);
}
runEval(evalfunc) {
this.setDebugCondition(() => {
if (++this.debugClock >= this.debugTargetClock && this.isStable()) {
var cpuState = this.getCPUState();
if (evalfunc(cpuState)) {
this.breakpointHit(this.debugClock);
return true;
}
else {
return false;
}
}
});
}
runToPC(pc) {
this.debugTargetClock++;
this.runEval((c) => {
return c.PC == pc;
});
}
runUntilReturn() {
var SP0 = this.getSP();
this.runEval((c) => {
return c.SP > SP0; // TODO: check for RTS/RET opcode
});
}
runToFrameClock(clock) {
this.restartDebugging();
this.debugTargetClock = clock;
this.runEval(() => { return true; });
}
step() {
this.runToFrameClock(this.debugClock + 1);
}
stepBack() {
var prevState;
var prevClock;
var clock0 = this.debugTargetClock;
this.restartDebugging();
this.debugTargetClock = clock0 - 25; // TODO: depends on CPU
this.runEval((c) => {
if (this.debugClock < clock0) {
prevState = this.saveState();
prevClock = this.debugClock;
return false;
}
else {
if (prevState) {
this.loadState(prevState);
this.debugClock = prevClock;
}
return true;
}
});
}
runToVsync() {
this.restartDebugging();
var frame0 = this.frameCount;
this.runEval(() => {
return this.frameCount > frame0;
});
}
}
exports.BaseDebugPlatform = BaseDebugPlatform;
function inspectSymbol(platform, sym) {
if (!platform.debugSymbols)
return;
var symmap = platform.debugSymbols.symbolmap;
var addr2sym = platform.debugSymbols.addr2symbol;
if (!symmap || !platform.readAddress)
return null;
var addr = symmap["_" + sym] || symmap[sym]; // look for C or asm symbol
if (!(typeof addr == 'number'))
return null;
var b = platform.readAddress(addr);
// don't show 2 bytes if there's a symbol at the next address
if (addr2sym && addr2sym[addr + 1] != null) {
return "$" + (0, util_1.hex)(addr, 4) + " = $" + (0, util_1.hex)(b, 2) + " (" + b + " decimal)"; // unsigned
}
else {
let b2 = platform.readAddress(addr + 1);
let w = b | (b2 << 8);
return "$" + (0, util_1.hex)(addr, 4) + " = $" + (0, util_1.hex)(b, 2) + " $" + (0, util_1.hex)(b2, 2) + " (" + ((w << 16) >> 16) + " decimal)"; // signed
}
}
exports.inspectSymbol = inspectSymbol;
////// 6502
function getToolForFilename_6502(fn) {
if (fn.endsWith(".pla"))
return "plasm";
if (fn.endsWith(".c"))
return "cc65";
if (fn.endsWith(".h"))
return "cc65";
if (fn.endsWith(".s"))
return "ca65";
if (fn.endsWith(".ca65"))
return "ca65";
if (fn.endsWith(".dasm"))
return "dasm";
if (fn.endsWith(".acme"))
return "acme";
if (fn.endsWith(".wiz"))
return "wiz";
return "dasm"; // .a
}
exports.getToolForFilename_6502 = getToolForFilename_6502;
// TODO: can merge w/ Z80?
class Base6502Platform extends BaseDebugPlatform {
constructor() {
super(...arguments);
// some platforms store their PC one byte before or after the first opcode
// so we correct when saving and loading from state
this.debugPCDelta = -1;
this.getToolForFilename = getToolForFilename_6502;
}
fixPC(c) { c.PC = (c.PC + this.debugPCDelta) & 0xffff; return c; }
unfixPC(c) { c.PC = (c.PC - this.debugPCDelta) & 0xffff; return c; }
getSP() { return this.getCPUState().SP; }
;
getPC() { return this.getCPUState().PC; }
;
isStable() { return !this.getCPUState()['T']; }
newCPU(membus) {
var cpu = new jt.M6502();
cpu.connectBus(membus);
return cpu;
}
getOpcodeMetadata(opcode, offset) {
return getOpcodeMetadata_6502(opcode, offset);
}
getOriginPC() {
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
}
disassemble(pc, read) {
return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2));
}
getDefaultExtension() { return ".a"; }
;
getDebugCategories() {
return ['CPU', 'ZPRAM', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return (0, emu_1.dumpRAM)(state.b || state.ram, 0x0, 0x100);
case 'Stack': return dumpStackToString(this, state.b || state.ram, 0x100, 0x1ff, 0x100 + state.c.SP, 0x20);
}
}
}
exports.Base6502Platform = Base6502Platform;
function cpuStateToLongString_6502(c) {
function decodeFlags(c) {
var s = "";
s += c.N ? " N" : " -";
s += c.V ? " V" : " -";
s += c.D ? " D" : " -";
s += c.Z ? " Z" : " -";
s += c.C ? " C" : " -";
s += c.I ? " I" : " -";
return s;
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c) + "\n"
+ " A " + (0, util_1.hex)(c.A) + " " + (c.R ? "" : "BUSY") + "\n"
+ " X " + (0, util_1.hex)(c.X) + "\n"
+ " Y " + (0, util_1.hex)(c.Y) + " " + "SP " + (0, util_1.hex)(c.SP) + "\n";
}
exports.cpuStateToLongString_6502 = cpuStateToLongString_6502;
var OPMETA_6502 = {
cycletime: [
7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 0, 6, 0, 6, 3, 3, 3, 3, 2, 0, 2, 0, 4, 4, 4, 4, 2, 6, 0, 0, 4, 4, 4, 4, 2, 5, 2, 0, 0, 5, 0, 0, 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 0, 4, 4, 4, 4, 2, 5, 0, 5, 4, 4, 4, 4, 2, 4, 2, 0, 4, 4, 4, 4, 2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 3, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 0, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7
],
extracycles: [
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1
],
insnlengths: [
1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 3, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 0, 2, 0, 2, 2, 2, 2, 2, 1, 0, 1, 0, 3, 3, 3, 3, 2, 2, 0, 0, 2, 2, 2, 3, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3
],
validinsns: [
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
],
};
function getOpcodeMetadata_6502(opcode, address) {
// TODO: more intelligent maximum cycles
// TODO: must always be new object, b/c we might modify it
return {
opcode: opcode,
minCycles: OPMETA_6502.cycletime[opcode],
maxCycles: OPMETA_6502.cycletime[opcode] + OPMETA_6502.extracycles[opcode],
insnlength: OPMETA_6502.insnlengths[opcode]
};
}
exports.getOpcodeMetadata_6502 = getOpcodeMetadata_6502;
////// Z80
function cpuStateToLongString_Z80(c) {
function decodeFlags(flags) {
return (0, util_1.printFlags)(flags, ["S", "Z", , "H", , "V", "N", "C"], true);
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c.AF) + " " + (c.iff1 ? "I" : "-") + (c.iff2 ? "I" : "-") + "\n"
+ "SP " + (0, util_1.hex)(c.SP, 4) + " IR " + (0, util_1.hex)(c.IR, 4) + "\n"
+ "IX " + (0, util_1.hex)(c.IX, 4) + " IY " + (0, util_1.hex)(c.IY, 4) + "\n"
+ "AF " + (0, util_1.hex)(c.AF, 4) + " BC " + (0, util_1.hex)(c.BC, 4) + "\n"
+ "DE " + (0, util_1.hex)(c.DE, 4) + " HL " + (0, util_1.hex)(c.HL, 4) + "\n";
}
exports.cpuStateToLongString_Z80 = cpuStateToLongString_Z80;
class BaseZ80Platform extends BaseDebugPlatform {
constructor() {
super(...arguments);
this.waitCycles = 0;
this.getToolForFilename = getToolForFilename_z80;
}
newCPU(membus, iobus) {
this._cpu = new ZilogZ80_1.Z80();
this._cpu.connectMemoryBus(membus);
this._cpu.connectIOBus(iobus);
return this._cpu;
}
getPC() { return this._cpu.getPC(); }
getSP() { return this._cpu.getSP(); }
isStable() { return true; }
// TODO: refactor other parts into here
runCPU(cpu, cycles) {
this._cpu = cpu; // TODO?
this.waitCycles = 0; // TODO: needs to spill over betwenn calls
if (this.wasBreakpointHit())
return 0;
var debugCond = this.getDebugCallback();
var n = 0;
this.waitCycles += cycles;
while (this.waitCycles > 0) {
if (debugCond && debugCond()) {
debugCond = null;
break;
}
var cyc = cpu.advanceInsn();
n += cyc;
this.waitCycles -= cyc;
}
return n;
}
getDefaultExtension() { return ".c"; }
;
// TODO: Z80 opcode metadata
//this.getOpcodeMetadata = function() { }
getDebugCategories() {
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_Z80(state.c);
case 'Stack': {
var sp = (state.c.SP - 1) & 0xffff;
var start = sp & 0xff00;
var end = start + 0xff;
if (sp == 0)
sp = 0x10000;
console.log(sp, start, end);
return dumpStackToString(this, [], start, end, sp, 0xcd);
}
}
}
disassemble(pc, read) {
return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3));
}
}
exports.BaseZ80Platform = BaseZ80Platform;
function getToolForFilename_z80(fn) {
if (fn.endsWith(".c"))
return "sdcc";
if (fn.endsWith(".h"))
return "sdcc";
if (fn.endsWith(".s"))
return "sdasz80";
if (fn.endsWith(".ns"))
return "naken";
if (fn.endsWith(".scc"))
return "sccz80";
if (fn.endsWith(".z"))
return "zmac";
if (fn.endsWith(".wiz"))
return "wiz";
return "zmac";
}
exports.getToolForFilename_z80 = getToolForFilename_z80;
////// 6809
function cpuStateToLongString_6809(c) {
function decodeFlags(flags) {
return (0, util_1.printFlags)(flags, ["E", "F", "H", "I", "N", "Z", "V", "C"], true);
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c.CC) + "\n"
+ "SP " + (0, util_1.hex)(c.SP, 4) + "\n"
+ "DP " + (0, util_1.hex)(c.DP, 2) + "\n"
+ " A " + (0, util_1.hex)(c.A, 2) + "\n"
+ " B " + (0, util_1.hex)(c.B, 2) + "\n"
+ " X " + (0, util_1.hex)(c.X, 4) + "\n"
+ " Y " + (0, util_1.hex)(c.Y, 4) + "\n"
+ " U " + (0, util_1.hex)(c.U, 4) + "\n";
}
exports.cpuStateToLongString_6809 = cpuStateToLongString_6809;
function getToolForFilename_6809(fn) {
if (fn.endsWith(".c"))
return "cmoc";
if (fn.endsWith(".h"))
return "cmoc";
if (fn.endsWith(".xasm"))
return "xasm6809";
return "lwasm";
}
exports.getToolForFilename_6809 = getToolForFilename_6809;
class Base6809Platform extends BaseZ80Platform {
constructor() {
super(...arguments);
//this.getOpcodeMetadata = function() { }
this.getToolForFilename = getToolForFilename_6809;
}
newCPU(membus) {
var cpu = Object.create((0, _6809_1.CPU6809)());
cpu.init(membus.write, membus.read, 0);
return cpu;
}
cpuStateToLongString(c) {
return cpuStateToLongString_6809(c);
}
disassemble(pc, read) {
// TODO: don't create new CPU
return Object.create((0, _6809_1.CPU6809)()).disasm(read(pc), read(pc + 1), read(pc + 2), read(pc + 3), read(pc + 4), pc);
}
getDefaultExtension() { return ".asm"; }
;
getDebugCategories() {
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6809(state.c);
default: return super.getDebugInfo(category, state);
}
}
}
exports.Base6809Platform = Base6809Platform;
//TODO: how to get stack_end?
function dumpStackToString(platform, mem, start, end, sp, jsrop) {
var s = "";
var nraw = 0;
//s = dumpRAM(mem.slice(start,start+end+1), start, end-start+1);
function read(addr) {
if (addr < mem.length)
return mem[addr];
else
return platform.readAddress(addr);
}
while (sp < end) {
sp++;
// see if there's a JSR on the stack here
// TODO: make work with roms and memory maps
var addr = read(sp) + read(sp + 1) * 256;
var jsrofs = jsrop == 0x20 ? -2 : -3; // 6502 vs Z80
var opcode = read(addr + jsrofs); // might be out of bounds
if (opcode == jsrop) { // JSR
s += "\n$" + (0, util_1.hex)(sp) + ": ";
s += (0, util_1.hex)(addr, 4) + " " + lookupSymbol(platform, addr, true);
sp++;
nraw = 0;
}
else {
if (nraw == 0)
s += "\n$" + (0, util_1.hex)(sp) + ": ";
s += (0, util_1.hex)(read(sp)) + " ";
if (++nraw == 8)
nraw = 0;
}
}
return s + "\n";
}
exports.dumpStackToString = dumpStackToString;
// TODO: slow, funky, uses global
function lookupSymbol(platform, addr, extra) {
var start = addr;
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
while (addr2symbol && addr >= 0) {
var sym = addr2symbol[addr];
if (sym) { // return first symbol we find
var sym = addr2symbol[addr];
return extra ? (sym + " + $" + (0, util_1.hex)(start - addr)) : sym;
}
if (!extra)
break;
addr--;
}
return "";
}
exports.lookupSymbol = lookupSymbol;
function hasVideo(arg) {
return typeof arg.connectVideo === 'function';
}
exports.hasVideo = hasVideo;
function hasAudio(arg) {
return typeof arg.connectAudio === 'function';
}
exports.hasAudio = hasAudio;
function hasKeyInput(arg) {
return typeof arg.setKeyInput === 'function';
}
exports.hasKeyInput = hasKeyInput;
function hasJoyInput(arg) {
return typeof arg.setJoyInput === 'function';
}
exports.hasJoyInput = hasJoyInput;
function hasPaddleInput(arg) {
return typeof arg.setPaddleInput === 'function';
}
exports.hasPaddleInput = hasPaddleInput;
function isRaster(arg) {
return typeof arg.getRasterY === 'function';
}
exports.isRaster = isRaster;
function hasProbe(arg) {
return typeof arg.connectProbe == 'function';
}
exports.hasProbe = hasProbe;
function hasBIOS(arg) {
return typeof arg.loadBIOS == 'function';
}
exports.hasBIOS = hasBIOS;
function hasSerialIO(arg) {
return typeof arg.connectSerialIO === 'function';
}
exports.hasSerialIO = hasSerialIO;
class BaseMachinePlatform extends BaseDebugPlatform {
constructor(mainElement) {
super();
this.mainElement = mainElement;
}
reset() {
this.machine.reset();
if (this.serialVisualizer != null)
this.serialVisualizer.reset();
}
loadState(s) { this.machine.loadState(s); }
saveState() { return this.machine.saveState(); }
getSP() { return this.machine.cpu.getSP(); }
getPC() { return this.machine.cpu.getPC(); }
isStable() { return this.machine.cpu.isStable(); }
getCPUState() { return this.machine.cpu.saveState(); }
loadControlsState(s) { this.machine.loadControlsState(s); }
saveControlsState() { return this.machine.saveControlsState(); }
async start() {
this.machine = this.newMachine();
const m = this.machine;
// block on WASM loading
if (m instanceof wasmplatform_1.BaseWASMMachine) {
await m.loadWASM();
}
var videoFrequency;
if (hasVideo(m)) {
var vp = m.getVideoParams();
this.video = new emu_1.RasterVideo(this.mainElement, vp.width, vp.height, { overscan: !!vp.overscan,
rotate: vp.rotate | 0,
aspect: vp.aspect });
this.video.create();
m.connectVideo(this.video.getFrameData());
// TODO: support keyboard w/o video?
if (hasKeyInput(m)) {
this.video.setKeyboardEvents(m.setKeyInput.bind(m));
this.poller = new emu_1.ControllerPoller(m.setKeyInput.bind(m));
}
videoFrequency = vp.videoFrequency;
}
this.timer = new emu_1.AnimationTimer(videoFrequency || 60, this.nextFrame.bind(this));
if (hasAudio(m)) {
var ap = m.getAudioParams();
this.audio = new audio_1.SampledAudio(ap.sampleRate);
this.audio.start();
m.connectAudio(this.audio);
}
if (hasPaddleInput(m)) {
this.video.setupMouseEvents();
}
if (hasProbe(m)) {
this.probeRecorder = new recorder_1.ProbeRecorder(m);
this.startProbing = () => {
m.connectProbe(this.probeRecorder);
return this.probeRecorder;
};
this.stopProbing = () => {
m.connectProbe(null);
};
}
if (hasBIOS(m)) {
this.loadBIOS = (title, data) => {
m.loadBIOS(data, title);
};
}
if (hasSerialIO(m)) {
if (this.serialIOInterface == null) {
this.serialVisualizer = new SerialIOVisualizer(this.mainElement, m);
}
else {
m.connectSerialIO(this.serialIOInterface);
}
}
}
loadROM(title, data) {
this.machine.loadROM(data);
this.reset();
}
pollControls() {
this.poller && this.poller.poll();
if (hasPaddleInput(this.machine)) {
this.machine.setPaddleInput(0, this.video.paddle_x);
this.machine.setPaddleInput(1, this.video.paddle_y);
}
// TODO: put into interface
if (this.machine['pollControls']) {
this.machine['pollControls']();
}
}
advance(novideo) {
var steps = this.machine.advanceFrame(this.getDebugCallback());
if (!novideo && this.video)
this.video.updateFrame();
if (!novideo && this.serialVisualizer)
this.serialVisualizer.refresh();
return steps;
}
advanceFrameClock(trap, step) {
if (!(step > 0))
return;
if (this.machine instanceof wasmplatform_1.BaseWASMMachine) {
return this.machine.advanceFrameClock(trap, step);
}
else {
return this.machine.advanceFrame(() => {
return --step <= 0;
});
}
}
isRunning() {
return this.timer && this.timer.isRunning();
}
resume() {
this.timer.start();
this.audio && this.audio.start();
}
pause() {
this.timer.stop();
this.audio && this.audio.stop();
// i guess for runToVsync()?
if (this.probeRecorder) {
this.probeRecorder.singleFrame = true;
}
}
// so probe views stick around TODO: must be a better way?
runToVsync() {
if (this.probeRecorder) {
this.probeRecorder.clear();
this.probeRecorder.singleFrame = false;
}
super.runToVsync();
}
// TODO: reset target clock counter
getRasterScanline() {
return isRaster(this.machine) && this.machine.getRasterY();
}
readAddress(addr) {
return this.machine.read(addr);
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
}
getDebugInfo(category, state) {
return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
exports.BaseMachinePlatform = BaseMachinePlatform;
// TODO: move debug info into CPU?
class Base6502MachinePlatform extends BaseMachinePlatform {
constructor() {
super(...arguments);
this.getOpcodeMetadata = getOpcodeMetadata_6502;
this.getToolForFilename = getToolForFilename_6502;
}
disassemble(pc, read) {
return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2));
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
else
return ['CPU', 'ZPRAM', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return (0, emu_1.dumpRAM)(state.b || state.ram, 0x0, 0x100);
case 'Stack': return dumpStackToString(this, state.b || state.ram, 0x100, 0x1ff, 0x100 + state.c.SP, 0x20);
default: return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
}
exports.Base6502MachinePlatform = Base6502MachinePlatform;
class BaseZ80MachinePlatform extends BaseMachinePlatform {
constructor() {
super(...arguments);
//getOpcodeMetadata = getOpcodeMetadata_z80;
this.getToolForFilename = getToolForFilename_z80;
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
else
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_Z80(state.c);
case 'Stack': {
var sp = (state.c.SP - 1) & 0xffff;
var start = sp & 0xff00;
var end = start + 0xff;
if (sp == 0)
sp = 0x10000;
console.log(sp, start, end);
return dumpStackToString(this, [], start, end, sp, 0xcd);
}
default: return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
disassemble(pc, read) {
return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3));
}
}
exports.BaseZ80MachinePlatform = BaseZ80MachinePlatform;
///
class SerialIOVisualizer {
constructor(parentElement, device) {
this.lastOutCount = -1;
this.lastInCount = -1;
this.device = device;
this.textarea = document.createElement("textarea");
this.textarea.classList.add('transcript');
this.textarea.classList.add('transcript-style-2');
this.textarea.style.display = 'none';
parentElement.appendChild(this.textarea);
/*
this.vlist = new VirtualTextScroller(parentElement);
this.vlist.create(parentElement, 1024, this.getMemoryLineAt.bind(this));
this.vlist.maindiv.style.height = '8em';
this.vlist.maindiv.style.overflow = 'clip';
*/
}
reset() {
this.lastOutCount = 0;
this.lastInCount = 0;
this.textarea.style.display = 'none';
}
refresh() {
var lastop = '';
if (this.device.serialOut.length != this.lastOutCount) {
var s = '';
for (var ev of this.device.serialOut) {
if (lastop != ev.op) {
if (s != '')
s += '\n';
if (ev.op === 'read')
s += '<< ';
else if (ev.op === 'write')
s += '>> ';
lastop = ev.op;
}
if (ev.value == 10) {
s += '\u21b5';
lastop = '';
}
else {
s += (0, util_1.byteToASCII)(ev.value);
}
}
this.textarea.value = s;
this.lastOutCount = this.device.serialOut.length;
this.textarea.style.display = 'block';
}
}
}
//# sourceMappingURL=baseplatform.js.map

File diff suppressed because one or more lines are too long

1814
gen/common/basic/compiler.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

53
gen/common/basic/fuzz.js Normal file
View File

@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fuzz = void 0;
const compiler_1 = require("./compiler");
const runtime_1 = require("./runtime");
const emu_1 = require("../emu");
process.on('unhandledRejection', (reason, promise) => {
if (!(reason instanceof emu_1.EmuHalt))
console.log('Unhandled Rejection at:', promise, 'reason:', reason);
// Application specific logging, throwing an error, or other logic here
});
function fuzz(buf) {
var parser = new compiler_1.BASICParser();
var str = buf.toString();
try {
var pgm = parser.parseFile(str, "test.bas");
var runtime = new runtime_1.BASICRuntime();
runtime.load(pgm);
runtime.reset();
runtime.print = (s) => {
if (s == null)
throw new Error("PRINT null string");
};
runtime.input = function (prompt, nargs) {
var p = new Promise((resolve, reject) => {
var arr = [];
for (var i = 0; i < Math.random() * 10; i++)
arr.push(i + "");
resolve({ vals: arr, line: arr.join(' ') });
});
return p;
};
for (var i = 0; i < 50000; i++) {
if (!runtime.step())
break;
}
if (Math.random() < 0.001)
runtime.load(pgm);
for (var i = 0; i < 50000; i++) {
if (!runtime.step())
break;
}
}
catch (e) {
if (e instanceof emu_1.EmuHalt)
return;
if (e instanceof compiler_1.CompileError)
return;
throw e;
}
}
exports.fuzz = fuzz;
//# sourceMappingURL=fuzz.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fuzz.js","sourceRoot":"","sources":["../../../src/common/basic/fuzz.ts"],"names":[],"mappings":";;;AACA,yCAA+E;AAC/E,uCAAwD;AACxD,gCAAiC;AAEjC,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;IACjD,IAAI,CAAC,CAAC,MAAM,YAAY,aAAO,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3E,uEAAuE;AACvE,CAAC,CAAC,CAAC;AAEH,SAAgB,IAAI,CAAC,GAAG;IACpB,IAAI,MAAM,GAAG,IAAI,sBAAW,EAAE,CAAC;IAC/B,IAAI,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACzB,IAAI;QACA,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC5C,IAAI,OAAO,GAAG,IAAI,sBAAY,EAAE,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE;YAClB,IAAI,CAAC,IAAI,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACxD,CAAC,CAAA;QACD,OAAO,CAAC,KAAK,GAAG,UAAS,MAAc,EAAE,KAAa;YAClD,IAAI,CAAC,GAAG,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACpD,IAAI,GAAG,GAAG,EAAE,CAAC;gBACb,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,IAAI,CAAC,MAAM,EAAE,GAAC,EAAE,EAAE,CAAC,EAAE;oBACjC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAC,EAAE,CAAC,CAAC;gBACnB,OAAO,CAAC,EAAC,IAAI,EAAC,GAAG,EAAE,IAAI,EAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,CAAC;QACb,CAAC,CAAA;QACD,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACxB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;gBAAE,MAAM;SAC9B;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7C,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACxB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;gBAAE,MAAM;SAC9B;KACJ;IAAC,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,YAAY,aAAO;YAAE,OAAO;QACjC,IAAI,CAAC,YAAY,uBAAY;YAAE,OAAO;QACtC,MAAM,CAAC,CAAC;KACX;AACL,CAAC;AAhCD,oBAgCC"}

174
gen/common/basic/run.js Normal file
View File

@@ -0,0 +1,174 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const compiler_1 = require("./compiler");
const runtime_1 = require("./runtime");
const util_1 = require("../util");
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
crlfDelay: Infinity,
});
var inputlines = [];
rl.on('line', (line) => {
//console.log(`Line from file: ${line}`);
inputlines.push(line);
});
var fs = require('fs');
var parser = new compiler_1.BASICParser();
var runtime = new runtime_1.BASICRuntime();
function getCurrentLabel() {
var loc = runtime.getCurrentSourceLocation();
return loc ? loc.label : "?";
}
// parse args
var filename = '/dev/stdin';
var args = process.argv.slice(2);
var force = false;
for (var i = 0; i < args.length; i++) {
if (args[i] == '-v')
runtime.trace = true;
else if (args[i] == '-d')
parser.opts = compiler_1.DIALECTS[args[++i]] || Error('no such dialect');
else if (args[i] == '-f')
force = true;
else if (args[i] == '--dialects')
dumpDialectInfo();
else
filename = args[i];
}
// parse file
var data = fs.readFileSync(filename, 'utf-8');
try {
var pgm = parser.parseFile(data, filename);
}
catch (e) {
console.log(e);
if (parser.errors.length == 0)
console.log(`@@@ ${e}`);
}
parser.errors.forEach((err) => console.log(`@@@ ${err.msg} (line ${err.label})`));
if (parser.errors.length && !force)
process.exit(2);
// run program
try {
runtime.load(pgm);
}
catch (e) {
console.log(`### ${e.message} (line ${getCurrentLabel()})`);
process.exit(1);
}
runtime.reset();
runtime.print = (s) => {
fs.writeSync(1, s + "");
};
runtime.input = async (prompt) => {
return new Promise((resolve, reject) => {
function answered(answer) {
var line = answer.toUpperCase();
var vals = line.split(',');
//console.log(">>>",vals);
resolve({ line: line, vals: vals });
}
prompt += ' ?';
if (inputlines.length) {
fs.writeSync(1, prompt);
fs.writeSync(1, '\n');
answered(inputlines.shift());
}
else
rl.question(prompt, (answer) => {
fs.writeSync(1, '\n');
answered(answer);
});
});
};
runtime.resume = function () {
process.nextTick(() => {
try {
if (runtime.step()) {
if (runtime.running)
runtime.resume();
}
else if (runtime.exited) {
//console.log("*** PROGRAM EXITED ***");
process.exit(0);
}
}
catch (e) {
console.log(`### ${e.message} (line ${getCurrentLabel()})`);
process.exit(1);
}
});
};
runtime.resume();
/////
function dumpDialectInfo() {
var dialects = new Set();
var array = {};
var SELECTED_DIALECTS = ['TINY', 'ECMA55', 'DARTMOUTH', 'HP', 'DEC', 'ALTAIR', 'BASIC80', 'MODERN'];
SELECTED_DIALECTS.forEach((dkey) => {
dialects.add(compiler_1.DIALECTS[dkey]);
});
var ALL_KEYWORDS = new Set();
var ALL_FUNCTIONS = new Set();
var ALL_OPERATORS = new Set();
dialects.forEach((dialect) => {
Object.entries(dialect).forEach(([key, value]) => {
if (value === null)
value = "all";
else if (value === true)
value = "Y";
else if (value === false)
value = "-";
else if (Array.isArray(value))
value = value.length;
if (!array[key])
array[key] = [];
array[key].push(value);
if (dialect.validKeywords)
dialect.validKeywords.map(ALL_KEYWORDS.add.bind(ALL_KEYWORDS));
if (dialect.validFunctions)
dialect.validFunctions.map(ALL_FUNCTIONS.add.bind(ALL_FUNCTIONS));
if (dialect.validOperators)
dialect.validOperators.map(ALL_OPERATORS.add.bind(ALL_OPERATORS));
});
});
dialects.forEach((dialect) => {
ALL_KEYWORDS.forEach((keyword) => {
if (parser.supportsCommand(keyword)) {
var has = dialect.validKeywords == null || dialect.validKeywords.indexOf(keyword) >= 0;
keyword = '`' + keyword + '`';
if (!array[keyword])
array[keyword] = [];
array[keyword].push(has ? "Y" : "-");
}
});
ALL_OPERATORS.forEach((keyword) => {
var has = dialect.validOperators == null || dialect.validOperators.indexOf(keyword) >= 0;
if (keyword == '#')
keyword = '*#*';
keyword = "*a* " + keyword + " *b*";
if (!array[keyword])
array[keyword] = [];
array[keyword].push(has ? "Y" : "-");
});
ALL_FUNCTIONS.forEach((keyword) => {
if (runtime.supportsFunction(keyword)) {
var has = dialect.validFunctions == null || dialect.validFunctions.indexOf(keyword) >= 0;
keyword = '`' + keyword + '()`';
if (!array[keyword])
array[keyword] = [];
array[keyword].push(has ? "Y" : "-");
}
});
});
Object.entries(array).forEach(([key, arr]) => {
var s = (0, util_1.rpad)(key, 30) + "|";
s += arr.map((val) => (0, util_1.rpad)(val, 9)).join('|');
console.log(s);
});
process.exit(0);
}
//# sourceMappingURL=run.js.map

File diff suppressed because one or more lines are too long

1303
gen/common/basic/runtime.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2653
gen/common/cpu/6809.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

4007
gen/common/cpu/ARM.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2106
gen/common/cpu/MOS6502.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2816
gen/common/cpu/ZilogZ80.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,286 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.disassemble6502 = exports.OPS_6502 = void 0;
const util_1 = require("../util");
exports.OPS_6502 = [
{ mn: "BRK", am: "", nb: 1, il: 0, c1: 7, c2: 0 },
{ mn: "ORA", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SLO", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa", nb: 2, il: 1, c1: 3, c2: 0 },
{ mn: "ORA", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "ASL", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "SLO", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "PHP", am: "", nb: 1, il: 0, c1: 3, c2: 0 },
{ mn: "ORA", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "ASL", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ANC", am: "#aa", nb: 2, il: 1, c1: 2, c2: 0 },
{ mn: "NOP", am: "AAAA", nb: 3, il: 1, c1: 4, c2: 0 },
{ mn: "ORA", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "ASL", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "SLO", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BPL", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "ORA", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SLO", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "ORA", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "ASL", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "SLO", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "CLC", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ORA", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SLO", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "ORA", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "ASL", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "SLO", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "JSR", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "AND", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RLA", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "BIT", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "AND", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "ROL", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "RLA", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "PLP", am: "", nb: 1, il: 0, c1: 4, c2: 0 },
{ mn: "AND", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "ROL", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ANC", am: "#aa", nb: 2, il: 1, c1: 2, c2: 0 },
{ mn: "BIT", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "AND", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "ROL", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "RLA", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BMI", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "AND", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RLA", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "AND", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "ROL", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "RLA", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "SEC", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "AND", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RLA", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "AND", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "ROL", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "RLA", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "RTI", am: "", nb: 1, il: 0, c1: 6, c2: 0 },
{ mn: "EOR", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SRE", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa", nb: 2, il: 1, c1: 3, c2: 0 },
{ mn: "EOR", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "LSR", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "SRE", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "PHA", am: "", nb: 1, il: 0, c1: 3, c2: 0 },
{ mn: "EOR", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "LSR", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ASR", am: "#aa", nb: 2, il: 1, c1: 2, c2: 0 },
{ mn: "JMP", am: "AAAA", nb: 3, il: 0, c1: 3, c2: 0 },
{ mn: "EOR", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "LSR", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "SRE", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BVC", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "EOR", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SRE", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "EOR", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "LSR", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "SRE", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "CLI", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "EOR", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SRE", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "EOR", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "LSR", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "SRE", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "RTS", am: "", nb: 1, il: 0, c1: 6, c2: 0 },
{ mn: "ADC", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RRA", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa", nb: 2, il: 1, c1: 3, c2: 0 },
{ mn: "ADC", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "ROR", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "RRA", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "PLA", am: "", nb: 1, il: 0, c1: 4, c2: 0 },
{ mn: "ADC", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "ROR", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ARR", am: "#aa", nb: 2, il: 1, c1: 2, c2: 0 },
{ mn: "JMP", am: "(AAAA)", nb: 3, il: 0, c1: 5, c2: 0 },
{ mn: "ADC", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "ROR", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "RRA", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BVS", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "ADC", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RRA", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "ADC", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "ROR", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "RRA", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "SEI", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ADC", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "RRA", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "ADC", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "ROR", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "RRA", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "STA", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "NOP", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "SAX", am: "(aa,x)", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "STY", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "STA", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "STX", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "SAX", am: "aa", nb: 2, il: 1, c1: 3, c2: 0 },
{ mn: "DEY", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "NOP", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "TXA", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "ANE", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "STY", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "STA", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "STX", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "SAX", am: "AAAA", nb: 3, il: 1, c1: 4, c2: 0 },
{ mn: "BCC", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "STA", am: "(aa),y", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "SHA", am: "(aa),y", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "STY", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "STA", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "STX", am: "aa,y", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "SAX", am: "aa,y", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "TYA", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "STA", am: "AAAA,y", nb: 3, il: 0, c1: 5, c2: 0 },
{ mn: "TXS", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "SHS", am: "AAAA,y", nb: 3, il: 1, c1: 0, c2: 0 },
{ mn: "SHY", am: "AAAA,x", nb: 3, il: 1, c1: 0, c2: 0 },
{ mn: "STA", am: "AAAA,x", nb: 3, il: 0, c1: 5, c2: 0 },
{ mn: "SHX", am: "AAAA,y", nb: 3, il: 1, c1: 0, c2: 0 },
{ mn: "SHA", am: "AAAA,y", nb: 3, il: 1, c1: 0, c2: 0 },
{ mn: "LDY", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "LDA", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "LDX", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "LAX", am: "(aa,x)", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "LDY", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "LDA", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "LDX", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "LAX", am: "aa", nb: 2, il: 1, c1: 3, c2: 0 },
{ mn: "TAY", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "LDA", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "TAX", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "LXA", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "LDY", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "LDA", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "LDX", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "LAX", am: "AAAA", nb: 3, il: 1, c1: 4, c2: 0 },
{ mn: "BCS", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "LDA", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "LAX", am: "(aa),y", nb: 2, il: 1, c1: 5, c2: 1 },
{ mn: "LDY", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "LDA", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "LDX", am: "aa,y", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "LAX", am: "aa,y", nb: 2, il: 1, c1: 4, c2: 1 },
{ mn: "CLV", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "LDA", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "TSX", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "LAS", am: "AAAA,y", nb: 3, il: 1, c1: 0, c2: 0 },
{ mn: "LDY", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "LDA", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "LDX", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "LAX", am: "AAAA,y", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "CPY", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "CMP", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "NOP", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "DCP", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "CPY", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "CMP", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "DEC", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "DCP", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "INY", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "CMP", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "DEX", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "SBX", am: "#aa", nb: 2, il: 1, c1: 2, c2: 0 },
{ mn: "CPY", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "CMP", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "DEC", am: "AAAA", nb: 3, il: 0, c1: 3, c2: 0 },
{ mn: "DCP", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BNE", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "CMP", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "DCP", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "CMP", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "DEC", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "DCP", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "CLD", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "CMP", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "DCP", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "CMP", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "DEC", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "DCP", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "CPX", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "SBC", am: "(aa,x)", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "NOP", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "ISB", am: "(aa,x)", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "CPX", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "SBC", am: "aa", nb: 2, il: 0, c1: 3, c2: 0 },
{ mn: "INC", am: "aa", nb: 2, il: 0, c1: 5, c2: 0 },
{ mn: "ISB", am: "aa", nb: 2, il: 1, c1: 5, c2: 0 },
{ mn: "INX", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "SBC", am: "#aa", nb: 2, il: 0, c1: 2, c2: 0 },
{ mn: "NOP", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "SBC", am: "#aa", nb: 2, il: 1, c1: 0, c2: 0 },
{ mn: "CPX", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "SBC", am: "AAAA", nb: 3, il: 0, c1: 4, c2: 0 },
{ mn: "INC", am: "AAAA", nb: 3, il: 0, c1: 6, c2: 0 },
{ mn: "ISB", am: "AAAA", nb: 3, il: 1, c1: 6, c2: 0 },
{ mn: "BEQ", am: "branch", nb: 2, il: 0, c1: 2, c2: 2 },
{ mn: "SBC", am: "(aa),y", nb: 2, il: 0, c1: 5, c2: 1 },
{ mn: "KIL", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "ISB", am: "(aa),y", nb: 2, il: 1, c1: 8, c2: 1 },
{ mn: "NOP", am: "aa,x", nb: 2, il: 1, c1: 4, c2: 0 },
{ mn: "SBC", am: "aa,x", nb: 2, il: 0, c1: 4, c2: 0 },
{ mn: "INC", am: "aa,x", nb: 2, il: 0, c1: 6, c2: 0 },
{ mn: "ISB", am: "aa,x", nb: 2, il: 1, c1: 6, c2: 1 },
{ mn: "SED", am: "", nb: 1, il: 0, c1: 2, c2: 0 },
{ mn: "SBC", am: "AAAA,y", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "NOP", am: "", nb: 1, il: 1, c1: 0, c2: 0 },
{ mn: "ISB", am: "AAAA,y", nb: 3, il: 1, c1: 7, c2: 1 },
{ mn: "NOP", am: "AAAA,x", nb: 3, il: 1, c1: 4, c2: 1 },
{ mn: "SBC", am: "AAAA,x", nb: 3, il: 0, c1: 4, c2: 1 },
{ mn: "INC", am: "AAAA,x", nb: 3, il: 0, c1: 7, c2: 0 },
{ mn: "ISB", am: "AAAA,x", nb: 3, il: 1, c1: 7, c2: 1 }, // FF
];
function disassemble6502(pc, b0, b1, b2) {
var op = exports.OPS_6502[b0];
if (op == null)
return { line: "???", nbytes: 1, isaddr: false };
var s = op.mn;
var am = op.am;
var isaddr = false;
if (am == 'branch') {
var offset = (b1 < 0x80) ? (pc + 2 + b1) : (pc + 2 - (256 - b1));
offset &= 0xffff;
am = '$' + (0, util_1.hex)(offset, 4);
isaddr = true;
}
else {
am = am.replace('aa', '$' + (0, util_1.hex)(b1, 2));
am = am.replace('AAAA', '$' + (0, util_1.hex)(b1 + (b2 << 8), 4));
if (am.indexOf('#') < 0 && am.indexOf('$') >= 0)
isaddr = true;
}
return { line: op.mn + " " + am, nbytes: op.nb, isaddr: isaddr };
}
exports.disassemble6502 = disassemble6502;
;
//# sourceMappingURL=disasm6502.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.disassembleZ80 = void 0;
const util_1 = require("../util");
const Z80_OPS = ["nop", "ld bc,xx", "ld (bc),a", "inc bc", "inc b", "dec b", "ld b,x", "rlca", "ex af,af'", "add hl,bc", "ld a,(bc)", "dec bc", "inc c", "dec c", "ld c,x", "rrca", "djnz x", "ld de,xx", "ld (de),a", "inc de", "inc d", "dec d", "ld d,x", "rla", "jr x", "add hl,de", "ld a,(de)", "dec de", "inc e", "dec e", "ld e,x", "rra", "jr nz,x", "ld hl,xx", "ld (xx),hl", "inc hl", "inc h", "dec h", "ld h,x", "daa", "jr z,x", "add hl,hl", "ld hl,(xx)", "dec hl", "inc l", "dec l", "ld l,x", "cpl", "jr nc,x", "ld sp,xx", "ld (xx),a", "inc sp", "inc (hl)", "dec (hl)", "ld (hl),x", "scf", "jr c,x", "add hl,sp", "ld a,(xx)", "dec sp", "inc a", "dec a", "ld a,x", "ccf", "ld b,b", "ld b,c", "ld b,d", "ld b,e", "ld b,h", "ld b,l", "ld b,(hl)", "ld b,a", "ld c,b", "ld c,c", "ld c,d", "ld c,e", "ld c,h", "ld c,l", "ld c,(hl)", "ld c,a", "ld d,b", "ld d,c", "ld d,d", "ld d,e", "ld d,h", "ld d,l", "ld d,(hl)", "ld d,a", "ld e,b", "ld e,c", "ld e,d", "ld e,e", "ld e,h", "ld e,l", "ld e,(hl)", "ld e,a", "ld h,b", "ld h,c", "ld h,d", "ld h,e", "ld h,h", "ld h,l", "ld h,(hl)", "ld h,a", "ld l,b", "ld l,c", "ld l,d", "ld l,e", "ld l,h", "ld l,l", "ld l,(hl)", "ld l,a", "ld (hl),b", "ld (hl),c", "ld (hl),d", "ld (hl),e", "ld (hl),h", "ld (hl),l", "halt", "ld (hl),a", "ld a,b", "ld a,c", "ld a,d", "ld a,e", "ld a,h", "ld a,l", "ld a,(hl)", "ld a,a", "add a,b", "add a,c", "add a,d", "add a,e", "add a,h", "add a,l", "add a,(hl)", "add a,a", "adc a,b", "adc a,c", "adc a,d", "adc a,e", "adc a,h", "adc a,l", "adc a,(hl)", "adc a,a", "sub b", "sub c", "sub d", "sub e", "sub h", "sub l", "sub (hl)", "sub a", "sbc a,b", "sbc a,c", "sbc a,d", "sbc a,e", "sbc a,h", "sbc a,l", "sbc a,(hl)", "sbc a,a", "and b", "and c", "and d", "and e", "and h", "and l", "and (hl)", "and a", "xor b", "xor c", "xor d", "xor e", "xor h", "xor l", "xor (hl)", "xor a", "or b", "or c", "or d", "or e", "or h", "or l", "or (hl)", "or a", "cp b", "cp c", "cp d", "cp e", "cp h", "cp l", "cp (hl)", "cp a", "ret nz", "pop bc", "jp nz,xx", "jp xx", "call nz,xx", "push bc", "add a,x", "rst 00h", "ret z", "ret", "jp z,xx", "xxBITxx", "call z,xx", "call xx", "adc a,x", "rst 08h", "ret nc", "pop de", "jp nc,xx", "out (x),a", "call nc,xx", "push de", "sub x", "rst 10h", "ret c", "exx", "jp c,xx", "in a,(x)", "call c,xx", "xxIXxx", "sbc a,x", "rst 18h", "ret po", "pop hl", "jp po,xx", "ex (sp),hl", "call po,xx", "push hl", "and x", "rst 20h", "ret pe", "jp (hl)", "jp pe,xx", "ex de,hl", "call pe,xx", "xx80xx", "xor x", "rst 28h", "ret p", "pop af", "jp p,xx", "di", "call p,xx", "push af", "or x", "rst 30h", "ret m", "ld sp,hl", "jp m,xx", "ei", "call m,xx", "xxIYxx", "cp x", "rst 38h"];
const Z80_OPS_ED = ["in b,(c)", "out (c),b", "sbc hl,bc", "ld (xx),bc", "neg", "retn", "im 0", "ld i,a", "in c,(c)", "out (c),c", "adc hl,bc", "ld bc,(xx)", "neg", "reti", "", "ld r,a", "in d,(c)", "out (c),d", "sbc hl,de", "ld (xx),de", "neg", "retn", "im 1", "ld a,i", "in e,(c)", "out (c),e", "adc hl,de", "ld de,(xx)", "neg", "retn", "im 2", "ld a,r", "in h,(c)", "out (c),h", "sbc hl,hl", "ld (xx),hl", "neg", "retn", "", "rrd", "in l,(c)", "out (c),l", "adc hl,hl", "ld hl,(xx)", "neg", "retn", "", "rld", "in f,(c)", "out (c),f", "sbc hl,sp", "ld (xx),sp", "neg", "retn", "", "", "in a,(c)", "out (c),a", "adc hl,sp", "ld sp,(xx)", "neg", "reti", "", "", "ldi", "cpi", "ini", "outi", "", "", "", "", "ldd", "cpd", "ind", "outd", "", "", "", "", "ldir", "cpir", "inir", "otir", "", "", "", "", "lddr", "cpdr", "indr", "otdr", "", "", "", ""];
const Z80_OPS_CB = ["rlc b", "rlc c", "rlc d", "rlc e", "rlc h", "rlc l", "rlc (hl)", "rlc a", "rrc b", "rrc c", "rrc d", "rrc e", "rrc h", "rrc l", "rrc (hl)", "rrc a", "rl b", "rl c", "rl d", "rl e", "rl h", "rl l", "rl (hl)", "rl a", "rr b", "rr c", "rr d", "rr e", "rr h", "rr l", "rr (hl)", "rr a", "sla b", "sla c", "sla d", "sla e", "sla h", "sla l", "sla (hl)", "sla a", "sra b", "sra c", "sra d", "sra e", "sra h", "sra l", "sra (hl)", "sra a", "sll b", "sll c", "sll d", "sll e", "sll h", "sll l", "sll (hl)", "sll a", "srl b", "srl c", "srl d", "srl e", "srl h", "srl l", "srl (hl)", "srl a", "bit 0,b", "bit 0,c", "bit 0,d", "bit 0,e", "bit 0,h", "bit 0,l", "bit 0,(hl)", "bit 0,a", "bit 1,b", "bit 1,c", "bit 1,d", "bit 1,e", "bit 1,h", "bit 1,l", "bit 1,(hl)", "bit 1,a", "bit 2,b", "bit 2,c", "bit 2,d", "bit 2,e", "bit 2,h", "bit 2,l", "bit 2,(hl)", "bit 2,a", "bit 3,b", "bit 3,c", "bit 3,d", "bit 3,e", "bit 3,h", "bit 3,l", "bit 3,(hl)", "bit 3,a", "bit 4,b", "bit 4,c", "bit 4,d", "bit 4,e", "bit 4,h", "bit 4,l", "bit 4,(hl)", "bit 4,a", "bit 5,b", "bit 5,c", "bit 5,d", "bit 5,e", "bit 5,h", "bit 5,l", "bit 5,(hl)", "bit 5,a", "bit 6,b", "bit 6,c", "bit 6,d", "bit 6,e", "bit 6,h", "bit 6,l", "bit 6,(hl)", "bit 6,a", "bit 7,b", "bit 7,c", "bit 7,d", "bit 7,e", "bit 7,h", "bit 7,l", "bit 7,(hl)", "bit 7,a", "res 0,b", "res 0,c", "res 0,d", "res 0,e", "res 0,h", "res 0,l", "res 0,(hl)", "res 0,a", "res 1,b", "res 1,c", "res 1,d", "res 1,e", "res 1,h", "res 1,l", "res 1,(hl)", "res 1,a", "res 2,b", "res 2,c", "res 2,d", "res 2,e", "res 2,h", "res 2,l", "res 2,(hl)", "res 2,a", "res 3,b", "res 3,c", "res 3,d", "res 3,e", "res 3,h", "res 3,l", "res 3,(hl)", "res 3,a", "res 4,b", "res 4,c", "res 4,d", "res 4,e", "res 4,h", "res 4,l", "res 4,(hl)", "res 4,a", "res 5,b", "res 5,c", "res 5,d", "res 5,e", "res 5,h", "res 5,l", "res 5,(hl)", "res 5,a", "res 6,b", "res 6,c", "res 6,d", "res 6,e", "res 6,h", "res 6,l", "res 6,(hl)", "res 6,a", "res 7,b", "res 7,c", "res 7,d", "res 7,e", "res 7,h", "res 7,l", "res 7,(hl)", "res 7,a", "set 0,b", "set 0,c", "set 0,d", "set 0,e", "set 0,h", "set 0,l", "set 0,(hl)", "set 0,a", "set 1,b", "set 1,c", "set 1,d", "set 1,e", "set 1,h", "set 1,l", "set 1,(hl)", "set 1,a", "set 2,b", "set 2,c", "set 2,d", "set 2,e", "set 2,h", "set 2,l", "set 2,(hl)", "set 2,a", "set 3,b", "set 3,c", "set 3,d", "set 3,e", "set 3,h", "set 3,l", "set 3,(hl)", "set 3,a", "set 4,b", "set 4,c", "set 4,d", "set 4,e", "set 4,h", "set 4,l", "set 4,(hl)", "set 4,a", "set 5,b", "set 5,c", "set 5,d", "set 5,e", "set 5,h", "set 5,l", "set 5,(hl)", "set 5,a", "set 6,b", "set 6,c", "set 6,d", "set 6,e", "set 6,h", "set 6,l", "set 6,(hl)", "set 6,a", "set 7,b", "set 7,c", "set 7,d", "set 7,e", "set 7,h", "set 7,l", "set 7,(hl)", "set 7,a"];
function disassembleZ80(pc, b0, b1, b2, b3) {
var op, n, am;
var bytes = [b0, b1, b2, b3];
var isaddr = false;
n = 1;
switch (b0) {
case 0xcb:
am = Z80_OPS_CB[b1];
n++;
break;
case 0xed:
if (b1 >= 0x40 && b1 <= 0x7f)
am = Z80_OPS_ED[b1 - 0x40];
if (b1 >= 0xa0 && b1 <= 0xbf)
am = Z80_OPS_ED[b1 - 0xa0 + 0x40];
n++;
break;
case 0xdd:
case 0xfd:
var ireg = (b0 == 0xdd) ? 'ix' : 'iy';
if (b1 == 0xcb) {
// swap the 3rd and 4th bytes [$dd $cb displacement opcode]
am = Z80_OPS_CB[b3];
bytes[2] = b3;
bytes[3] = b2;
n++;
}
else {
am = Z80_OPS[b1];
}
am = am.replace(/[(]hl[)]/, '(' + ireg + '+x)');
am = am.replace(/\bhl\b/, ireg);
n++;
break;
default:
am = Z80_OPS[b0];
break;
}
if (!am || !am.length)
am = "??";
if (/\bxx\b/.test(am)) {
am = am.replace(/\bxx\b/, '$' + (0, util_1.hex)(bytes[n] + (bytes[n + 1] << 8), 4));
n += 2;
isaddr = true;
}
else if (/\bx\b/.test(am)) {
if (am.startsWith('j')) {
var offset = (b1 < 0x80) ? (pc + 2 + b1) : (pc + 2 - (256 - b1));
offset &= 0xffff;
am = am.replace(/\bx\b/, '$' + (0, util_1.hex)(offset, 4));
isaddr = true;
}
else {
am = am.replace(/\bx\b/, '$' + (0, util_1.hex)(bytes[n], 2));
}
n += 1;
}
return { line: am.toUpperCase(), nbytes: n, isaddr: isaddr };
}
exports.disassembleZ80 = disassembleZ80;
;
//# sourceMappingURL=disasmz80.js.map

File diff suppressed because one or more lines are too long

162
gen/common/devices.js Normal file
View File

@@ -0,0 +1,162 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicScanlineMachine = exports.BasicMachine = exports.BasicHeadlessMachine = exports.NullProbe = void 0;
class NullProbe {
logClocks() { }
logNewScanline() { }
logNewFrame() { }
logExecute() { }
logInterrupt() { }
logRead() { }
logWrite() { }
logIORead() { }
logIOWrite() { }
logVRAMRead() { }
logVRAMWrite() { }
logIllegal() { }
logData() { }
addLogBuffer(src) { }
}
exports.NullProbe = NullProbe;
class BasicHeadlessMachine {
constructor() {
this.inputs = new Uint8Array(32);
this.nullProbe = new NullProbe();
this.probe = this.nullProbe;
}
setKeyInput(key, code, flags) {
this.handler && this.handler(key, code, flags);
}
connectProbe(probe) {
this.probe = probe || this.nullProbe;
}
reset() {
this.cpu.reset();
}
loadROM(data, title) {
if (!this.rom)
this.rom = new Uint8Array(this.defaultROMSize);
this.rom.set(data);
}
loadState(state) {
this.cpu.loadState(state.c);
this.ram.set(state.ram);
this.inputs.set(state.inputs);
}
saveState() {
return {
c: this.cpu.saveState(),
ram: this.ram.slice(0),
inputs: this.inputs.slice(0),
};
}
loadControlsState(state) {
this.inputs.set(state.inputs);
}
saveControlsState() {
return {
inputs: this.inputs.slice(0)
};
}
advanceCPU() {
var c = this.cpu;
var n = 1;
if (this.cpu.isStable()) {
this.probe.logExecute(this.cpu.getPC(), this.cpu.getSP());
}
if (c.advanceClock) {
c.advanceClock();
}
else if (c.advanceInsn) {
n = c.advanceInsn(1);
}
this.probe.logClocks(n);
return n;
}
probeMemoryBus(membus) {
return {
read: (a) => {
let val = membus.read(a);
this.probe.logRead(a, val);
return val;
},
write: (a, v) => {
this.probe.logWrite(a, v);
membus.write(a, v);
}
};
}
connectCPUMemoryBus(membus) {
this.cpu.connectMemoryBus(this.probeMemoryBus(membus));
}
probeIOBus(iobus) {
return {
read: (a) => {
let val = iobus.read(a);
this.probe.logIORead(a, val);
return val;
},
write: (a, v) => {
this.probe.logIOWrite(a, v);
iobus.write(a, v);
}
};
}
connectCPUIOBus(iobus) {
this.cpu['connectIOBus'](this.probeIOBus(iobus));
}
}
exports.BasicHeadlessMachine = BasicHeadlessMachine;
class BasicMachine extends BasicHeadlessMachine {
constructor() {
super(...arguments);
this.overscan = false;
this.rotate = 0;
}
getAudioParams() {
return { sampleRate: this.sampleRate, stereo: false };
}
connectAudio(audio) {
this.audio = audio;
}
getVideoParams() {
return { width: this.canvasWidth, height: this.numVisibleScanlines, overscan: this.overscan, rotate: this.rotate };
}
connectVideo(pixels) {
this.pixels = pixels;
}
}
exports.BasicMachine = BasicMachine;
class BasicScanlineMachine extends BasicMachine {
advanceFrame(trap) {
this.preFrame();
var endLineClock = 0;
var steps = 0;
this.probe.logNewFrame();
this.frameCycles = 0;
for (var sl = 0; sl < this.numTotalScanlines; sl++) {
endLineClock += this.cpuCyclesPerLine; // could be fractional
this.scanline = sl;
this.startScanline();
while (this.frameCycles < endLineClock) {
if (trap && trap()) {
sl = 999;
break;
}
this.frameCycles += this.advanceCPU();
steps++;
}
this.drawScanline();
this.probe.logNewScanline();
this.probe.logClocks(Math.floor(this.frameCycles - endLineClock)); // remainder of prev. line
}
this.postFrame();
return steps; // TODO: return steps, not clock? for recorder
}
preFrame() { }
postFrame() { }
getRasterY() { return this.scanline; }
getRasterX() { return this.frameCycles % this.cpuCyclesPerLine; }
}
exports.BasicScanlineMachine = BasicScanlineMachine;
//# sourceMappingURL=devices.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/common/devices.ts"],"names":[],"mappings":";;;AAoLA,MAAa,SAAS;IACpB,SAAS,KAAK,CAAC;IACf,cAAc,KAAI,CAAC;IACnB,WAAW,KAAK,CAAC;IACjB,UAAU,KAAK,CAAC;IAChB,YAAY,KAAI,CAAC;IACjB,OAAO,KAAK,CAAC;IACb,QAAQ,KAAK,CAAC;IACd,SAAS,KAAK,CAAC;IACf,UAAU,KAAK,CAAC;IAChB,WAAW,KAAK,CAAC;IACjB,YAAY,KAAI,CAAC;IACjB,UAAU,KAAK,CAAC;IAChB,OAAO,KAAU,CAAC;IAClB,YAAY,CAAC,GAAgB,IAAG,CAAC;CAClC;AAfD,8BAeC;AAaD,MAAsB,oBAAoB;IAA1C;QAUE,WAAM,GAAgB,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAGzC,cAAS,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,UAAK,GAAc,IAAI,CAAC,SAAS,CAAC;IA+EpC,CAAC;IA1EC,WAAW,CAAC,GAAU,EAAE,IAAW,EAAE,KAAY;QAC/C,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAC,IAAI,EAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,YAAY,CAAC,KAAe;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC;IACvC,CAAC;IACD,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,IAAe,EAAE,KAAa;QACpC,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,IAAI,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,SAAS,CAAC,KAAK;QACb,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IACD,SAAS;QACP,OAAO;YACL,CAAC,EAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE;YACtB,GAAG,EAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YACrB,MAAM,EAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;SAC5B,CAAC;IACJ,CAAC;IACD,iBAAiB,CAAC,KAAK;QACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IACD,iBAAiB;QACf,OAAO;YACL,MAAM,EAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;SAC5B,CAAC;IACJ,CAAC;IACD,UAAU;QACR,IAAI,CAAC,GAAG,IAAI,CAAC,GAAU,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;SAAE;QACvF,IAAI,CAAC,CAAC,YAAY,EAAE;YAAE,CAAC,CAAC,YAAY,EAAE,CAAC;SAAE;aACpC,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;SAAE;QACjD,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,cAAc,CAAC,MAAU;QACvB,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;gBACV,IAAI,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAC,GAAG,CAAC,CAAC;gBAC1B,OAAO,GAAG,CAAC;YACb,CAAC;YACD,KAAK,EAAE,CAAC,CAAC,EAAC,CAAC,EAAE,EAAE;gBACb,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAC,CAAC,CAAC,CAAC;gBACzB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAC,CAAC,CAAC,CAAC;YACpB,CAAC;SACF,CAAC;IACJ,CAAC;IACD,mBAAmB,CAAC,MAAU;QAC5B,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,UAAU,CAAC,KAAS;QAClB,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;gBACV,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAC,GAAG,CAAC,CAAC;gBAC5B,OAAO,GAAG,CAAC;YACb,CAAC;YACD,KAAK,EAAE,CAAC,CAAC,EAAC,CAAC,EAAE,EAAE;gBACb,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAC,CAAC,CAAC,CAAC;gBAC3B,KAAK,CAAC,KAAK,CAAC,CAAC,EAAC,CAAC,CAAC,CAAC;YACnB,CAAC;SACF,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,KAAS;QACvB,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,CAAC;CACF;AA7FD,oDA6FC;AAED,MAAsB,YAAa,SAAQ,oBAAoB;IAA/D;;QAKE,aAAQ,GAAa,KAAK,CAAC;QAC3B,WAAM,GAAY,CAAC,CAAC;IAmBtB,CAAC;IAZC,cAAc;QACZ,OAAO,EAAC,UAAU,EAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAC,KAAK,EAAC,CAAC;IACpD,CAAC;IACD,YAAY,CAAC,KAAwB;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,cAAc;QACZ,OAAO,EAAC,KAAK,EAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAC,IAAI,CAAC,mBAAmB,EAAE,QAAQ,EAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAC,IAAI,CAAC,MAAM,EAAC,CAAC;IAC/G,CAAC;IACD,YAAY,CAAC,MAAkB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAzBD,oCAyBC;AAED,MAAsB,oBAAqB,SAAQ,YAAY;IAU7D,YAAY,CAAC,IAAmB;QAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,KAAK,IAAI,EAAE,GAAC,CAAC,EAAE,EAAE,GAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE;YAC9C,YAAY,IAAI,IAAI,CAAC,gBAAgB,CAAC,CAAC,sBAAsB;YAC7D,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;YACnB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,WAAW,GAAG,YAAY,EAAE;gBACtC,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE;oBAClB,EAAE,GAAG,GAAG,CAAC;oBACT,MAAM;iBACP;gBACD,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACtC,KAAK,EAAE,CAAC;aACT;YACD,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,0BAA0B;SAC9F;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC,CAAC,8CAA8C;IAC9D,CAAC;IACD,QAAQ,KAAK,CAAC;IACd,SAAS,KAAK,CAAC;IACf,UAAU,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtC,UAAU,KAAK,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;CAClE;AAvCD,oDAuCC"}

663
gen/common/emu.js Normal file
View File

@@ -0,0 +1,663 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VirtualTextScroller = exports.getVisibleEditorLineHeight = exports.getMousePos = exports.newAddressDecoder = exports.AddressDecoder = exports.padBytes = exports.ControllerPoller = exports.makeKeycodeMap = exports.setKeyboardFromMap = exports.newKeyboardHandler = exports.Keys = exports.dumpRAM = exports.AnimationTimer = exports.useRequestAnimationFrame = exports.EmuHalt = exports.RAM = exports.VectorVideo = exports.RasterVideo = exports._setKeyboardEvents = exports.KeyFlags = exports.__createCanvas = exports.setNoiseSeed = exports.getNoiseSeed = exports.noise = exports.PLATFORMS = void 0;
const util_1 = require("./util");
const vlist_1 = require("./vlist");
// Emulator classes
exports.PLATFORMS = {};
var _random_state = 1;
function noise() {
let x = _random_state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return (_random_state = x) & 0xff;
}
exports.noise = noise;
function getNoiseSeed() {
return _random_state;
}
exports.getNoiseSeed = getNoiseSeed;
function setNoiseSeed(x) {
_random_state = x;
}
exports.setNoiseSeed = setNoiseSeed;
function __createCanvas(doc, mainElement, width, height) {
var canvas = doc.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.classList.add("emuvideo");
canvas.tabIndex = -1; // Make it focusable
mainElement.appendChild(canvas);
return canvas;
}
exports.__createCanvas = __createCanvas;
var KeyFlags;
(function (KeyFlags) {
KeyFlags[KeyFlags["KeyDown"] = 1] = "KeyDown";
KeyFlags[KeyFlags["Shift"] = 2] = "Shift";
KeyFlags[KeyFlags["Ctrl"] = 4] = "Ctrl";
KeyFlags[KeyFlags["Alt"] = 8] = "Alt";
KeyFlags[KeyFlags["Meta"] = 16] = "Meta";
KeyFlags[KeyFlags["KeyUp"] = 64] = "KeyUp";
KeyFlags[KeyFlags["KeyPress"] = 128] = "KeyPress";
})(KeyFlags = exports.KeyFlags || (exports.KeyFlags = {}));
function _setKeyboardEvents(canvas, callback) {
canvas.onkeydown = (e) => {
callback(e.which, 0, KeyFlags.KeyDown | _metakeyflags(e));
if (e.ctrlKey || e.which == 8 || e.which == 9 || e.which == 27) { // eat backspace, tab, escape keys
e.preventDefault();
}
};
canvas.onkeyup = (e) => {
callback(e.which, 0, KeyFlags.KeyUp | _metakeyflags(e));
};
canvas.onkeypress = (e) => {
callback(e.which, e.charCode, KeyFlags.KeyPress | _metakeyflags(e));
};
}
exports._setKeyboardEvents = _setKeyboardEvents;
;
class RasterVideo {
constructor(mainElement, width, height, options) {
this.paddle_x = 255;
this.paddle_y = 255;
this.mainElement = mainElement;
this.width = width;
this.height = height;
this.options = options;
}
setRotate(rotate) {
var canvas = this.canvas;
if (rotate) {
// TODO: aspect ratio?
canvas.style.transform = "rotate(" + rotate + "deg)";
if (canvas.width < canvas.height)
canvas.style.paddingLeft = canvas.style.paddingRight = "10%";
}
else {
canvas.style.transform = null;
canvas.style.paddingLeft = canvas.style.paddingRight = null;
}
}
create(doc) {
var canvas;
this.canvas = canvas = __createCanvas(doc || document, this.mainElement, this.width, this.height);
this.vcanvas = $(canvas);
if (this.options && this.options.rotate) {
this.setRotate(this.options.rotate);
}
if (this.options && this.options.overscan) {
this.vcanvas.css('padding', '0px');
}
if (this.options && this.options.aspect) {
console.log(this.options);
this.vcanvas.css('aspect-ratio', this.options.aspect + "");
}
this.ctx = canvas.getContext('2d');
this.imageData = this.ctx.createImageData(this.width, this.height);
this.datau32 = new Uint32Array(this.imageData.data.buffer);
}
setKeyboardEvents(callback) {
_setKeyboardEvents(this.canvas, callback);
}
getFrameData() { return this.datau32; }
getContext() { return this.ctx; }
updateFrame(sx, sy, dx, dy, w, h) {
if (w && h)
this.ctx.putImageData(this.imageData, sx, sy, dx, dy, w, h);
else
this.ctx.putImageData(this.imageData, 0, 0);
}
clearRect(dx, dy, w, h) {
var ctx = this.ctx;
ctx.fillStyle = '#000000';
ctx.fillRect(dx, dy, w, h);
}
setupMouseEvents(el) {
if (!el)
el = this.canvas;
$(el).mousemove((e) => {
var pos = getMousePos(el, e);
var new_x = Math.floor(pos.x * 255 / this.canvas.width);
var new_y = Math.floor(pos.y * 255 / this.canvas.height);
this.paddle_x = (0, util_1.clamp)(0, 255, new_x);
this.paddle_y = (0, util_1.clamp)(0, 255, new_y);
});
}
;
}
exports.RasterVideo = RasterVideo;
class VectorVideo extends RasterVideo {
constructor() {
super(...arguments);
this.persistenceAlpha = 0.5;
this.jitter = 1.0;
this.gamma = 0.8;
this.COLORS = [
'#111111',
'#1111ff',
'#11ff11',
'#11ffff',
'#ff1111',
'#ff11ff',
'#ffff11',
'#ffffff'
];
}
create() {
super.create();
this.sx = this.width / 1024.0;
this.sy = this.height / 1024.0;
}
clear() {
var ctx = this.ctx;
ctx.globalCompositeOperation = 'source-over';
ctx.globalAlpha = this.persistenceAlpha;
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, this.width, this.height);
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'lighter';
}
drawLine(x1, y1, x2, y2, intensity, color) {
var ctx = this.ctx;
var sx = this.sx;
var sy = this.sy;
//console.log(x1,y1,x2,y2,intensity,color);
if (intensity > 0) {
// TODO: landscape vs portrait
var alpha = Math.pow(intensity / 255.0, this.gamma);
ctx.globalAlpha = alpha;
ctx.lineWidth = 3;
ctx.beginPath();
// TODO: bright dots
var jx = this.jitter * (Math.random() - 0.5);
var jy = this.jitter * (Math.random() - 0.5);
x1 += jx;
x2 += jx;
y1 += jy;
y2 += jy;
ctx.moveTo(x1 * sx, this.height - y1 * sy);
if (x1 == x2 && y1 == y2)
ctx.lineTo(x2 * sx + 1, this.height - y2 * sy);
else
ctx.lineTo(x2 * sx, this.height - y2 * sy);
ctx.strokeStyle = this.COLORS[color & 7];
ctx.stroke();
}
}
}
exports.VectorVideo = VectorVideo;
class RAM {
constructor(size) {
this.mem = new Uint8Array(new ArrayBuffer(size));
}
}
exports.RAM = RAM;
class EmuHalt extends Error {
constructor(msg, loc) {
super(msg);
this.$loc = loc;
Object.setPrototypeOf(this, EmuHalt.prototype);
}
}
exports.EmuHalt = EmuHalt;
exports.useRequestAnimationFrame = false;
class AnimationTimer {
constructor(frequencyHz, callback) {
this.running = false;
this.pulsing = false;
this.nextts = 0;
this.useReqAnimFrame = exports.useRequestAnimationFrame && typeof window.requestAnimationFrame === 'function'; // need for unit test
this.frameRate = frequencyHz;
this.intervalMsec = 1000.0 / frequencyHz;
this.callback = callback;
}
scheduleFrame(msec) {
var fn = (timestamp) => {
try {
this.nextFrame(this.useReqAnimFrame ? timestamp : Date.now());
}
catch (e) {
this.running = false;
this.pulsing = false;
throw e;
}
};
if (this.useReqAnimFrame)
window.requestAnimationFrame(fn);
else
setTimeout(fn, msec);
}
nextFrame(ts) {
if (ts > this.nextts) {
if (this.running) {
this.callback();
}
if (this.nframes == 0)
this.startts = ts;
if (this.nframes++ == 300) {
console.log("Avg framerate: " + this.nframes * 1000 / (ts - this.startts) + " fps");
}
}
this.nextts += this.intervalMsec;
// frames skipped? catch up
if ((ts - this.nextts) > 1000) {
//console.log(ts - this.nextts, 'msec skipped');
this.nextts = ts;
}
if (this.running) {
this.scheduleFrame(this.nextts - ts);
}
else {
this.pulsing = false;
}
}
isRunning() {
return this.running;
}
start() {
if (!this.running) {
this.running = true;
this.nextts = 0;
this.nframes = 0;
if (!this.pulsing) {
this.scheduleFrame(0);
this.pulsing = true;
}
}
}
stop() {
this.running = false;
}
}
exports.AnimationTimer = AnimationTimer;
// TODO: move to util?
function dumpRAM(ram, ramofs, ramlen) {
var s = "";
var bpel = ram['BYTES_PER_ELEMENT'] || 1;
var perline = Math.ceil(16 / bpel);
var isFloat = ram instanceof Float32Array || ram instanceof Float64Array;
// TODO: show scrollable RAM for other platforms
for (var ofs = 0; ofs < ramlen; ofs += perline) {
s += '$' + (0, util_1.hex)(ofs + ramofs) + ':';
for (var i = 0; i < perline; i++) {
if (ofs + i < ram.length) {
if (i == perline / 2)
s += " ";
if (isFloat)
s += " " + ram[ofs + i].toPrecision(bpel * 2);
else
s += " " + (0, util_1.hex)(ram[ofs + i], bpel * 2);
}
}
s += "\n";
}
return s;
}
exports.dumpRAM = dumpRAM;
;
exports.Keys = {
ANYKEY: { c: 0, n: "?" },
// gamepad and keyboard (player 0)
UP: { c: 38, n: "Up", plyr: 0, yaxis: -1 },
DOWN: { c: 40, n: "Down", plyr: 0, yaxis: 1 },
LEFT: { c: 37, n: "Left", plyr: 0, xaxis: -1 },
RIGHT: { c: 39, n: "Right", plyr: 0, xaxis: 1 },
A: { c: 32, n: "Space", plyr: 0, button: 0 },
B: { c: 16, n: "Shift", plyr: 0, button: 1 },
GP_A: { c: 88, n: "X", plyr: 0, button: 0 },
GP_B: { c: 90, n: "Z", plyr: 0, button: 1 },
GP_C: { c: 86, n: "V", plyr: 0, button: 2 },
GP_D: { c: 67, n: "C", plyr: 0, button: 3 },
SELECT: { c: 220, n: "\\", plyr: 0, button: 8 },
START: { c: 13, n: "Enter", plyr: 0, button: 9 },
// gamepad and keyboard (player 1)
P2_UP: { c: 87, n: "W", plyr: 1, yaxis: -1 },
P2_DOWN: { c: 83, n: "S", plyr: 1, yaxis: 1 },
P2_LEFT: { c: 65, n: "A", plyr: 1, xaxis: -1 },
P2_RIGHT: { c: 68, n: "D", plyr: 1, xaxis: 1 },
P2_A: { c: 84, n: "T", plyr: 1, button: 0 },
P2_B: { c: 82, n: "R", plyr: 1, button: 1 },
P2_GP_A: { c: 69, n: "E", plyr: 1, button: 0 },
P2_GP_B: { c: 82, n: "R", plyr: 1, button: 1 },
P2_GP_C: { c: 84, n: "T", plyr: 1, button: 2 },
P2_GP_D: { c: 89, n: "Y", plyr: 1, button: 3 },
P2_SELECT: { c: 70, n: "F", plyr: 1, button: 8 },
P2_START: { c: 71, n: "G", plyr: 1, button: 9 },
// keyboard only
VK_ESCAPE: { c: 27, n: "Esc" },
VK_F1: { c: 112, n: "F1" },
VK_F2: { c: 113, n: "F2" },
VK_F3: { c: 114, n: "F3" },
VK_F4: { c: 115, n: "F4" },
VK_F5: { c: 116, n: "F5" },
VK_F6: { c: 117, n: "F6" },
VK_F7: { c: 118, n: "F7" },
VK_F8: { c: 119, n: "F8" },
VK_F9: { c: 120, n: "F9" },
VK_F10: { c: 121, n: "F10" },
VK_F11: { c: 122, n: "F11" },
VK_F12: { c: 123, n: "F12" },
VK_SCROLL_LOCK: { c: 145, n: "ScrLck" },
VK_PAUSE: { c: 19, n: "Pause" },
VK_QUOTE: { c: 192, n: "'" },
VK_1: { c: 49, n: "1" },
VK_2: { c: 50, n: "2" },
VK_3: { c: 51, n: "3" },
VK_4: { c: 52, n: "4" },
VK_5: { c: 53, n: "5" },
VK_6: { c: 54, n: "6" },
VK_7: { c: 55, n: "7" },
VK_8: { c: 56, n: "8" },
VK_9: { c: 57, n: "9" },
VK_0: { c: 48, n: "0" },
VK_MINUS: { c: 189, n: "-" },
VK_MINUS2: { c: 173, n: "-" },
VK_EQUALS: { c: 187, n: "=" },
VK_EQUALS2: { c: 61, n: "=" },
VK_BACK_SPACE: { c: 8, n: "Bkspc" },
VK_TAB: { c: 9, n: "Tab" },
VK_Q: { c: 81, n: "Q" },
VK_W: { c: 87, n: "W" },
VK_E: { c: 69, n: "E" },
VK_R: { c: 82, n: "R" },
VK_T: { c: 84, n: "T" },
VK_Y: { c: 89, n: "Y" },
VK_U: { c: 85, n: "U" },
VK_I: { c: 73, n: "I" },
VK_O: { c: 79, n: "O" },
VK_P: { c: 80, n: "P" },
VK_ACUTE: { c: 219, n: "´" },
VK_OPEN_BRACKET: { c: 221, n: "[" },
VK_CLOSE_BRACKET: { c: 220, n: "]" },
VK_CAPS_LOCK: { c: 20, n: "CpsLck" },
VK_A: { c: 65, n: "A" },
VK_S: { c: 83, n: "S" },
VK_D: { c: 68, n: "D" },
VK_F: { c: 70, n: "F" },
VK_G: { c: 71, n: "G" },
VK_H: { c: 72, n: "H" },
VK_J: { c: 74, n: "J" },
VK_K: { c: 75, n: "K" },
VK_L: { c: 76, n: "L" },
VK_CEDILLA: { c: 186, n: "Ç" },
VK_TILDE: { c: 222, n: "~" },
VK_ENTER: { c: 13, n: "Enter" },
VK_SHIFT: { c: 16, n: "Shift" },
VK_BACK_SLASH: { c: 226, n: "\\" },
VK_Z: { c: 90, n: "Z" },
VK_X: { c: 88, n: "X" },
VK_C: { c: 67, n: "C" },
VK_V: { c: 86, n: "V" },
VK_B: { c: 66, n: "B" },
VK_N: { c: 78, n: "N" },
VK_M: { c: 77, n: "M" },
VK_COMMA: { c: 188, n: "] =" },
VK_PERIOD: { c: 190, n: "." },
VK_SEMICOLON: { c: 191, n: ";" },
VK_SLASH: { c: 193, n: "/" },
VK_CONTROL: { c: 17, n: "Ctrl" },
VK_ALT: { c: 18, n: "Alt" },
VK_SPACE: { c: 32, n: "Space" },
VK_INSERT: { c: 45, n: "Ins" },
VK_DELETE: { c: 46, n: "Del" },
VK_HOME: { c: 36, n: "Home" },
VK_END: { c: 35, n: "End" },
VK_PAGE_UP: { c: 33, n: "PgUp" },
VK_PAGE_DOWN: { c: 34, n: "PgDown" },
VK_UP: { c: 38, n: "Up" },
VK_DOWN: { c: 40, n: "Down" },
VK_LEFT: { c: 37, n: "Left" },
VK_RIGHT: { c: 39, n: "Right" },
VK_NUM_LOCK: { c: 144, n: "Num" },
VK_DIVIDE: { c: 111, n: "Num /" },
VK_MULTIPLY: { c: 106, n: "Num *" },
VK_SUBTRACT: { c: 109, n: "Num -" },
VK_ADD: { c: 107, n: "Num +" },
VK_DECIMAL: { c: 194, n: "Num ." },
VK_NUMPAD0: { c: 96, n: "Num 0" },
VK_NUMPAD1: { c: 97, n: "Num 1" },
VK_NUMPAD2: { c: 98, n: "Num 2" },
VK_NUMPAD3: { c: 99, n: "Num 3" },
VK_NUMPAD4: { c: 100, n: "Num 4" },
VK_NUMPAD5: { c: 101, n: "Num 5" },
VK_NUMPAD6: { c: 102, n: "Num 6" },
VK_NUMPAD7: { c: 103, n: "Num 7" },
VK_NUMPAD8: { c: 104, n: "Num 8" },
VK_NUMPAD9: { c: 105, n: "Num 9" },
VK_NUMPAD_CENTER: { c: 12, n: "Num Cntr" }
};
function _metakeyflags(e) {
return (e.shiftKey ? KeyFlags.Shift : 0) |
(e.ctrlKey ? KeyFlags.Ctrl : 0) |
(e.altKey ? KeyFlags.Alt : 0) |
(e.metaKey ? KeyFlags.Meta : 0);
}
function newKeyboardHandler(switches, map, func, alwaysfunc) {
return (key, code, flags) => {
if (!map) {
func(null, key, code, flags);
return;
}
var o = map[key];
if (!o)
o = map[0];
if (func && (o || alwaysfunc)) {
func(o, key, code, flags);
}
if (o) {
//console.log(key,code,flags,o);
var mask = o.mask;
if (mask < 0) { // negative mask == active low
mask = -mask;
if (flags & (KeyFlags.KeyDown | KeyFlags.KeyUp))
flags ^= KeyFlags.KeyDown | KeyFlags.KeyUp;
}
if (flags & KeyFlags.KeyDown) {
switches[o.index] |= mask;
}
else if (flags & KeyFlags.KeyUp) {
switches[o.index] &= ~mask;
}
}
};
}
exports.newKeyboardHandler = newKeyboardHandler;
function setKeyboardFromMap(video, switches, map, func, alwaysfunc) {
var handler = newKeyboardHandler(switches, map, func, alwaysfunc);
video.setKeyboardEvents(handler);
return new ControllerPoller(handler);
}
exports.setKeyboardFromMap = setKeyboardFromMap;
function makeKeycodeMap(table) {
var map = new Map();
for (var i = 0; i < table.length; i++) {
var entry = table[i];
var val = { index: entry[1], mask: entry[2], def: entry[0] };
map[entry[0].c] = val;
}
return map;
}
exports.makeKeycodeMap = makeKeycodeMap;
const DEFAULT_CONTROLLER_KEYS = [
exports.Keys.UP, exports.Keys.DOWN, exports.Keys.LEFT, exports.Keys.RIGHT, exports.Keys.A, exports.Keys.B, exports.Keys.SELECT, exports.Keys.START,
exports.Keys.P2_UP, exports.Keys.P2_DOWN, exports.Keys.P2_LEFT, exports.Keys.P2_RIGHT, exports.Keys.P2_A, exports.Keys.P2_B, exports.Keys.P2_SELECT, exports.Keys.P2_START,
];
class ControllerPoller {
constructor(handler) {
this.active = false;
this.state = new Int8Array(32);
this.lastState = new Int8Array(32);
this.AXIS0 = 24; // first joystick axis index
this.handler = handler;
window.addEventListener("gamepadconnected", (event) => {
console.log("Gamepad connected:", event);
this.active = typeof navigator.getGamepads === 'function';
});
window.addEventListener("gamepaddisconnected", (event) => {
console.log("Gamepad disconnected:", event);
});
}
poll() {
if (!this.active)
return;
var gamepads = navigator.getGamepads();
for (var gpi = 0; gpi < gamepads.length; gpi++) {
var gp = gamepads[gpi];
if (gp) {
for (var i = 0; i < gp.axes.length; i++) {
var k = i + this.AXIS0;
this.state[k] = Math.round(gp.axes[i]);
if (this.state[k] != this.lastState[k]) {
this.handleStateChange(gpi, k);
}
}
for (var i = 0; i < gp.buttons.length; i++) {
this.state[i] = gp.buttons[i].pressed ? 1 : 0;
if (this.state[i] != this.lastState[i]) {
this.handleStateChange(gpi, i);
}
}
this.lastState.set(this.state);
}
}
}
handleStateChange(gpi, k) {
var axis = k - this.AXIS0;
// TODO: this is slow
for (var def of DEFAULT_CONTROLLER_KEYS) {
// is this a gamepad entry? same player #?
if (def && def.plyr == gpi) {
var code = def.c;
var state = this.state[k];
var lastState = this.lastState[k];
// check for button/axis match
if (k == def.button || (axis == 0 && def.xaxis == state) || (axis == 1 && def.yaxis == state)) {
//console.log(gpi,k,state,entry);
if (state != 0) {
this.handler(code, 0, KeyFlags.KeyDown);
}
else {
this.handler(code, 0, KeyFlags.KeyUp);
}
break;
}
// joystick released?
else if (state == 0 && (axis == 0 && def.xaxis == lastState) || (axis == 1 && def.yaxis == lastState)) {
this.handler(code, 0, KeyFlags.KeyUp);
break;
}
}
}
}
}
exports.ControllerPoller = ControllerPoller;
function padBytes(data, len, padstart) {
if (data.length > len) {
throw Error("Data too long, " + data.length + " > " + len);
}
var r = new RAM(len);
if (padstart)
r.mem.set(data, len - data.length);
else
r.mem.set(data);
return r.mem;
}
exports.padBytes = padBytes;
// TODO: better performance, check values
function AddressDecoder(table, options) {
var self = this;
function makeFunction() {
var s = "";
if (options && options.gmask) {
s += "a&=" + options.gmask + ";";
}
for (var i = 0; i < table.length; i++) {
var entry = table[i];
var start = entry[0] | 0;
var end = entry[1] | 0;
var mask = entry[2] | 0;
var func = entry[3];
self['__fn' + i] = func;
s += "if (a>=" + start + " && a<=" + end + "){";
if (mask)
s += "a&=" + mask + ";";
s += "return this.__fn" + i + "(a,v)&0xff;}\n";
}
s += "return 0;"; // TODO: noise()?
return new Function('a', 'v', s);
}
return makeFunction().bind(self);
}
exports.AddressDecoder = AddressDecoder;
function newAddressDecoder(table, options) {
return new AddressDecoder(table, options);
}
exports.newAddressDecoder = newAddressDecoder;
// https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width, // relationship bitmap vs. element for X
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
return {
x: (evt.clientX - rect.left) * scaleX,
y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
};
}
exports.getMousePos = getMousePos;
///
// TODO: https://stackoverflow.com/questions/10463518/converting-em-to-px-in-javascript-and-getting-default-font-size
function getVisibleEditorLineHeight() {
return $("#booksMenuButton").first().height();
}
exports.getVisibleEditorLineHeight = getVisibleEditorLineHeight;
class VirtualTextScroller {
constructor(parent) {
var div = document.createElement('div');
div.setAttribute("class", "memdump");
parent.appendChild(div);
this.maindiv = div;
}
create(workspace, maxRowCount, fn) {
this.getLineAt = fn;
this.memorylist = new vlist_1.VirtualList({
w: $(workspace).width(),
h: $(workspace).height(),
itemHeight: getVisibleEditorLineHeight(),
totalRows: maxRowCount,
generatorFn: (row) => {
var line = fn(row);
var linediv = document.createElement("div");
linediv.appendChild(document.createTextNode(line.text));
if (line.clas != null)
linediv.className = line.clas;
return linediv;
}
});
$(this.maindiv).append(this.memorylist.container);
}
// TODO: refactor with elsewhere
refresh() {
if (this.memorylist) {
$(this.maindiv).find('[data-index]').each((i, e) => {
var div = e;
var row = parseInt(div.getAttribute('data-index'));
var oldtext = div.innerText;
var line = this.getLineAt(row);
var newtext = line.text;
if (oldtext != newtext) {
div.innerText = newtext;
if (line.clas != null && !div.classList.contains(line.clas)) {
var oldclasses = Array.from(div.classList);
oldclasses.forEach((c) => div.classList.remove(c));
div.classList.add('vrow');
div.classList.add(line.clas);
}
}
});
}
}
}
exports.VirtualTextScroller = VirtualTextScroller;
//# sourceMappingURL=emu.js.map

1
gen/common/emu.js.map Normal file

File diff suppressed because one or more lines are too long

48
gen/common/hdl/fuzz.js Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fuzz = void 0;
//import binaryen = require('binaryen');
//import { fn } from "jquery";
const hdlruntime_1 = require("./hdlruntime");
const hdlwasm_1 = require("./hdlwasm");
const vxmlparser_1 = require("./vxmlparser");
function fuzz(buf) {
var parser = new vxmlparser_1.VerilogXMLParser();
var str = buf.toString();
try {
parser.parse(str);
}
catch (e) {
if (e instanceof vxmlparser_1.CompileError)
return;
throw e;
}
if (1) {
var wmod = new hdlwasm_1.HDLModuleWASM(parser.modules['TOP'], parser.modules['@CONST-POOL@']);
wmod.traceBufferSize = 0x8000;
wmod.maxMemoryMB = 0.25;
wmod.initSync();
wmod.powercycle();
wmod.tick2(10000);
}
if (0) {
var jmod = new hdlruntime_1.HDLModuleJS(parser.modules['TOP'], parser.modules['@CONST-POOL@']);
jmod.init();
try {
jmod.powercycle();
jmod.tick2(10000);
}
catch (e) {
if (e instanceof hdlruntime_1.HDLError)
return;
const fs = require('fs');
fs.writeFileSync('hdlfuzz-output.js', jmod.getJSCode());
throw e;
}
finally {
jmod.dispose();
}
}
}
exports.fuzz = fuzz;
//# sourceMappingURL=fuzz.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fuzz.js","sourceRoot":"","sources":["../../../src/common/hdl/fuzz.ts"],"names":[],"mappings":";;;AACA,wCAAwC;AACxC,8BAA8B;AAC9B,6CAAqD;AACrD,uCAA0C;AAC1C,6CAA8D;AAE9D,SAAgB,IAAI,CAAC,GAAG;IACpB,IAAI,MAAM,GAAG,IAAI,6BAAgB,EAAE,CAAC;IACpC,IAAI,GAAG,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACzB,IAAI;QACA,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;KACrB;IAAC,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,YAAY,yBAAY;YAAE,OAAO;QACtC,MAAM,CAAC,CAAC;KACX;IACD,IAAI,CAAC,EAAE;QACH,IAAI,IAAI,GAAG,IAAI,uBAAa,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;QACpF,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;KACrB;IACD,IAAI,CAAC,EAAE;QACH,IAAI,IAAI,GAAG,IAAI,wBAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI;YACA,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SACrB;QAAC,OAAO,CAAC,EAAE;YACR,IAAI,CAAC,YAAY,qBAAQ;gBAAE,OAAO;YAClC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,EAAE,CAAC,aAAa,CAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YACxD,MAAM,CAAC,CAAC;SACX;gBAAS;YACN,IAAI,CAAC,OAAO,EAAE,CAAC;SAClB;KACJ;AACL,CAAC;AAhCD,oBAgCC"}

View File

@@ -0,0 +1,645 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HDLModuleJS = exports.HDLError = void 0;
const emu_1 = require("../emu");
const util_1 = require("../util");
const hdltypes_1 = require("./hdltypes");
class HDLError extends emu_1.EmuHalt {
constructor(obj, msg) {
super(msg, obj ? obj.$loc : null);
Object.setPrototypeOf(this, HDLError.prototype);
this.obj = obj;
if (obj)
console.log(obj);
}
}
exports.HDLError = HDLError;
class HDLModuleJS {
constructor(mod, constpool) {
this.finished = false;
this.stopped = false;
this.settleTime = 0;
this.specfuncs = [];
this.getFileData = null;
this.mod = mod;
this.constpool = constpool;
this.basefuncs = {};
this.state = {}; //new Object(this.funcs) as any;
this.globals = {};
// set built-in functions
Object.getOwnPropertyNames(Object.getPrototypeOf(this)).filter((f) => f.startsWith('$')).forEach((f) => {
this.basefuncs[f] = this[f].bind(this);
});
// set initial state
if (this.constpool) {
var cp = new HDLModuleJS(this.constpool, null);
cp.init();
Object.assign(this.state, cp.state);
Object.assign(this.globals, cp.globals);
}
for (var varname in this.mod.vardefs) {
var vardef = this.mod.vardefs[varname];
this.globals[varname] = vardef;
this.state[varname] = this.defaultValue(vardef.dtype, vardef);
}
// generate functions
this.basefuncs = this.genFuncs({});
this.curfuncs = this.basefuncs;
/*
for (var i=0; i<16; i++) {
this.specfuncs[i] = this.genFuncs({
//reset:(i&1),
//clk:(i&2),
//CPU16$state:i
test_CPU16_top$cpu$state:i
});
}
*/
}
init() {
}
dispose() {
}
genFuncs(constants) {
var funcs = Object.create(this.basefuncs);
this.curconsts = constants;
for (var block of this.mod.blocks) {
this.locals = {};
// if we have at least 1 constant value, check for it (set counter to zero)
this.constused = (Object.keys(this.curconsts).length == 0) ? 99999 : 0;
var s = this.block2js(block);
if (this.constused) {
try {
var funcname = block.name || '__anon';
var funcbody = `'use strict'; function ${funcname}(o) { ${s} }; return ${funcname};`;
var func = new Function('', funcbody)();
funcs[block.name] = func;
//console.log(funcbody);
}
catch (e) {
console.log(funcbody);
throw e;
}
}
//if (this.constused) console.log('FUNC',constants,funcname,this.constused);
}
return funcs;
}
getJSCode() {
var s = '';
for (var funcname in this.basefuncs) {
if (funcname && funcname.startsWith("_")) {
s += this.basefuncs[funcname].toString();
s += "\n";
}
}
return s;
}
powercycle() {
this.resetStartTimeMsec = new Date().getTime() - 1;
this.finished = false;
this.stopped = false;
this.basefuncs._ctor_var_reset(this.state);
this.basefuncs._eval_initial(this.state);
for (var i = 0; i < 100; i++) {
this.basefuncs._eval_settle(this.state);
this.basefuncs._eval(this.state);
var Vchange = this.basefuncs._change_request(this.state);
if (!Vchange) {
this.settleTime = i;
return;
}
}
throw new HDLError(null, `model did not converge on reset()`);
}
eval() {
var clk = this.state.clk;
var reset = this.state.reset;
var state = this.state.test_CPU16_top$cpu$state;
var opcode = this.state.CPU$opcode;
var aluop = this.state.CPU$aluop;
var fi = state;
//this.curfuncs = this.specfuncs[fi & 0xff];
this.curfuncs = this.basefuncs;
for (var i = 0; i < 100; i++) {
this.curfuncs._eval(this.state);
var Vchange = this.curfuncs._change_request(this.state);
/*
--- don't do it this way! it's like 4x slower...
this.call('_eval');
var Vchange = this.call('_change_request');
*/
if (!Vchange) {
this.settleTime = i;
return;
}
}
throw new HDLError(null, `model did not converge on eval()`);
}
tick2(iters) {
while (iters-- > 0) {
this.state.clk = 0;
this.eval();
this.state.clk = 1;
this.eval();
}
}
defaultValue(dt, vardef) {
if ((0, hdltypes_1.isLogicType)(dt)) {
if (dt.left <= 31)
return 0;
else
return BigInt(0);
}
else if ((0, hdltypes_1.isArrayType)(dt) && typeof dt.high.cvalue === 'number' && typeof dt.low.cvalue === 'number') {
let arr;
let arrlen = dt.high.cvalue - dt.low.cvalue + 1;
if (arrlen < 0)
arrlen = -arrlen; // TODO?
if ((0, hdltypes_1.isLogicType)(dt.subtype)) {
if (dt.subtype.left <= 7)
arr = new Uint8Array(arrlen);
else if (dt.subtype.left <= 15)
arr = new Uint16Array(arrlen);
else if (dt.subtype.left <= 31)
arr = new Uint32Array(arrlen);
else {
arr = []; // TODO?
}
}
else {
arr = [];
for (let i = 0; i < arrlen; i++) {
arr[i] = this.defaultValue(dt.subtype);
}
}
if (vardef != null && vardef.initValue != null) {
for (let i = 0; i < vardef.initValue.exprs.length; i++) {
let e = vardef.initValue.exprs[i];
if ((0, hdltypes_1.isArrayItem)(e) && (0, hdltypes_1.isConstExpr)(e.expr)) {
arr[e.index] = e.expr.cvalue;
}
else {
throw new HDLError(dt, `non-const expr in initarray`);
}
}
}
return arr;
}
throw new HDLError(dt, `no default value for var type: ${vardef.name}`);
}
constValue(expr) {
if ((0, hdltypes_1.isConstExpr)(expr)) {
return expr.cvalue;
}
else {
throw new HDLError(expr, `no const value for expr`);
}
}
block2js(block) {
return this.expr2js(block);
}
expr2js(e, options) {
if (e == null) {
return "/*null*/"; // TODO
}
if ((0, hdltypes_1.isVarRef)(e)) {
if (this.curconsts[e.refname] != null && !(options || {}).store) {
this.constused++;
return `${this.curconsts[e.refname]}`;
}
else if (this.locals[e.refname]) {
return `${e.refname}`;
}
else if (this.globals[e.refname]) {
return `o.${e.refname}`;
}
else
throw new HDLError(e, `cannot find variable '${e.refname}'`);
}
else if ((0, hdltypes_1.isVarDecl)(e)) {
this.locals[e.name] = e;
let s = `var ${e.name}`;
if (e.constValue != null) {
s += ` = ${this.constValue(e)}`; // TODO?
}
else if (e.initValue != null) {
// TODO?
throw new HDLError(e, `can't init array here`);
}
else if ((0, hdltypes_1.isLogicType)(e.dtype) && e.dtype.left > 31) {
// TODO: hack for big ints ($readmem)
s += ` = []`;
}
return s;
}
else if ((0, hdltypes_1.isConstExpr)(e)) {
return `0x${e.cvalue.toString(16)}`;
}
else if ((0, hdltypes_1.isBigConstExpr)(e)) {
return e.bigvalue.toString(); // TODO?
}
else if ((0, hdltypes_1.isTriop)(e)) {
switch (e.op) {
case 'if':
if (e.right == null || ((0, hdltypes_1.isBlock)(e.right) && e.right.exprs.length == 0))
return `if (${this.expr2js(e.cond, { cond: true })}) { ${this.expr2js(e.left)} }`;
else
return `if (${this.expr2js(e.cond, { cond: true })}) { ${this.expr2js(e.left)} } else { ${this.expr2js(e.right)} }`;
case 'cond':
case 'condbound':
return `(${this.expr2js(e.cond, { cond: true })} ? ${this.expr2js(e.left)} : ${this.expr2js(e.right)})`;
default:
throw new HDLError(e, `unknown triop ${e.op}`);
}
}
else if ((0, hdltypes_1.isBinop)(e)) {
switch (e.op) {
case 'contassign':
case 'assign':
case 'assignpre':
case 'assigndly':
case 'assignpost':
return `${this.expr2js(e.right, { store: true })} = ${this.expr2js(e.left)}`;
case 'arraysel':
case 'wordsel':
return `${this.expr2js(e.left)}[${this.expr2js(e.right)}]`;
case 'changedet':
// __req |= ((vlTOPp->control_test_top__02Ehsync ^ vlTOPp->__Vchglast__TOP__control_test_top__02Ehsync)
// vlTOPp->__Vchglast__TOP__control_test_top__02Ehsync = vlTOPp->control_test_top__02Ehsync;
return `$$req |= (${this.expr2js(e.left)} ^ ${this.expr2js(e.right)}); ${this.expr2js(e.right)} = ${this.expr2js(e.left)}`;
default:
var jsop = OP2JS[e.op];
if (!jsop) {
throw new HDLError(e, `unknown binop ${e.op}`);
}
if (jsop.startsWith('?')) {
jsop = jsop.substr(1);
if (!options || !options.cond) {
return `((${this.expr2js(e.left)} ${jsop} ${this.expr2js(e.right)})?1:0)`;
}
}
return `(${this.expr2js(e.left)} ${jsop} ${this.expr2js(e.right)})`;
}
}
else if ((0, hdltypes_1.isUnop)(e)) {
switch (e.op) {
case 'ccast': // TODO: cast ints, cast bools?
return this.expr2js(e.left);
case 'creturn':
return `return ${this.expr2js(e.left)}`;
case 'creset':
return this.expr2reset(e.left);
case 'not':
return `(~${this.expr2js(e.left)})`;
//return `(${this.expr2js(e.left)}?0:1)`;
case 'negate':
return `(-${this.expr2js(e.left)})`;
case 'extends':
let shift = 32 - e.widthminv;
return `((${this.expr2js(e.left)} << ${shift}) >> ${shift})`;
case 'redxor':
return `this.$$${e.op}(${this.expr2js(e.left)})`;
default:
throw new HDLError(e, `unknown unop ${e.op}`);
}
}
else if ((0, hdltypes_1.isBlock)(e)) {
// TODO: { e } ?
var body = e.exprs.map((x) => this.expr2js(x)).join(';\n');
if (e.name) {
if (e.name.startsWith('_change_request')) {
return `var $$req = 0;\n${body}\n;return $$req;`;
}
else if (e.blocktype == 'sformatf') {
var args = e.exprs.map((x) => this.expr2js(x));
args = [JSON.stringify(e.name)].concat(args);
return args.join(', ');
}
}
return body;
}
else if ((0, hdltypes_1.isWhileop)(e)) {
return `for (${this.expr2js(e.precond)}; ${this.expr2js(e.loopcond)}; ${this.expr2js(e.inc)}) { ${this.expr2js(e.body)} }`;
}
else if ((0, hdltypes_1.isFuncCall)(e)) {
if ((e.funcname == '$stop' || e.funcname == '$finish') && e.$loc) {
return `this.${e.funcname}(o, ${JSON.stringify(e.$loc)})`;
}
else if (e.args == null || e.args.length == 0) {
return `this.${e.funcname}(o)`;
}
else {
return `this.${e.funcname}(o, ${e.args.map(arg => this.expr2js(arg)).join(', ')})`;
}
}
console.log(e);
throw new Error(`unrecognized expr: ${JSON.stringify(e)}`);
}
expr2reset(e) {
if ((0, hdltypes_1.isVarRef)(e)) {
if (this.curconsts[e.refname] != null) {
return `${e.refname}`;
}
else if ((0, hdltypes_1.isLogicType)(e.dtype)) {
if (e.dtype.left <= 31)
return `${this.expr2js(e)} = 0`;
else
return `${this.expr2js(e)} = BigInt(0)`;
}
else if ((0, hdltypes_1.isArrayType)(e.dtype)) {
if ((0, hdltypes_1.isLogicType)(e.dtype.subtype)) {
return `${this.expr2js(e)}.fill(0)`;
}
else if ((0, hdltypes_1.isArrayType)(e.dtype.subtype) && (0, hdltypes_1.isLogicType)(e.dtype.subtype.subtype)) {
return `${this.expr2js(e)}.forEach((a) => a.fill(0))`;
}
else {
// TODO: 3d arrays?
throw new HDLError(e, `unsupported data type for reset: ${JSON.stringify(e.dtype)}`);
}
}
}
else {
throw new HDLError(e, `can only reset var refs`);
}
}
// runtime methods
// TODO: $time, $display, etc
$finish(o, loc) {
if (!this.finished) {
console.log("Simulation $finish", loc);
this.finished = true;
}
}
$stop(o, loc) {
if (!this.stopped) {
console.log("Simulation $stop", loc);
this.stopped = true;
}
}
$rand(o) {
return Math.random() | 0;
}
$display(o, fmt, ...args) {
// TODO: replace args, etc
console.log(fmt, args);
}
// TODO: implement arguments, XML
$readmem(o, filename, memp, lsbp, msbp, ishex) {
// parse filename from 32-bit values into characters
var barr = [];
for (var i = 0; i < filename.length; i++) {
barr.push((filename[i] >> 0) & 0xff);
barr.push((filename[i] >> 8) & 0xff);
barr.push((filename[i] >> 16) & 0xff);
barr.push((filename[i] >> 24) & 0xff);
}
barr = barr.filter(x => x != 0); // ignore zeros
barr.reverse(); // reverse it
var strfn = (0, util_1.byteArrayToString)(barr); // convert to string
// parse hex/binary file
var strdata = this.getFileData(strfn);
if (strdata == null)
throw new HDLError(null, "Could not $readmem '" + strfn + "'");
var data = strdata.split('\n').filter(s => s !== '').map(s => parseInt(s, ishex ? 16 : 2));
console.log('$readmem', ishex, strfn, data.length);
// copy into destination array
if (memp === null)
throw new HDLError(null, "No destination array to $readmem " + strfn);
if (memp.length < data.length)
throw new HDLError(null, "Destination array too small to $readmem " + strfn);
for (i = 0; i < data.length; i++)
memp[i] = data[i];
}
$time(o) {
return (new Date().getTime() - this.resetStartTimeMsec); // TODO: timescale
}
$$redxor(r) {
r = (r ^ (r >> 1));
r = (r ^ (r >> 2));
r = (r ^ (r >> 4));
r = (r ^ (r >> 8));
r = (r ^ (r >> 16));
return r;
}
//
isStopped() { return this.stopped; }
isFinished() { return this.finished; }
tick() {
this.state.clk ^= 1;
this.eval();
}
get(varname) {
return this.state[varname];
}
set(varname, value) {
if (varname in this.state) {
this.state[varname] = value;
}
}
saveState() {
return (0, util_1.safe_extend)(true, {}, this.state);
}
getGlobals() {
return this.saveState();
}
loadState(state) {
(0, util_1.safe_extend)(true, this.state, state);
}
}
exports.HDLModuleJS = HDLModuleJS;
const OP2JS = {
'eq': '?===',
'neq': '?!==',
'gt': '?>',
'lt': '?<',
'gte': '?>=',
'lte': '?<=',
'and': '&',
'or': '|',
'xor': '^',
'add': '+',
'sub': '-',
'shiftr': '>>>',
'shiftl': '<<',
// TODO: correct?
'mul': '*',
'moddiv': '%',
'div': '/',
// TODO: signed versions? functions?
'muls': '*',
'moddivs': '%',
'divs': '/',
'gts': '?>',
'gtes': '?>=',
'lts': '?<',
'ltes': '?<=',
};
/**
// SIMULATOR STUFF (should be global)
export var vl_finished = false;
export var vl_stopped = false;
export function VL_UL(x) { return x|0; }
//export function VL_ULL(x) { return x|0; }
export function VL_TIME_Q() { return (new Date().getTime())|0; }
/// Return true if data[bit] set
export function VL_BITISSET_I(data,bit) { return (data & (VL_UL(1)<<VL_UL(bit))); }
export function VL_EXTENDSIGN_I(lbits, lhs) { return (-((lhs)&(VL_UL(1)<<(lbits-1)))); }
export function VL_EXTEND_II(obits,lbits,lhs) { return lhs; }
export function VL_EXTENDS_II(x,lbits,lhs) {
return VL_EXTENDSIGN_I(lbits,lhs) | lhs;
}
export function VL_NEGATE_I(x) { return -x; }
export function VL_LTS_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) < VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
export function VL_GTS_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) > VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
export function VL_LTES_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) <= VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
export function VL_GTES_III(x,lbits,y,lhs,rhs) {
return (VL_EXTENDS_II(x,lbits,lhs) >= VL_EXTENDS_II(x,lbits,rhs)) ? 1 : 0; }
export function VL_DIV_III(lbits,lhs,rhs) {
return (((rhs)==0)?0:(lhs)/(rhs)); }
export function VL_MULS_III(lbits,lhs,rhs) {
return (((rhs)==0)?0:(lhs)*(rhs)); }
export function VL_MODDIV_III(lbits,lhs,rhs) {
return (((rhs)==0)?0:(lhs)%(rhs)); }
export function VL_DIVS_III(lbits,lhs,rhs) {
var lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
var rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
return (((rhs_signed)==0)?0:(lhs_signed)/(rhs_signed));
}
export function VL_MODDIVS_III(lbits,lhs,rhs) {
var lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
var rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
return (((rhs_signed)==0)?0:(lhs_signed)%(rhs_signed));
}
export function VL_REDXOR_32(r) {
r=(r^(r>>1)); r=(r^(r>>2)); r=(r^(r>>4)); r=(r^(r>>8)); r=(r^(r>>16));
return r;
}
export var VL_WRITEF = console.log; // TODO: $write
export function vl_finish(filename,lineno,hier) {
if (!vl_finished) console.log("Finished at " + filename + ":" + lineno, hier);
vl_finished = true;
}
export function vl_stop(filename,lineno,hier) {
if (!vl_stopped) console.log("Stopped at " + filename + ":" + lineno, hier);
vl_stopped = true;
}
export function VL_RAND_RESET_I(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
export function VL_RANDOM_I(bits) { return 0 | Math.floor(Math.random() * (1<<bits)); }
export function VL_READMEM_Q(ishex,width,depth,array_lsb,fnwords,filename,memp,start,end) {
VL_READMEM_W(ishex,width,depth,array_lsb,fnwords,filename,memp,start,end);
}
export function VL_READMEM_W(ishex,width,depth,array_lsb,fnwords,filename,memp,start,end) {
}
// SIMULATOR BASE
abstract class VerilatorBase {
totalTicks = 0;
maxVclockLoop = 0;
clk = 0;
reset = 0;
vl_fatal(msg:string) {
console.log(msg);
}
ticks() : number { return this.totalTicks; }
setTicks(T:number) { this.totalTicks = T|0; }
__reset() {
if (this.reset !== undefined) {
this.totalTicks = 0;
this.reset = 0;
this.tick2();
this.reset = 1;
}
}
tick2() {
this.clk = 0;
this.eval();
this.clk = 1;
this.eval();
}
abstract _eval(vlSymsp);
abstract __Vm_didInit : boolean;
abstract __Vm_activity : boolean;
abstract _change_request(vlSymsp);
abstract _eval_initial(vlSymsp);
abstract _eval_settle(vlSymsp);
eval() {
let vlSymsp = this; //{TOPp:this};
// Initialize
if (!vlSymsp.__Vm_didInit)
this._eval_initial_loop(vlSymsp);
// Evaluate till stable
//VL_DEBUG_IF(VL_PRINTF("\n----TOP Evaluate Vmain::eval\n"); );
var __VclockLoop = 0;
var __Vchange=1;
while (__Vchange) {
//VL_DEBUG_IF(VL_PRINTF(" Clock loop\n"););
vlSymsp.__Vm_activity = true;
this._eval(vlSymsp);
__Vchange = this._change_request(vlSymsp);
if (++__VclockLoop > 100) { this.vl_fatal("Verilated model didn't converge"); }
}
if (__VclockLoop > this.maxVclockLoop) {
this.maxVclockLoop = __VclockLoop;
if (this.maxVclockLoop > 1) {
console.log("Graph took " + this.maxVclockLoop + " iterations to stabilize");
$("#verilog_bar").show();
$("#settle_label").text(this.maxVclockLoop+"");
}
}
this.totalTicks++;
}
_eval_initial_loop(vlSymsp) {
vlSymsp.TOPp = this;
vlSymsp.__Vm_didInit = true;
this._eval_initial(vlSymsp);
vlSymsp.__Vm_activity = true;
var __VclockLoop = 0;
var __Vchange=1;
while (__Vchange) {
this._eval_settle(vlSymsp);
this._eval(vlSymsp);
__Vchange = this._change_request(vlSymsp);
if (++__VclockLoop > 100) { this.vl_fatal("Verilated model didn't DC converge"); }
}
}
}
*/
//# sourceMappingURL=hdlruntime.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isArrayItem = exports.isFuncCall = exports.isBlock = exports.isWhileop = exports.isTriop = exports.isBinop = exports.isUnop = exports.isVarRef = exports.isBigConstExpr = exports.isConstExpr = exports.isVarDecl = exports.hasDataType = exports.HDLFile = exports.isArrayType = exports.isLogicType = void 0;
function isLogicType(arg) {
return typeof arg.left === 'number' && typeof arg.right === 'number';
}
exports.isLogicType = isLogicType;
function isArrayType(arg) {
return arg.subtype != null && arg.low != null && arg.high != null
&& typeof arg.low.cvalue === 'number' && typeof arg.high.cvalue === 'number';
}
exports.isArrayType = isArrayType;
class HDLFile {
}
exports.HDLFile = HDLFile;
function hasDataType(arg) {
return typeof arg.dtype === 'object';
}
exports.hasDataType = hasDataType;
function isVarDecl(arg) {
return typeof arg.isParam !== 'undefined';
}
exports.isVarDecl = isVarDecl;
function isConstExpr(arg) {
return typeof arg.cvalue === 'number';
}
exports.isConstExpr = isConstExpr;
function isBigConstExpr(arg) {
return typeof arg.bigvalue === 'bigint';
}
exports.isBigConstExpr = isBigConstExpr;
function isVarRef(arg) {
return arg.refname != null;
}
exports.isVarRef = isVarRef;
function isUnop(arg) {
return arg.op != null && arg.left != null && arg.right == null;
}
exports.isUnop = isUnop;
function isBinop(arg) {
return arg.op != null && arg.left != null && arg.right != null && arg.cond == null;
}
exports.isBinop = isBinop;
function isTriop(arg) {
return arg.op != null && arg.cond != null;
}
exports.isTriop = isTriop;
function isWhileop(arg) {
return arg.op === 'while' && arg.loopcond != null;
}
exports.isWhileop = isWhileop;
function isBlock(arg) {
return arg.blocktype != null;
}
exports.isBlock = isBlock;
function isFuncCall(arg) {
return typeof arg.funcname === 'string';
}
exports.isFuncCall = isFuncCall;
function isArrayItem(arg) {
return typeof arg.index === 'number' && arg.expr != null;
}
exports.isArrayItem = isArrayItem;
//# sourceMappingURL=hdltypes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hdltypes.js","sourceRoot":"","sources":["../../../src/common/hdl/hdltypes.ts"],"names":[],"mappings":";;;AA2CA,SAAgB,WAAW,CAAC,GAAO;IAC/B,OAAO,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC;AACzE,CAAC;AAFD,kCAEC;AAED,SAAgB,WAAW,CAAC,GAAO;IAC/B,OAAO,GAAG,CAAC,OAAO,IAAI,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI;WAC5D,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;AACnF,CAAC;AAHD,kCAGC;AAED,MAAa,OAAO;CAInB;AAJD,0BAIC;AAeD,SAAgB,WAAW,CAAC,GAAQ;IAChC,OAAO,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC;AACzC,CAAC;AAFD,kCAEC;AAoBD,SAAgB,SAAS,CAAC,GAAO;IAC7B,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,WAAW,CAAC;AAC9C,CAAC;AAFD,8BAEC;AAOD,SAAgB,WAAW,CAAC,GAAO;IAC/B,OAAO,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC;AAC1C,CAAC;AAFD,kCAEC;AAED,SAAgB,cAAc,CAAC,GAAO;IAClC,OAAO,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAC5C,CAAC;AAFD,wCAEC;AAqBD,SAAgB,QAAQ,CAAC,GAAO;IAC5B,OAAO,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC;AAC/B,CAAC;AAFD,4BAEC;AAYD,SAAgB,MAAM,CAAC,GAAO;IAC1B,OAAO,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC;AACnE,CAAC;AAFD,wBAEC;AAMD,SAAgB,OAAO,CAAC,GAAO;IAC3B,OAAO,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AACvF,CAAC;AAFD,0BAEC;AAMD,SAAgB,OAAO,CAAC,GAAO;IAC3B,OAAO,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAC9C,CAAC;AAFD,0BAEC;AAUD,SAAgB,SAAS,CAAC,GAAO;IAC7B,OAAO,GAAG,CAAC,EAAE,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;AACtD,CAAC;AAFD,8BAEC;AAQD,SAAgB,OAAO,CAAC,GAAO;IAC3B,OAAO,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC;AACjC,CAAC;AAFD,0BAEC;AAqBD,SAAgB,UAAU,CAAC,GAAO;IAC9B,OAAO,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAC5C,CAAC;AAFD,gCAEC;AAOD,SAAgB,WAAW,CAAC,GAAO;IAC/B,OAAO,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAC7D,CAAC;AAFD,kCAEC"}

1166
gen/common/hdl/hdlwasm.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,624 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VerilogXMLParser = exports.CompileError = void 0;
const util_1 = require("../util");
const hdltypes_1 = require("./hdltypes");
/**
* Whaa?
*
* Each hierarchy takes (uint32[] -> uint32[])
* - convert to/from js object
* - JS or WASM
* - Fixed-size packets
* - state is another uint32[]
* Find optimal packing of bits
* Find clocks
* Find pivots (reset, state) concat them together
* Dependency cycles
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
*/
class CompileError extends Error {
constructor($loc, msg) {
super(msg);
this.$loc = $loc;
Object.setPrototypeOf(this, CompileError.prototype);
}
}
exports.CompileError = CompileError;
class VerilogXMLParser {
constructor() {
this.files = {};
this.dtypes = {};
this.modules = {};
this.hierarchies = {};
this.cur_deferred = [];
// TODO: other types?
this.dtypes['QData'] = { left: 63, right: 0, signed: false };
this.dtypes['IData'] = { left: 31, right: 0, signed: false };
this.dtypes['SData'] = { left: 15, right: 0, signed: false };
this.dtypes['CData'] = { left: 7, right: 0, signed: false };
this.dtypes['byte'] = { left: 7, right: 0, signed: true };
this.dtypes['shortint'] = { left: 15, right: 0, signed: true };
this.dtypes['int'] = { left: 31, right: 0, signed: true };
this.dtypes['integer'] = { left: 31, right: 0, signed: true };
this.dtypes['longint'] = { left: 63, right: 0, signed: true };
this.dtypes['time'] = { left: 63, right: 0, signed: false };
}
defer(fn) {
this.cur_deferred.unshift(fn);
}
defer2(fn) {
this.cur_deferred.push(fn);
}
run_deferred() {
this.cur_deferred.forEach((fn) => fn());
this.cur_deferred = [];
}
name2js(s) {
if (s == null)
throw new CompileError(this.cur_loc, `no name`);
return s.replace(/[^a-z0-9_]/gi, '$');
}
findChildren(node, type, required) {
var arr = node.children.filter((n) => n.type == type);
if (arr.length == 0 && required)
throw new CompileError(this.cur_loc, `no child of type ${type}`);
return arr;
}
parseSourceLocation(node) {
var loc = node.attrs['loc'];
if (loc) {
if (loc == this.cur_loc_str) {
return this.cur_loc; // cache last parsed $loc object
}
else {
var [fileid, line, col, end_line, end_col] = loc.split(',');
var $loc = {
hdlfile: this.files[fileid],
path: this.files[fileid].filename,
line: parseInt(line),
start: parseInt(col) - 1,
end_line: parseInt(end_line),
end: parseInt(end_col) - 1,
};
this.cur_loc = $loc;
this.cur_loc_str = loc;
return $loc;
}
}
else {
return null;
}
}
open_module(node) {
var module = {
$loc: this.parseSourceLocation(node),
name: node.attrs['name'],
origName: node.attrs['origName'],
blocks: [],
instances: [],
vardefs: {},
};
if (this.cur_module)
throw new CompileError(this.cur_loc, `nested modules not supported`);
this.cur_module = module;
return module;
}
deferDataType(node, def) {
var dtype_id = node.attrs['dtype_id'];
if (dtype_id != null) {
this.defer(() => {
def.dtype = this.dtypes[dtype_id];
if (!def.dtype) {
throw new CompileError(this.cur_loc, `Unknown data type ${dtype_id} for ${node.type}`);
}
});
}
}
parseConstValue(s) {
const re_const = /(\d+)'([s]?)h([0-9a-f]+)/i;
var m = re_const.exec(s);
if (m) {
var numstr = m[3];
if (numstr.length <= 8)
return parseInt(numstr, 16);
else
return BigInt('0x' + numstr);
}
else {
throw new CompileError(this.cur_loc, `could not parse constant "${s}"`);
}
}
resolveVar(s, mod) {
var def = mod.vardefs[s];
if (def == null)
throw new CompileError(this.cur_loc, `could not resolve variable "${s}"`);
return def;
}
resolveModule(s) {
var mod = this.modules[s];
if (mod == null)
throw new CompileError(this.cur_loc, `could not resolve module "${s}"`);
return mod;
}
//
visit_verilator_xml(node) {
}
visit_package(node) {
}
visit_module(node) {
this.findChildren(node, 'var', false).forEach((n) => {
if ((0, hdltypes_1.isVarDecl)(n.obj)) {
this.cur_module.vardefs[n.obj.name] = n.obj;
}
});
this.modules[this.cur_module.name] = this.cur_module;
this.cur_module = null;
}
visit_var(node) {
var name = node.attrs['name'];
name = this.name2js(name);
var vardef = {
$loc: this.parseSourceLocation(node),
name: name,
origName: node.attrs['origName'],
isInput: node.attrs['dir'] == 'input',
isOutput: node.attrs['dir'] == 'output',
isParam: node.attrs['param'] == 'true',
dtype: null,
};
this.deferDataType(node, vardef);
var const_nodes = this.findChildren(node, 'const', false);
if (const_nodes.length) {
vardef.constValue = const_nodes[0].obj;
}
var init_nodes = this.findChildren(node, 'initarray', false);
if (init_nodes.length) {
vardef.initValue = init_nodes[0].obj;
}
return vardef;
}
visit_const(node) {
var name = node.attrs['name'];
var cvalue = this.parseConstValue(name);
var constdef = {
$loc: this.parseSourceLocation(node),
dtype: null,
cvalue: typeof cvalue === 'number' ? cvalue : null,
bigvalue: typeof cvalue === 'bigint' ? cvalue : null,
};
this.deferDataType(node, constdef);
return constdef;
}
visit_varref(node) {
var name = node.attrs['name'];
name = this.name2js(name);
var varref = {
$loc: this.parseSourceLocation(node),
dtype: null,
refname: name
};
this.deferDataType(node, varref);
var mod = this.cur_module;
/*
this.defer2(() => {
varref.vardef = this.resolveVar(name, mod);
});
*/
return varref;
}
visit_sentree(node) {
// TODO
}
visit_always(node) {
// TODO
var sentree;
var expr;
if (node.children.length == 2) {
sentree = node.children[0].obj;
expr = node.children[1].obj;
// TODO: check sentree
}
else {
sentree = null;
expr = node.children[0].obj;
}
var always = {
$loc: this.parseSourceLocation(node),
blocktype: node.type,
name: null,
senlist: sentree,
exprs: [expr],
};
this.cur_module.blocks.push(always);
return always;
}
visit_begin(node) {
var exprs = [];
node.children.forEach((n) => exprs.push(n.obj));
return {
$loc: this.parseSourceLocation(node),
blocktype: node.type,
name: node.attrs['name'],
exprs: exprs,
};
}
visit_initarray(node) {
return this.visit_begin(node);
}
visit_inititem(node) {
this.expectChildren(node, 1, 1);
return {
index: parseInt(node.attrs['index']),
expr: node.children[0].obj
};
}
visit_cfunc(node) {
if (this.cur_module == null) { // TODO?
//console.log('no module open, skipping', node);
return;
}
var block = this.visit_begin(node);
block.exprs = [];
node.children.forEach((n) => block.exprs.push(n.obj));
this.cur_module.blocks.push(block);
return block;
}
visit_cuse(node) {
}
visit_instance(node) {
var instance = {
$loc: this.parseSourceLocation(node),
name: node.attrs['name'],
origName: node.attrs['origName'],
ports: [],
module: null,
};
node.children.forEach((child) => {
instance.ports.push(child.obj);
});
this.cur_module.instances.push(instance);
this.defer(() => {
instance.module = this.resolveModule(node.attrs['defName']);
});
return instance;
}
visit_iface(node) {
throw new CompileError(this.cur_loc, `interfaces not supported`);
}
visit_intfref(node) {
throw new CompileError(this.cur_loc, `interfaces not supported`);
}
visit_port(node) {
this.expectChildren(node, 1, 1);
var varref = {
$loc: this.parseSourceLocation(node),
name: node.attrs['name'],
expr: node.children[0].obj
};
return varref;
}
visit_netlist(node) {
}
visit_files(node) {
}
visit_module_files(node) {
node.children.forEach((n) => {
if (n.obj) {
var file = this.files[n.obj.id];
if (file)
file.isModule = true;
}
});
}
visit_file(node) {
return this.visit_file_or_module(node, false);
}
// TODO
visit_scope(node) {
}
visit_topscope(node) {
}
visit_file_or_module(node, isModule) {
var file = {
id: node.attrs['id'],
filename: node.attrs['filename'],
isModule: isModule,
};
this.files[file.id] = file;
return file;
}
visit_cells(node) {
this.expectChildren(node, 1, 9999);
var hier = node.children[0].obj;
if (hier != null) {
var hiername = hier.name;
this.hierarchies[hiername] = hier;
}
}
visit_cell(node) {
var hier = {
$loc: this.parseSourceLocation(node),
name: node.attrs['name'],
module: null,
parent: null,
children: node.children.map((n) => n.obj),
};
if (node.children.length > 0)
throw new CompileError(this.cur_loc, `multiple non-flattened modules not yet supported`);
node.children.forEach((n) => n.obj.parent = hier);
this.defer(() => {
hier.module = this.resolveModule(node.attrs['submodname']);
});
return hier;
}
visit_basicdtype(node) {
let id = node.attrs['id'];
var dtype;
var dtypename = node.attrs['name'];
switch (dtypename) {
case 'logic':
case 'integer': // TODO?
case 'bit':
let dlogic = {
$loc: this.parseSourceLocation(node),
left: parseInt(node.attrs['left'] || "0"),
right: parseInt(node.attrs['right'] || "0"),
signed: node.attrs['signed'] == 'true'
};
dtype = dlogic;
break;
case 'string':
let dstring = {
$loc: this.parseSourceLocation(node),
jstype: 'string'
};
dtype = dstring;
break;
default:
dtype = this.dtypes[dtypename];
if (dtype == null) {
throw new CompileError(this.cur_loc, `unknown data type ${dtypename}`);
}
}
this.dtypes[id] = dtype;
return dtype;
}
visit_refdtype(node) {
}
visit_enumdtype(node) {
}
visit_enumitem(node) {
}
visit_packarraydtype(node) {
// TODO: packed?
return this.visit_unpackarraydtype(node);
}
visit_memberdtype(node) {
throw new CompileError(null, `structs not supported`);
}
visit_constdtype(node) {
// TODO? throw new CompileError(null, `constant data types not supported`);
}
visit_paramtypedtype(node) {
// TODO? throw new CompileError(null, `constant data types not supported`);
}
visit_unpackarraydtype(node) {
let id = node.attrs['id'];
let sub_dtype_id = node.attrs['sub_dtype_id'];
let range = node.children[0].obj;
if ((0, hdltypes_1.isConstExpr)(range.left) && (0, hdltypes_1.isConstExpr)(range.right)) {
var dtype = {
$loc: this.parseSourceLocation(node),
subtype: null,
low: range.left,
high: range.right,
};
this.dtypes[id] = dtype;
this.defer(() => {
dtype.subtype = this.dtypes[sub_dtype_id];
if (!dtype.subtype)
throw new CompileError(this.cur_loc, `Unknown data type ${sub_dtype_id} for array`);
});
return dtype;
}
else {
throw new CompileError(this.cur_loc, `could not parse constant exprs in array`);
}
}
visit_senitem(node) {
var edgeType = node.attrs['edgeType'];
if (edgeType != "POS" && edgeType != "NEG")
throw new CompileError(this.cur_loc, "POS/NEG required");
return {
$loc: this.parseSourceLocation(node),
edgeType: edgeType,
expr: node.obj
};
}
visit_text(node) {
}
visit_cstmt(node) {
}
visit_cfile(node) {
}
visit_typetable(node) {
}
visit_constpool(node) {
}
visit_comment(node) {
}
expectChildren(node, low, high) {
if (node.children.length < low || node.children.length > high)
throw new CompileError(this.cur_loc, `expected between ${low} and ${high} children`);
}
__visit_unop(node) {
this.expectChildren(node, 1, 1);
var expr = {
$loc: this.parseSourceLocation(node),
op: node.type,
dtype: null,
left: node.children[0].obj,
};
this.deferDataType(node, expr);
return expr;
}
visit_extend(node) {
var unop = this.__visit_unop(node);
unop.width = parseInt(node.attrs['width']);
unop.widthminv = parseInt(node.attrs['widthminv']);
if (unop.width != 32)
throw new CompileError(this.cur_loc, `extends width ${unop.width} != 32`);
return unop;
}
visit_extends(node) {
return this.visit_extend(node);
}
__visit_binop(node) {
this.expectChildren(node, 2, 2);
var expr = {
$loc: this.parseSourceLocation(node),
op: node.type,
dtype: null,
left: node.children[0].obj,
right: node.children[1].obj,
};
this.deferDataType(node, expr);
return expr;
}
visit_if(node) {
this.expectChildren(node, 2, 3);
var expr = {
$loc: this.parseSourceLocation(node),
op: 'if',
dtype: null,
cond: node.children[0].obj,
left: node.children[1].obj,
right: node.children[2] && node.children[2].obj,
};
return expr;
}
// while and for loops
visit_while(node) {
this.expectChildren(node, 2, 4);
var expr = {
$loc: this.parseSourceLocation(node),
op: 'while',
dtype: null,
precond: node.children[0].obj,
loopcond: node.children[1].obj,
body: node.children[2] && node.children[2].obj,
inc: node.children[3] && node.children[3].obj,
};
return expr;
}
__visit_triop(node) {
this.expectChildren(node, 3, 3);
var expr = {
$loc: this.parseSourceLocation(node),
op: node.type,
dtype: null,
cond: node.children[0].obj,
left: node.children[1].obj,
right: node.children[2].obj,
};
this.deferDataType(node, expr);
return expr;
}
__visit_func(node) {
var expr = {
$loc: this.parseSourceLocation(node),
dtype: null,
funcname: node.attrs['func'] || ('$' + node.type),
args: node.children.map(n => n.obj)
};
this.deferDataType(node, expr);
return expr;
}
visit_not(node) { return this.__visit_unop(node); }
visit_negate(node) { return this.__visit_unop(node); }
visit_redand(node) { return this.__visit_unop(node); }
visit_redor(node) { return this.__visit_unop(node); }
visit_redxor(node) { return this.__visit_unop(node); }
visit_initial(node) { return this.__visit_unop(node); }
visit_ccast(node) { return this.__visit_unop(node); }
visit_creset(node) { return this.__visit_unop(node); }
visit_creturn(node) { return this.__visit_unop(node); }
visit_contassign(node) { return this.__visit_binop(node); }
visit_assigndly(node) { return this.__visit_binop(node); }
visit_assignpre(node) { return this.__visit_binop(node); }
visit_assignpost(node) { return this.__visit_binop(node); }
visit_assign(node) { return this.__visit_binop(node); }
visit_arraysel(node) { return this.__visit_binop(node); }
visit_wordsel(node) { return this.__visit_binop(node); }
visit_eq(node) { return this.__visit_binop(node); }
visit_neq(node) { return this.__visit_binop(node); }
visit_lte(node) { return this.__visit_binop(node); }
visit_gte(node) { return this.__visit_binop(node); }
visit_lt(node) { return this.__visit_binop(node); }
visit_gt(node) { return this.__visit_binop(node); }
visit_and(node) { return this.__visit_binop(node); }
visit_or(node) { return this.__visit_binop(node); }
visit_xor(node) { return this.__visit_binop(node); }
visit_add(node) { return this.__visit_binop(node); }
visit_sub(node) { return this.__visit_binop(node); }
visit_concat(node) { return this.__visit_binop(node); } // TODO?
visit_shiftl(node) { return this.__visit_binop(node); }
visit_shiftr(node) { return this.__visit_binop(node); }
visit_shiftrs(node) { return this.__visit_binop(node); }
visit_mul(node) { return this.__visit_binop(node); }
visit_div(node) { return this.__visit_binop(node); }
visit_moddiv(node) { return this.__visit_binop(node); }
visit_muls(node) { return this.__visit_binop(node); }
visit_divs(node) { return this.__visit_binop(node); }
visit_moddivs(node) { return this.__visit_binop(node); }
visit_gts(node) { return this.__visit_binop(node); }
visit_lts(node) { return this.__visit_binop(node); }
visit_gtes(node) { return this.__visit_binop(node); }
visit_ltes(node) { return this.__visit_binop(node); }
// TODO: more?
visit_range(node) { return this.__visit_binop(node); }
visit_cond(node) { return this.__visit_triop(node); }
visit_condbound(node) { return this.__visit_triop(node); }
visit_sel(node) { return this.__visit_triop(node); }
visit_changedet(node) {
if (node.children.length == 0)
return null; //{ op: "changedet", dtype:null, left:null, right:null }
else
return this.__visit_binop(node);
}
visit_ccall(node) { return this.__visit_func(node); }
visit_finish(node) { return this.__visit_func(node); }
visit_stop(node) { return this.__visit_func(node); }
visit_rand(node) { return this.__visit_func(node); }
visit_time(node) { return this.__visit_func(node); }
visit_display(node) { return null; }
visit_sformatf(node) { return null; }
visit_scopename(node) { return null; }
visit_readmem(node) { return this.__visit_func(node); }
//
xml_open(node) {
this.cur_node = node;
var method = this[`open_${node.type}`];
if (method) {
return method.bind(this)(node);
}
}
xml_close(node) {
this.cur_node = node;
var method = this[`visit_${node.type}`];
if (method) {
return method.bind(this)(node);
}
else {
throw new CompileError(this.cur_loc, `no visitor for ${node.type}`);
}
}
parse(xmls) {
(0, util_1.parseXMLPoorly)(xmls, this.xml_open.bind(this), this.xml_close.bind(this));
this.cur_node = null;
this.run_deferred();
}
}
exports.VerilogXMLParser = VerilogXMLParser;
//# sourceMappingURL=vxmlparser.js.map

File diff suppressed because one or more lines are too long

112
gen/common/hdl/vxmltest.js Normal file
View File

@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("../util");
const hdlruntime_1 = require("./hdlruntime");
const hdlwasm_1 = require("./hdlwasm");
const vxmlparser_1 = require("./vxmlparser");
var fs = require('fs');
var xmltxt = fs.readFileSync(process.argv[2], 'utf8');
var parser = new vxmlparser_1.VerilogXMLParser();
try {
parser.parse(xmltxt);
}
catch (e) {
console.log(parser.cur_node);
throw e;
}
//console.log(parser);
var modname = 'TOP'; //process.argv[3];
async function testWASM() {
var bmod = new hdlwasm_1.HDLModuleWASM(parser.modules[modname], parser.modules['@CONST-POOL@']);
await bmod.init();
bmod.powercycle();
//console.log(this.globals);
bmod.state.reset = 1;
for (var i = 0; i < 10; i++) {
bmod.tick2(1);
if (i == 5)
bmod.state.reset = 0;
bmod.nextTrace();
}
console.log(bmod.databuf);
var t1 = new Date().getTime();
var tickiters = 10000;
var looplen = Math.round(100000000 / tickiters);
for (var i = 0; i < looplen; i++) {
bmod.tick2(tickiters);
}
var t2 = new Date().getTime();
console.log('wasm:', t2 - t1, 'msec', i * tickiters, 'iterations');
console.log(bmod.databuf);
}
async function testJS() {
var mod = new hdlruntime_1.HDLModuleJS(parser.modules[modname], parser.modules['@CONST-POOL@']);
mod.init();
console.log(mod.getJSCode());
mod.powercycle();
var t1 = new Date().getTime();
for (var i = 0; i < 100000000; i++) {
mod.tick2(1);
}
mod.state.reset = 1;
for (var j = 0; j < 10000000; j++) {
mod.tick2(1);
}
var t2 = new Date().getTime();
console.log(mod.state);
console.log('js:', t2 - t1, 'msec', i, 'iterations', i / 1000 / (t2 - t1), 'MHz');
//console.log(emitter);
}
async function testJSvsWASM() {
const top = parser.modules[modname];
const constpool = parser.modules['@CONST-POOL@'];
var jmod = new hdlruntime_1.HDLModuleJS(top, constpool);
jmod.init();
jmod.powercycle();
var bmod = new hdlwasm_1.HDLModuleWASM(top, constpool);
await bmod.init();
bmod.powercycle();
var varnames = Object.keys(top.vardefs);
var exit = false;
for (var i = 0; i < 100000000; i++) {
for (var vname of varnames) {
var jvalue = jmod.state[vname];
var bvalue = bmod.state[vname];
if (typeof jvalue === 'number') {
if (jvalue != bvalue) {
console.log('*** Value for', vname, 'differs', jvalue, bvalue);
exit = true;
}
}
else if (jvalue.buffer != null) {
if (!(0, util_1.arrayCompare)(jvalue, bvalue)) {
console.log('*** Value for', vname, 'differs', jvalue, bvalue);
exit = true;
}
}
}
if (jmod.isFinished() || bmod.isFinished()) {
if (jmod.isFinished() != bmod.isFinished()) {
console.log('*** Abnormal finish', jmod.isFinished(), bmod.isFinished());
}
exit = true;
}
if (jmod.isStopped() || bmod.isStopped()) {
if (jmod.isStopped() != bmod.isStopped()) {
console.log('*** Abnormal stop', jmod.isStopped(), bmod.isStopped());
}
exit = true;
}
if (exit) {
console.log('exit iteration', i);
break;
}
jmod.tick2(1);
bmod.tick2(1);
}
}
//testJS();
testWASM();
//testJSvsWASM();
//testWASM().then(testJS).then(testJSvsWASM);
//# sourceMappingURL=vxmltest.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"vxmltest.js","sourceRoot":"","sources":["../../../src/common/hdl/vxmltest.ts"],"names":[],"mappings":"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"}

358
gen/common/mameplatform.js Normal file
View File

@@ -0,0 +1,358 @@
"use strict";
/// MAME SUPPORT
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseMAMEZ80Platform = exports.BaseMAME6502Platform = exports.BaseMAMEPlatform = void 0;
const baseplatform_1 = require("./baseplatform");
const disasm6502_1 = require("./cpu/disasm6502");
const disasmz80_1 = require("./cpu/disasmz80");
const emu_1 = require("./emu");
class BaseMAMEPlatform {
constructor(mainElement) {
this.loaded = false;
this.preinitted = false;
this.started = false;
this.romtype = 'cart';
this.running = false;
this.initluavars = false;
this.mainElement = mainElement;
this.timer = new emu_1.AnimationTimer(20, this.poll.bind(this));
}
// http://docs.mamedev.org/techspecs/luaengine.html
luacall(s) {
if (!this.js_lua_string)
this.js_lua_string = Module.cwrap('_Z13js_lua_stringPKc', 'string', ['string']);
return this.js_lua_string(s || "");
}
_pause() {
this.running = false;
this.timer.stop();
}
pause() {
if (this.loaded && this.running) {
this.luacall('emu.pause()');
this._pause();
}
}
_resume() {
this.luacall('emu.unpause()');
this.running = true;
this.timer.start();
}
resume() {
if (this.loaded && !this.running) { // TODO
this._resume();
}
}
reset() {
if (this.loaded) {
this.luacall('manager:machine():soft_reset()');
this.running = true;
this.initluavars = false;
}
}
isRunning() {
return this.running;
}
bufferConsoleOutput(s) {
if (typeof s !== 'string')
return;
console.log(s);
}
startModule(mainElement, opts) {
this.started = true;
var romfn = this.romfn = this.romfn || opts.romfn;
var romdata = this.romdata = this.romdata || opts.romdata || new emu_1.RAM(opts.romsize).mem;
var romtype = this.romtype = this.romtype || opts.romtype;
// create canvas
var video = this.video = new emu_1.RasterVideo(this.mainElement, opts.width, opts.height);
video.create();
$(video.canvas).attr('id', 'canvas');
// load asm.js module
console.log("loading", opts.jsfile);
var modargs = [opts.driver,
'-debug',
'-debugger', 'none',
'-verbose', '-window', '-nokeepaspect',
'-resolution', video.canvas.width + 'x' + video.canvas.height
];
if (romfn) {
modargs.push('-' + romtype, romfn);
}
if (opts.extraargs) {
modargs = modargs.concat(opts.extraargs);
}
console.log(modargs);
window['JSMESS'] = {};
window['Module'] = {
arguments: modargs,
screenIsReadOnly: true,
print: this.bufferConsoleOutput,
canvas: video.canvas,
doNotCaptureKeyboard: true,
keyboardListeningElement: video.canvas,
preInit: () => {
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');
if (romfn) {
FS.writeFile(romfn, romdata, { encoding: 'binary' });
}
//FS.writeFile('/debug.ini', 'debugger none\n', {encoding:'utf8'});
if (opts.preInit) {
opts.preInit(self);
}
this.preinitted = true;
},
preRun: [
() => {
$(video.canvas).click((e) => {
video.canvas.focus();
});
this.loaded = true;
console.log("about to run...");
}
]
};
// preload files
// TODO: ensure loaded
var fetch_cfg, fetch_lua;
var fetch_bios = $.Deferred();
var fetch_wasm = $.Deferred();
// fetch config file
if (opts.cfgfile) {
fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, (data) => {
opts.cfgdata = data;
console.log("loaded " + opts.cfgfile);
}, 'text');
}
// fetch BIOS file
if (opts.biosfile) {
var oReq1 = new XMLHttpRequest();
oReq1.open("GET", 'mame/roms/' + opts.biosfile, true);
oReq1.responseType = "arraybuffer";
oReq1.onload = (oEvent) => {
opts.biosdata = new Uint8Array(oReq1.response);
console.log("loaded " + opts.biosfile); // + " (" + oEvent.total + " bytes)");
fetch_bios.resolve();
};
oReq1.ontimeout = function (oEvent) {
throw Error("Timeout loading " + opts.biosfile);
};
oReq1.send();
}
else {
fetch_bios.resolve();
}
// load debugger Lua script
fetch_lua = $.get('mame/debugger.lua', (data) => {
this.luadebugscript = data;
console.log("loaded debugger.lua");
}, 'text');
// load WASM
{
var oReq2 = new XMLHttpRequest();
oReq2.open("GET", 'mame/' + opts.jsfile.replace('.js', '.wasm'), true);
oReq2.responseType = "arraybuffer";
oReq2.onload = (oEvent) => {
console.log("loaded WASM file");
window['Module'].wasmBinary = new Uint8Array(oReq2.response);
fetch_wasm.resolve();
};
oReq2.ontimeout = function (oEvent) {
throw Error("Timeout loading " + opts.jsfile);
};
oReq2.send();
}
// start loading script
$.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done(() => {
var script = document.createElement('script');
script.src = 'mame/' + opts.jsfile;
document.getElementsByTagName('head')[0].appendChild(script);
console.log("created script element");
});
// for debugging via browser console
window['mamelua'] = (s) => {
this.initlua();
return [s, this.luacall(s)];
};
}
loadROMFile(data) {
this.romdata = data;
if (this.preinitted && this.romfn) {
FS.writeFile(this.romfn, data, { encoding: 'binary' });
}
}
loadRegion(region, data) {
if (this.loaded && data.length > 0) {
//this.luacall('cart=manager:machine().images["cart"]\nprint(cart:filename())\ncart:load("' + region + '")\n');
var s = 'rgn = manager:machine():memory().regions["' + region + '"]\n';
//s += 'print(rgn.size)\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 += 'rgn:write_u32(' + i + ',' + v + ')\n'; // TODO: endian?
}
this.luacall(s);
this.reset();
}
}
// DEBUGGING SUPPORT
initlua() {
if (!this.initluavars) {
this.luacall(this.luadebugscript);
this.luacall('mamedbg.init()');
this.initluavars = true;
}
}
readAddress(a) {
this.initlua();
return parseInt(this.luacall('return mem:read_u8(' + a + ')'));
}
getCPUReg(reg) {
if (!this.loaded)
return 0; // TODO
this.initlua();
return parseInt(this.luacall('return cpu.state.' + reg + '.value'));
}
grabState(expr) {
this.initlua();
return {
c: this.getCPUState(),
buf: this.luacall("return string.tohex(" + expr + ")")
};
}
saveState() {
return this.grabState("manager:machine():buffer_save()");
}
loadState(state) {
this.initlua();
return this.luacall("manager:machine():buffer_load(string.fromhex('" + state.buf + "'))");
}
poll() {
if (this.onBreakpointHit && this.luacall("return tostring(mamedbg.is_stopped())") == 'true') {
this._pause();
//this.luacall("manager:machine():buffer_load(lastBreakState)");
var state = this.grabState("lastBreakState");
this.onBreakpointHit(state);
}
}
clearDebug() {
this.onBreakpointHit = null;
if (this.loaded) {
this.initlua();
this.luacall('mamedbg.reset()');
}
}
getDebugCallback() {
return this.onBreakpointHit; // TODO?
}
setupDebug(callback) {
this.onBreakpointHit = callback;
}
debugcmd(s) {
this.initlua();
this.luacall(s);
this._resume();
}
runToPC(pc) {
this.debugcmd('mamedbg.runTo(' + pc + ')');
}
runToVsync() {
this.debugcmd('mamedbg.runToVsync()');
}
runUntilReturn() {
this.debugcmd('mamedbg.runUntilReturn()');
}
// TODO
runEval() {
this.reset();
this.step();
}
step() {
this.debugcmd('mamedbg.step()');
}
getDebugCategories() {
return ['CPU'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return this.cpuStateToLongString(state.c);
}
}
getDebugTree() {
this.initlua();
var devices = JSON.parse(this.luacall(`return table.tojson(manager:machine().devices)`));
var images = JSON.parse(this.luacall(`return table.tojson(manager:machine().images)`));
var regions = JSON.parse(this.luacall(`return table.tojson(manager:machine():memory().regions)`));
return {
devices: devices,
images: images,
regions: regions,
};
}
}
exports.BaseMAMEPlatform = BaseMAMEPlatform;
class BaseMAME6502Platform extends BaseMAMEPlatform {
getPC() {
return this.getCPUReg('PC');
}
getSP() {
return this.getCPUReg('SP');
}
isStable() { return true; }
getCPUState() {
return {
PC: this.getPC(),
SP: this.getSP(),
A: this.getCPUReg('A'),
X: this.getCPUReg('X'),
Y: this.getCPUReg('Y'),
flags: this.getCPUReg('P'),
};
}
disassemble(pc, read) {
return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2));
}
cpuStateToLongString(c) {
return (0, baseplatform_1.cpuStateToLongString_6502)(c);
}
}
exports.BaseMAME6502Platform = BaseMAME6502Platform;
class BaseMAMEZ80Platform extends BaseMAMEPlatform {
getPC() {
return this.getCPUReg('PC');
}
getSP() {
return this.getCPUReg('SP');
}
isStable() { return true; }
getCPUState() {
return {
PC: this.getPC(),
SP: this.getSP(),
AF: this.getCPUReg('AF'),
BC: this.getCPUReg('BC'),
DE: this.getCPUReg('DE'),
HL: this.getCPUReg('HL'),
IX: this.getCPUReg('IX'),
IY: this.getCPUReg('IY'),
IR: this.getCPUReg('R') + (this.getCPUReg('I') << 8),
};
}
disassemble(pc, read) {
return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3));
}
cpuStateToLongString(c) {
return (0, baseplatform_1.cpuStateToLongString_Z80)(c);
}
}
exports.BaseMAMEZ80Platform = BaseMAMEZ80Platform;
//# sourceMappingURL=mameplatform.js.map

File diff suppressed because one or more lines are too long

281
gen/common/recorder.js Normal file
View File

@@ -0,0 +1,281 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProbeRecorder = exports.ProbeFlags = exports.StateRecorderImpl = void 0;
const emu_1 = require("./emu");
class StateRecorderImpl {
constructor(platform) {
this.checkpointInterval = 10;
this.maxCheckpoints = 300;
this.reset();
this.platform = platform;
}
reset() {
this.checkpoints = [];
this.framerecs = [];
this.frameCount = 0;
this.lastSeekFrame = 0;
this.lastSeekStep = 0;
this.lastStepCount = 0;
if (this.callbackStateChanged)
this.callbackStateChanged();
}
frameRequested() {
var controls = {
controls: this.platform.saveControlsState(),
seed: (0, emu_1.getNoiseSeed)()
};
var requested = false;
// are we replaying? then we don't need to save a frame, just replace controls
if (this.lastSeekFrame < this.frameCount) {
this.loadControls(this.lastSeekFrame);
}
else {
// record the control state, if available
if (this.platform.saveControlsState) {
this.framerecs.push(controls);
}
// time to save next frame?
requested = (this.frameCount++ % this.checkpointInterval) == 0;
}
this.lastSeekFrame++;
this.lastSeekStep = 0;
if (this.callbackStateChanged)
this.callbackStateChanged();
return requested;
}
numFrames() {
return this.frameCount;
}
currentFrame() {
return this.lastSeekFrame;
}
currentStep() {
return this.lastSeekStep;
}
recordFrame(state) {
this.checkpoints.push(state);
if (this.callbackNewCheckpoint)
this.callbackNewCheckpoint(state);
// checkpoints full?
if (this.checkpoints.length > this.maxCheckpoints) {
this.checkpoints.shift(); // remove 1st checkpoint
this.framerecs = this.framerecs.slice(this.checkpointInterval);
this.lastSeekFrame -= this.checkpointInterval;
this.frameCount -= this.checkpointInterval;
if (this.callbackStateChanged)
this.callbackStateChanged();
}
}
getStateAtOrBefore(frame) {
// initial frame?
if (frame <= 0 && this.checkpoints.length > 0)
return { frame: 0, state: this.checkpoints[0] };
var bufidx = Math.floor(frame / this.checkpointInterval);
var foundidx = bufidx < this.checkpoints.length ? bufidx : this.checkpoints.length - 1;
var foundframe = foundidx * this.checkpointInterval;
return { frame: foundframe, state: this.checkpoints[foundidx] };
}
loadFrame(seekframe, seekstep) {
seekframe |= 0;
seekstep |= 0;
if (seekframe == this.lastSeekFrame && seekstep == this.lastSeekStep) {
return seekframe; // already set to this frame
}
// TODO: what if < 1?
let { frame, state } = this.getStateAtOrBefore(seekframe - 1);
if (state) {
var numSteps = 0;
this.platform.pause();
this.platform.loadState(state);
// seek to frame index
while (frame < seekframe) {
if (frame < this.framerecs.length) {
this.loadControls(frame);
}
frame++;
numSteps = this.platform.advance(frame < seekframe); // TODO: infinite loop?
}
// TODO: if first frame, we must figure out # of steps
if (frame == 0) {
numSteps = this.platform.advance(true);
this.platform.loadState(state);
}
// seek to step index
// TODO: what if advance() returns clocks, but steps use insns?
if (seekstep > 0 && this.platform.advanceFrameClock) {
seekstep = this.platform.advanceFrameClock(null, seekstep);
}
// record new values
this.lastSeekFrame = seekframe;
this.lastSeekStep = seekstep;
this.lastStepCount = numSteps;
return seekframe;
}
else {
return -1;
}
}
loadControls(frame) {
if (this.platform.loadControlsState)
this.platform.loadControlsState(this.framerecs[frame].controls);
(0, emu_1.setNoiseSeed)(this.framerecs[frame].seed);
}
getLastCheckpoint() {
return this.checkpoints.length && this.checkpoints[this.checkpoints.length - 1];
}
}
exports.StateRecorderImpl = StateRecorderImpl;
var ProbeFlags;
(function (ProbeFlags) {
ProbeFlags[ProbeFlags["CLOCKS"] = 0] = "CLOCKS";
ProbeFlags[ProbeFlags["EXECUTE"] = 16777216] = "EXECUTE";
ProbeFlags[ProbeFlags["HAS_VALUE"] = 268435456] = "HAS_VALUE";
ProbeFlags[ProbeFlags["MEM_READ"] = 301989888] = "MEM_READ";
ProbeFlags[ProbeFlags["MEM_WRITE"] = 318767104] = "MEM_WRITE";
ProbeFlags[ProbeFlags["IO_READ"] = 335544320] = "IO_READ";
ProbeFlags[ProbeFlags["IO_WRITE"] = 352321536] = "IO_WRITE";
ProbeFlags[ProbeFlags["VRAM_READ"] = 369098752] = "VRAM_READ";
ProbeFlags[ProbeFlags["VRAM_WRITE"] = 385875968] = "VRAM_WRITE";
ProbeFlags[ProbeFlags["INTERRUPT"] = 134217728] = "INTERRUPT";
ProbeFlags[ProbeFlags["ILLEGAL"] = 150994944] = "ILLEGAL";
ProbeFlags[ProbeFlags["SP_PUSH"] = 167772160] = "SP_PUSH";
ProbeFlags[ProbeFlags["SP_POP"] = 184549376] = "SP_POP";
ProbeFlags[ProbeFlags["SCANLINE"] = 2113929216] = "SCANLINE";
ProbeFlags[ProbeFlags["FRAME"] = 2130706432] = "FRAME";
})(ProbeFlags = exports.ProbeFlags || (exports.ProbeFlags = {}));
class ProbeFrame {
}
class ProbeRecorder {
constructor(m, buflen) {
this.idx = 0; // index into buffer
this.sl = 0; // scanline
this.cur_sp = -1; // last stack pointer
this.singleFrame = true; // clear between frames
this.m = m;
this.reset(buflen || 0x100000);
}
start() {
this.m.connectProbe(this);
}
stop() {
this.m.connectProbe(null);
}
reset(newbuflen) {
if (newbuflen)
this.buf = new Uint32Array(newbuflen);
this.sl = 0;
this.cur_sp = -1;
this.clear();
}
clear() {
this.idx = 0;
}
logData(a) {
this.log(a);
}
log(a) {
// TODO: coalesce READ and EXECUTE and PUSH/POP
if (this.idx >= this.buf.length)
return;
this.buf[this.idx++] = a;
}
relog(a) {
this.buf[this.idx - 1] = a;
}
lastOp() {
if (this.idx > 0)
return this.buf[this.idx - 1] & 0xff000000;
else
return -1;
}
lastAddr() {
if (this.idx > 0)
return this.buf[this.idx - 1] & 0xffffff;
else
return -1;
}
addLogBuffer(src) {
if (this.idx + src.length > this.buf.length) {
src = src.slice(0, this.buf.length - this.idx);
}
this.buf.set(src, this.idx);
this.idx += src.length;
}
logClocks(clocks) {
clocks |= 0;
if (clocks > 0) {
if (this.lastOp() == ProbeFlags.CLOCKS)
this.relog((this.lastAddr() + clocks) | ProbeFlags.CLOCKS); // coalesce clocks
else
this.log(clocks | ProbeFlags.CLOCKS);
}
}
logNewScanline() {
this.log(ProbeFlags.SCANLINE);
this.sl++;
}
logNewFrame() {
this.log(ProbeFlags.FRAME);
this.sl = 0;
if (this.singleFrame)
this.clear();
}
logExecute(address, SP) {
// record stack pushes/pops (from last instruction)
if (this.cur_sp !== SP) {
if (SP < this.cur_sp) {
this.log(ProbeFlags.SP_PUSH | SP);
}
if (SP > this.cur_sp) {
this.log(ProbeFlags.SP_POP | SP);
}
this.cur_sp = SP;
}
this.log(address | ProbeFlags.EXECUTE);
}
logInterrupt(type) {
this.log(type | ProbeFlags.INTERRUPT);
}
logValue(address, value, op) {
this.log((address & 0xffff) | ((value & 0xff) << 16) | op);
}
logRead(address, value) {
this.logValue(address, value, ProbeFlags.MEM_READ);
}
logWrite(address, value) {
this.logValue(address, value, ProbeFlags.MEM_WRITE);
}
logIORead(address, value) {
this.logValue(address, value, ProbeFlags.IO_READ);
}
logIOWrite(address, value) {
this.logValue(address, value, ProbeFlags.IO_WRITE);
}
logVRAMRead(address, value) {
this.logValue(address, value, ProbeFlags.VRAM_READ);
}
logVRAMWrite(address, value) {
this.logValue(address, value, ProbeFlags.VRAM_WRITE);
}
logIllegal(address) {
this.log(address | ProbeFlags.ILLEGAL);
}
countEvents(op) {
var count = 0;
for (var i = 0; i < this.idx; i++) {
if ((this.buf[i] & 0xff000000) == op)
count++;
}
return count;
}
countClocks() {
var count = 0;
for (var i = 0; i < this.idx; i++) {
if ((this.buf[i] & 0xff000000) == ProbeFlags.CLOCKS)
count += this.buf[i] & 0xffff;
}
return count;
}
}
exports.ProbeRecorder = ProbeRecorder;
//# sourceMappingURL=recorder.js.map

File diff suppressed because one or more lines are too long

300
gen/common/script/env.js Normal file
View File

@@ -0,0 +1,300 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Environment = exports.PROP_CONSTRUCTOR_NAME = void 0;
const ErrorStackParser = require("error-stack-parser");
const yufka_1 = __importDefault(require("yufka"));
const bitmap = __importStar(require("./lib/bitmap"));
const io = __importStar(require("./lib/io"));
const output = __importStar(require("./lib/output"));
const color = __importStar(require("./lib/color"));
const scriptui = __importStar(require("./lib/scriptui"));
exports.PROP_CONSTRUCTOR_NAME = "$$consname";
const IMPORTS = {
'bitmap': bitmap,
'io': io,
'output': output,
'color': color,
'ui': scriptui,
};
const LINE_NUMBER_OFFSET = 3; // TODO: shouldnt need?
const GLOBAL_BADLIST = [
'eval'
];
const GLOBAL_GOODLIST = [
'eval',
'Math', 'JSON',
'parseFloat', 'parseInt', 'isFinite', 'isNaN',
'String', 'Symbol', 'Number', 'Object', 'Boolean', 'NaN', 'Infinity', 'Date', 'BigInt',
'Set', 'Map', 'RegExp', 'Array', 'ArrayBuffer', 'DataView',
'Float32Array', 'Float64Array',
'Int8Array', 'Int16Array', 'Int32Array',
'Uint8Array', 'Uint16Array', 'Uint32Array', 'Uint8ClampedArray',
];
class RuntimeError extends Error {
constructor(loc, msg) {
super(msg);
this.loc = loc;
}
}
function setConstructorName(o) {
var _a, _b;
let name = (_b = (_a = Object.getPrototypeOf(o)) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name;
if (name != null && name != 'Object') {
o[exports.PROP_CONSTRUCTOR_NAME] = name;
}
}
class Environment {
constructor(globalenv, path) {
this.globalenv = globalenv;
this.path = path;
var badlst = Object.getOwnPropertyNames(this.globalenv).filter(name => GLOBAL_GOODLIST.indexOf(name) < 0);
this.builtins = Object.assign({ print: (...args) => this.print(args) }, IMPORTS);
this.preamble = `'use strict';var ${badlst.join(',')};`;
for (var impname in this.builtins) {
this.preamble += `var ${impname}=$$.${impname};`;
}
this.preamble += '{\n';
this.postamble = '\n}';
}
error(varname, msg) {
let obj = this.declvars && this.declvars[varname];
console.log('ERROR', varname, obj, this);
throw new RuntimeError(obj && obj.loc, msg);
}
print(args) {
if (args && args.length > 0 && args[0] != null) {
this.obj[`$print__${this.seq++}`] = args.length == 1 ? args[0] : args;
}
}
preprocess(code) {
this.declvars = {};
this.seq = 0;
let options = {
// https://www.npmjs.com/package/magic-string#sgeneratemap-options-
sourceMap: {
file: this.path,
source: this.path,
hires: false,
includeContent: false
},
// https://github.com/acornjs/acorn/blob/master/acorn/README.md
acorn: {
ecmaVersion: 6,
locations: true,
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true,
allowReserved: true,
}
};
const result = (0, yufka_1.default)(code, options, (node, { update, source, parent }) => {
const isTopLevel = () => {
return parent() && parent().type === 'ExpressionStatement' && parent(2) && parent(2).type === 'Program';
};
const convertTopToPrint = () => {
if (isTopLevel()) {
let printkey = `$print__${this.seq++}`;
update(`this.${printkey} = io.data.load(${source()}, ${JSON.stringify(printkey)})`);
//update(`print(${source()});`)
}
};
const left = node['left'];
switch (node.type) {
// add preamble, postamble
case 'Program':
update(`${this.preamble}${source()}${this.postamble}`);
break;
// error on forbidden keywords
case 'Identifier':
if (GLOBAL_BADLIST.indexOf(source()) >= 0) {
update(`__FORBIDDEN__KEYWORD__${source()}__`); // TODO? how to preserve line number?
}
else {
convertTopToPrint();
}
break;
// x = expr --> var x = expr (first use)
case 'AssignmentExpression':
if (isTopLevel()) {
if (left && left.type === 'Identifier') {
if (!this.declvars[left.name]) {
update(`var ${left.name}=io.data.load(this.${source()}, ${JSON.stringify(left.name)})`);
this.declvars[left.name] = left;
}
else {
update(`${left.name}=this.${source()}`);
}
}
}
break;
// convert lone expressions to print()
case 'UnaryExpression':
case 'BinaryExpression':
case 'CallExpression':
case 'MemberExpression':
convertTopToPrint();
break;
// literal comments
case 'Literal':
if (typeof node['value'] === 'string' && isTopLevel()) {
update(`this.$doc__${this.seq++} = { literaltext: ${source()} };`);
}
else {
convertTopToPrint();
}
break;
}
});
return result.toString();
}
async run(code) {
// TODO: split into cells based on "--" linebreaks?
code = this.preprocess(code);
this.obj = {};
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
const fn = new AsyncFunction('$$', code).bind(this.obj, this.builtins);
await fn.call(this);
this.checkResult(this.obj, new Set(), []);
}
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
// TODO: return initial location of thingie
checkResult(o, checked, fullkey) {
if (o == null)
return;
if (checked.has(o))
return;
if (typeof o === 'object') {
setConstructorName(o);
delete o.$$callback; // clear callbacks (TODO? put somewhere else?)
if (o.length > 100)
return; // big array, don't bother
if (o.BYTES_PER_ELEMENT > 0)
return; // typed array, don't bother
checked.add(o); // so we don't recurse if cycle
function prkey() { return fullkey.join('.'); }
// go through all object properties recursively
for (var [key, value] of Object.entries(o)) {
if (value == null && fullkey.length == 0 && !key.startsWith("$")) {
this.error(key, `"${key}" has no value.`);
}
fullkey.push(key);
if (typeof value === 'function') {
if (fullkey.length == 1)
this.error(fullkey[0], `"${prkey()}" is a function. Did you forget to pass parameters?`); // TODO? did you mean (needs to see entire expr)
else
this.error(fullkey[0], `This expression may be incomplete, or it contains a function object: ${prkey()}`); // TODO? did you mean (needs to see entire expr)
}
if (typeof value === 'symbol') {
this.error(fullkey[0], `"${prkey()}" is a Symbol, and can't be used.`); // TODO?
}
if (value instanceof Promise) {
this.error(fullkey[0], `"${prkey()}" is unresolved. Use "await" before expression.`); // TODO?
}
this.checkResult(value, checked, fullkey);
fullkey.pop();
}
}
}
render() {
var cells = [];
for (var [key, value] of Object.entries(this.obj)) {
if (typeof value === 'function') {
// TODO: find other values, functions embedded in objects?
}
else {
var cell = { id: key, object: value };
cells.push(cell);
}
}
return cells;
}
extractErrors(e) {
let loc = e['loc'];
if (loc && loc.start && loc.end) {
return [{
path: this.path,
msg: e.message,
line: loc.start.line,
start: loc.start.column,
end: loc.end.line,
}];
}
if (loc && loc.line != null) {
return [{
path: this.path,
msg: e.message,
line: loc.line,
start: loc.column,
}];
}
// TODO: Cannot parse given Error object?
let frames = ErrorStackParser.parse(e);
let frame = frames.findIndex(f => f.functionName === 'anonymous');
let errors = [];
// if ErrorStackParser fails, resort to regex
if (frame < 0 && e.stack != null) {
let m = /.anonymous.:(\d+):(\d+)/g.exec(e.stack);
if (m != null) {
errors.push({
path: this.path,
msg: e.message,
line: parseInt(m[1]) - LINE_NUMBER_OFFSET,
});
}
}
// otherwise iterate thru all the frames
while (frame >= 0) {
console.log(frames[frame]);
if (frames[frame].fileName.endsWith('Function')) {
// TODO: use source map
errors.push({
path: this.path,
msg: e.message,
line: frames[frame].lineNumber - LINE_NUMBER_OFFSET,
//start: frames[frame].columnNumber,
});
}
--frame;
}
// if no stack frames parsed, last resort error msg
if (errors.length == 0) {
errors.push({
path: this.path,
msg: e.message,
line: 0
});
}
return errors;
}
commitLoadableState() {
// TODO: visit children?
for (let [key, value] of Object.entries(this.obj)) {
let loadable = value;
io.data.save(loadable, key);
}
return io.$$getData();
}
}
exports.Environment = Environment;
//# sourceMappingURL=env.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,452 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertWordsToImages = exports.font = exports.png = exports.montage = exports.analyze = exports.decode = exports.indexed = exports.rgba = exports.IndexedBitmap = exports.MappedBitmap = exports.RGBABitmap = exports.AbstractBitmap = void 0;
// TODO: dynamic import
const fastpng = __importStar(require("fast-png"));
const color_1 = require("./color");
const io = __importStar(require("./io"));
const color = __importStar(require("./color"));
const util_1 = require("../../util");
class AbstractBitmap {
constructor(width, height) {
this.width = width;
this.height = height;
this.style = {}; // CSS styles (TODO: other elements?)
}
inbounds(x, y) {
return (x >= 0 && x < this.width && y >= 0 && y < this.height);
}
assign(fn) {
if (typeof fn === 'function') {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
this.set(x, y, fn(x, y));
}
}
}
else if (fn && fn['length'] != null) {
this.setarray(fn);
}
else {
throw new Error(`Illegal argument to assign(): ${fn}`);
}
}
clone() {
let bmp = this.blank(this.width, this.height);
bmp.assign((x, y) => this.get(x, y));
return bmp;
}
crop(srcx, srcy, width, height) {
let dest = this.blank(width, height);
dest.assign((x, y) => this.get(x + srcx, y + srcy));
return dest;
}
blit(src, destx, desty, srcx, srcy) {
destx |= 0;
desty |= 0;
srcx |= 0;
srcy |= 0;
for (var y = 0; y < src.height; y++) {
for (var x = 0; x < src.width; x++) {
let rgba = src.getrgba(x + srcx, y + srcy);
this.set(x + destx, y + desty, rgba);
}
}
}
fill(destx, desty, width, height, value) {
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
this.set(x + destx, y + desty, value);
}
}
}
}
exports.AbstractBitmap = AbstractBitmap;
class RGBABitmap extends AbstractBitmap {
constructor(width, height, initial) {
super(width, height);
this.rgba = new Uint32Array(this.width * this.height);
if (initial)
this.assign(initial);
}
setarray(arr) {
this.rgba.set(arr);
}
set(x, y, rgba) {
if (this.inbounds(x, y))
this.rgba[y * this.width + x] = rgba;
}
get(x, y) {
return this.inbounds(x, y) ? this.rgba[y * this.width + x] : 0;
}
getrgba(x, y) {
return this.get(x, y);
}
blank(width, height) {
return new RGBABitmap(width || this.width, height || this.height);
}
clone() {
let bitmap = this.blank(this.width, this.height);
bitmap.rgba.set(this.rgba);
return bitmap;
}
}
exports.RGBABitmap = RGBABitmap;
class MappedBitmap extends AbstractBitmap {
constructor(width, height, bpp, initial) {
super(width, height);
this.bpp = bpp;
if (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8)
throw new Error(`Invalid bits per pixel: ${bpp}`);
this.pixels = new Uint8Array(this.width * this.height);
if (initial)
this.assign(initial);
}
setarray(arr) {
this.pixels.set(arr);
}
set(x, y, index) {
if (this.inbounds(x, y))
this.pixels[y * this.width + x] = index;
}
get(x, y) {
return this.inbounds(x, y) ? this.pixels[y * this.width + x] : 0;
}
}
exports.MappedBitmap = MappedBitmap;
function getbpp(x) {
if (typeof x === 'number')
return x;
if (x instanceof color_1.Palette) {
if (x.colors.length <= 2)
return 1;
else if (x.colors.length <= 4)
return 2;
else if (x.colors.length <= 16)
return 4;
}
return 8;
}
class IndexedBitmap extends MappedBitmap {
constructor(width, height, bppOrPalette, initial) {
super(width, height, getbpp(bppOrPalette), initial);
this.palette = bppOrPalette instanceof color_1.Palette
? bppOrPalette
: color.palette.colors(1 << this.bpp);
}
getrgba(x, y) {
return this.palette && this.palette.colors[this.get(x, y)];
}
blank(width, height, newPalette) {
let bitmap = new IndexedBitmap(width || this.width, height || this.height, newPalette || this.palette);
return bitmap;
}
clone() {
let bitmap = this.blank(this.width, this.height);
bitmap.pixels.set(this.pixels);
return bitmap;
}
}
exports.IndexedBitmap = IndexedBitmap;
function rgba(width, height, initial) {
return new RGBABitmap(width, height, initial);
}
exports.rgba = rgba;
function indexed(width, height, bpp, initial) {
return new IndexedBitmap(width, height, bpp, initial);
}
exports.indexed = indexed;
// TODO: check arguments
function decode(arr, fmt) {
var pixels = convertWordsToImages(arr, fmt);
// TODO: guess if missing w/h/count?
// TODO: reverse mapping
// TODO: maybe better composable functions
let bpp = (fmt.bpp || 1) * (fmt.np || 1);
return pixels.map(data => new IndexedBitmap(fmt.w, fmt.h, bpp, data));
}
exports.decode = decode;
function analyze(bitmaps) {
bitmaps = (0, util_1.coerceToArray)(bitmaps);
let r = { min: { w: 0, h: 0 }, max: { w: 0, h: 0 } };
for (let bmp of bitmaps) {
if (!(bmp instanceof AbstractBitmap))
return null;
r.min.w = Math.min(bmp.width);
r.max.w = Math.max(bmp.width);
r.min.h = Math.min(bmp.height);
r.max.h = Math.max(bmp.height);
}
return r;
}
exports.analyze = analyze;
function montage(bitmaps, options) {
bitmaps = (0, util_1.coerceToArray)(bitmaps);
let minmax = (options && options.analysis) || analyze(bitmaps);
if (minmax == null)
throw new Error(`Expected an array of bitmaps`);
let hitrects = [];
let aspect = (options && options.aspect) || 1;
let gap = (options && options.gap) || 0;
if (minmax.min.w == minmax.max.w && minmax.min.h == minmax.max.h) {
let totalPixels = minmax.min.w * minmax.min.h * bitmaps.length;
let factors = (0, util_1.findIntegerFactors)(totalPixels, minmax.max.w, minmax.max.h, aspect);
let columns = Math.ceil(factors.a / minmax.min.w); // TODO: rounding?
let rows = Math.ceil(factors.b / minmax.min.h);
let result = new RGBABitmap(factors.a + gap * (columns - 1), factors.b + gap * (rows - 1));
let x = 0;
let y = 0;
bitmaps.forEach((bmp) => {
result.blit(bmp, x, y, 0, 0);
hitrects.push({ x, y, w: bmp.width, h: bmp.height });
x += bmp.width + gap;
if (x >= result.width) {
x = 0;
y += bmp.height + gap;
}
});
return result;
}
else {
throw new Error(`combine() only supports uniformly-sized images right now`); // TODO
}
}
exports.montage = montage;
/////
var png;
(function (png_1) {
function read(url) {
return decode(io.readbin(url));
}
png_1.read = read;
function decode(data) {
let png = fastpng.decode(data);
return convertToBitmap(png);
}
png_1.decode = decode;
function convertToBitmap(png) {
if (png.palette && png.depth <= 8) {
return convertIndexedToBitmap(png);
}
else {
return convertRGBAToBitmap(png);
}
}
function convertIndexedToBitmap(png) {
var palarr = png.palette;
var palette = new color_1.Palette(palarr);
let bitmap = new IndexedBitmap(png.width, png.height, png.depth);
if (png.depth == 8) {
bitmap.pixels.set(png.data);
}
else {
let pixperbyte = Math.floor(8 / png.depth);
let mask = (1 << png.depth) - 1;
for (let i = 0; i < bitmap.pixels.length; i++) {
var bofs = (i % pixperbyte) * png.depth;
let val = png.data[Math.floor(i / pixperbyte)];
bitmap.pixels[i] = (val >> bofs) & mask;
}
}
bitmap.palette = palette;
// TODO: aspect etc
return bitmap;
}
function convertRGBAToBitmap(png) {
const bitmap = new RGBABitmap(png.width, png.height);
const rgba = [0, 0, 0, 0];
for (let i = 0; i < bitmap.rgba.length; i++) {
for (let j = 0; j < 4; j++)
rgba[j] = png.data[i * 4 + j];
bitmap.rgba[i] = color.rgba(rgba);
}
// TODO: aspect etc
return bitmap;
}
})(png = exports.png || (exports.png = {}));
var font;
(function (font) {
class Glyph extends IndexedBitmap {
constructor(width, height, bpp, code, yoffset) {
super(width, height, bpp);
this.code = code;
this.yoffset = yoffset;
}
}
function read(url) {
if (url.endsWith('.yaff'))
return decodeyafflines(io.readlines(url));
if (url.endsWith('.draw'))
return decodedrawlines(io.readlines(url));
throw new Error(`Can't figure out font format for "${url}"`);
}
font.read = read;
function decodeglyph(glines, curcode, yoffset) {
let width = 0;
for (var gline of glines)
width = Math.max(width, gline.length);
let g = new Glyph(width, glines.length, 1, curcode, yoffset);
for (var y = 0; y < glines.length; y++) {
let gline = glines[y];
for (var x = 0; x < gline.length; x++) {
let ch = gline[x];
g.set(x, y, ch === '@' || ch === '#' ? 1 : 0); // TODO: provide mapping
}
}
return g;
}
font.decodeglyph = decodeglyph;
// https://github.com/robhagemans/monobit
function decodeyafflines(lines) {
let maxheight = 0;
let properties = {};
let glyphs = {};
let yoffset = 0;
let curcode = -1;
let curglyph = [];
const re_prop = /^([\w-]+):\s+(.+)/i;
const re_label = /^0x([0-9a-f]+):|u[+]([0-9a-f]+):|(\w+):/i;
const re_gline = /^\s+([.@]+)/;
function addfont() {
if (curcode >= 0 && curglyph.length) {
glyphs[curcode] = decodeglyph(curglyph, curcode, yoffset);
curcode = -1;
curglyph = [];
}
}
for (let line of lines) {
let m;
if (m = re_prop.exec(line)) {
properties[m[1]] = m[2];
if (m[1] === 'bottom')
yoffset = parseInt(m[2]);
if (m[1] === 'size')
maxheight = parseInt(m[2]);
}
else if (m = re_label.exec(line)) {
addfont();
if (m[1] != null)
curcode = parseInt(m[1], 16);
else if (m[2] != null)
curcode = parseInt(m[2], 16);
else if (m[3] != null)
curcode = null; // text labels not supported
}
else if (m = re_gline.exec(line)) {
curglyph.push(m[1]);
}
if (isNaN(curcode + yoffset + maxheight))
throw new Error(`couldn't decode .yaff: ${JSON.stringify(line)}`);
}
addfont();
return { maxheight, properties, glyphs };
}
font.decodeyafflines = decodeyafflines;
// https://github.com/robhagemans/monobit
function decodedrawlines(lines) {
let maxheight = 0;
let properties = {};
let glyphs = {};
let curcode = -1;
let curglyph = [];
const re_gline = /^([0-9a-f]+)?[:]?\s*([-#]+)/i;
function addfont() {
if (curcode >= 0 && curglyph.length) {
glyphs[curcode] = decodeglyph(curglyph, curcode, 0);
maxheight = Math.max(maxheight, curglyph.length);
curcode = -1;
curglyph = [];
}
}
for (let line of lines) {
let m;
if (m = re_gline.exec(line)) {
if (m[1] != null) {
addfont();
curcode = parseInt(m[1], 16);
if (isNaN(curcode))
throw new Error(`couldn't decode .draw: ${JSON.stringify(line)}`);
}
curglyph.push(m[2]);
}
}
addfont();
return { maxheight, properties, glyphs };
}
font.decodedrawlines = decodedrawlines;
})(font = exports.font || (exports.font = {}));
function remapBits(x, arr) {
if (!arr)
return x;
var y = 0;
for (var i = 0; i < arr.length; i++) {
var s = arr[i];
if (s < 0) {
s = -s - 1;
y ^= 1 << s;
}
if (x & (1 << i)) {
y ^= 1 << s;
}
}
return y;
}
function convertWordsToImages(words, fmt) {
var width = fmt.w;
var height = fmt.h;
var count = fmt.count || 1;
var bpp = fmt.bpp || 1;
var nplanes = fmt.np || 1;
var bitsperword = fmt.bpw || 8;
var wordsperline = fmt.sl || Math.ceil(width * bpp / bitsperword);
var mask = (1 << bpp) - 1;
var pofs = fmt.pofs || wordsperline * height * count;
var skip = fmt.skip || 0;
var images = [];
for (var n = 0; n < count; n++) {
var imgdata = [];
for (var y = 0; y < height; y++) {
var yp = fmt.flip ? height - 1 - y : y;
var ofs0 = n * wordsperline * height + yp * wordsperline;
var shift = 0;
for (var x = 0; x < width; x++) {
var color = 0;
var ofs = remapBits(ofs0, fmt.remap);
// TODO: if (fmt.reindex) { [ofs, shift] = reindexMask(x, fmt.reindex); ofs += ofs0; }
for (var p = 0; p < nplanes; p++) {
var byte = words[ofs + p * pofs + skip];
color |= ((fmt.brev ? byte >> (bitsperword - shift - bpp) : byte >> shift) & mask) << (p * bpp);
}
imgdata.push(color);
shift += bpp;
if (shift >= bitsperword && !fmt.reindex) {
ofs0 += 1;
shift = 0;
}
}
}
images.push(new Uint8Array(imgdata));
}
return images;
}
exports.convertWordsToImages = convertWordsToImages;
//# sourceMappingURL=bitmap.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,164 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.palette = exports.rgb2arr = exports.rgba2arr = exports.rgba = exports.rgb = exports.from = exports.chroma = exports.Palette = exports.isChroma = exports.isPalette = void 0;
const chroma_js_1 = __importDefault(require("chroma-js"));
const util_1 = require("../../util");
function checkCount(count) {
if (count < 0 || count > 65536) {
throw new Error("Palettes cannot have more than 2^16 (65536) colors.");
}
}
function isPalette(object) {
return object['colors'] instanceof Uint32Array;
}
exports.isPalette = isPalette;
function isChroma(object) {
return object['_rgb'] instanceof Array;
}
exports.isChroma = isChroma;
class Palette {
constructor(arg) {
// TODO: more array types
if (typeof arg === 'number') {
checkCount(arg);
this.colors = new Uint32Array(arg);
}
else if (arg instanceof Uint32Array) {
this.colors = new Uint32Array(arg);
}
else if ((0, util_1.isArray)(arg)) {
this.colors = new Uint32Array(arg.map(rgb));
}
else
throw new Error(`Invalid Palette constructor`);
}
get(index) {
return this.colors[index];
}
chromas() {
return Array.from(this.colors).map((rgba) => from(rgba & 0xffffff));
}
}
exports.Palette = Palette;
exports.chroma = chroma_js_1.default;
function from(obj) {
if (typeof obj === 'number')
return (0, chroma_js_1.default)((0, util_1.rgb2bgr)(obj & 0xffffff));
else
return (0, chroma_js_1.default)(obj);
}
exports.from = from;
function rgb(obj, g, b) {
return rgba(obj, g, b, 0xff) | 0xff000000;
}
exports.rgb = rgb;
function rgba(obj, g, b, a) {
if (isChroma(obj)) {
return rgba(obj._rgb[0], obj._rgb[1], obj._rgb[2], obj._rgb[3]);
}
if (typeof obj === 'number') {
let r = obj;
if (typeof g === 'number' && typeof b === 'number')
return ((r & 0xff) << 0) | ((g & 0xff) << 8) | ((b & 0xff) << 16) | ((a & 0xff) << 24);
else
return obj;
}
if (typeof obj !== 'string' && (0, util_1.isArray)(obj) && typeof obj[0] === 'number') {
let arr = obj;
let v = 0;
v |= (arr[0] & 0xff) << 0;
v |= (arr[1] & 0xff) << 8;
v |= (arr[2] & 0xff) << 16;
v |= (arr[3] & 0xff) << 24;
return v;
}
return rgba(from(obj).rgb());
}
exports.rgba = rgba;
function rgba2arr(v) {
return [
(v >> 0) & 0xff,
(v >> 8) & 0xff,
(v >> 16) & 0xff,
(v >> 24) & 0xff,
];
}
exports.rgba2arr = rgba2arr;
function rgb2arr(v) {
return rgba2arr(v).slice(0, 3);
}
exports.rgb2arr = rgb2arr;
var palette;
(function (palette) {
function from(obj, count) {
checkCount(count);
if (typeof obj === 'function') {
if (!count)
throw new Error(`You must also pass the number of colors to generate.`);
var pal = new Palette(count);
for (var i = 0; i < pal.colors.length; i++) {
pal.colors[i] = rgba(obj(i));
}
return pal;
}
else {
return new Palette(obj);
}
}
palette.from = from;
function mono() {
return greys(2);
}
palette.mono = mono;
function rgb2() {
return new Palette([
rgb(0, 0, 0),
rgb(0, 0, 255),
rgb(255, 0, 0),
rgb(0, 255, 0),
]);
}
function rgb3() {
return new Palette([
rgb(0, 0, 0),
rgb(0, 0, 255),
rgb(255, 0, 0),
rgb(255, 0, 255),
rgb(0, 255, 0),
rgb(0, 255, 255),
rgb(255, 255, 0),
rgb(255, 255, 255),
]);
}
function greys(count) {
return from((i) => {
let v = 255 * i / (count - 1);
return rgb(v, v, v);
}, count);
}
palette.greys = greys;
function colors(count) {
switch (count) {
case 2: return mono();
case 4: return rgb2();
case 8: return rgb3();
default: return factors(count); // TODO
}
}
palette.colors = colors;
function helix(count) {
checkCount(count);
return new Palette(exports.chroma.cubehelix().scale().colors(count));
}
palette.helix = helix;
function factors(count, mult) {
mult = mult || 0x031f0f;
return from((i) => rgb(i * mult), count);
}
palette.factors = factors;
// TODO: https://www.iquilezles.org/www/articles/palettes/palettes.htm
})(palette = exports.palette || (exports.palette = {}));
//# sourceMappingURL=color.js.map

File diff suppressed because one or more lines are too long

183
gen/common/script/lib/io.js Normal file
View File

@@ -0,0 +1,183 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mutable = exports.Mutable = exports.module = exports.splitlines = exports.readlines = exports.readbin = exports.read = exports.readnocache = exports.fetchurl = exports.canonicalurl = exports.IOWaitError = exports.data = exports.$$loadData = exports.$$getData = exports.$$setupFS = void 0;
const util_1 = require("../../util");
// remote resource cache
var $$cache = new util_1.FileDataCache(); // TODO: better cache?
// file read/write interface
var $$store;
// backing store for data
var $$data = {};
// module cache
var $$modules = new Map();
function $$setupFS(store) {
$$store = store;
}
exports.$$setupFS = $$setupFS;
function $$getData() {
return $$data;
}
exports.$$getData = $$getData;
function $$loadData(data) {
Object.assign($$data, data);
}
exports.$$loadData = $$loadData;
var data;
(function (data) {
function load(object, key) {
if (object == null)
return object;
let override = $$data && $$data[key];
if (override && object.$$setstate) {
object.$$setstate(override);
}
else if (override) {
Object.assign(object, override);
}
return object;
}
data.load = load;
function save(object, key) {
if ($$data && object && object.$$getstate) {
$$data[key] = object.$$getstate();
}
return object;
}
data.save = save;
function get(key) {
return $$data && $$data[key];
}
data.get = get;
function set(key, value) {
if ($$data) {
$$data[key] = value;
}
}
data.set = set;
})(data = exports.data || (exports.data = {}));
class IOWaitError extends Error {
}
exports.IOWaitError = IOWaitError;
function canonicalurl(url) {
// get raw resource URL for github
if (url.startsWith('https://github.com/')) {
let toks = url.split('/');
if (toks[5] === 'blob') {
return `https://raw.githubusercontent.com/${toks[3]}/${toks[4]}/${toks.slice(6).join('/')}`;
}
}
return url;
}
exports.canonicalurl = canonicalurl;
function fetchurl(url, type) {
// TODO: only works in web worker
var xhr = new XMLHttpRequest();
xhr.responseType = type === 'text' ? 'text' : 'arraybuffer';
xhr.open("GET", url, false); // synchronous request
xhr.send(null);
if (xhr.response != null && xhr.status == 200) {
if (type === 'text') {
return xhr.response;
}
else {
return new Uint8Array(xhr.response);
}
}
else {
throw new Error(`The resource at "${url}" responded with status code of ${xhr.status}.`);
}
}
exports.fetchurl = fetchurl;
function readnocache(url, type) {
if (url.startsWith('http:') || url.startsWith('https:')) {
return fetchurl(url, type);
}
if ($$store) {
return $$store.getFileData(url);
}
}
exports.readnocache = readnocache;
// TODO: read files too
function read(url, type) {
// canonical-ize url
url = canonicalurl(url);
// check cache first
let cachekey = url;
let data = $$cache.get(cachekey);
if (data != null)
return data;
// not in cache, read it
data = readnocache(url, type);
if (data == null)
throw new Error(`Cannot find resource "${url}"`);
if (type === 'text' && typeof data !== 'string')
throw new Error(`Resource "${url}" is not a string`);
if (type === 'binary' && !(data instanceof Uint8Array))
throw new Error(`Resource "${url}" is not a binary file`);
// store in cache
$$cache.put(cachekey, data);
return data;
}
exports.read = read;
function readbin(url) {
var data = read(url, 'binary');
if (data instanceof Uint8Array)
return data;
else
throw new Error(`The resource at "${url}" is not a binary file.`);
}
exports.readbin = readbin;
function readlines(url) {
return read(url, 'text').split('\n');
}
exports.readlines = readlines;
function splitlines(text) {
return text.split(/\n|\r\n/g);
}
exports.splitlines = splitlines;
function module(url) {
// find module in cache?
let key = `${url}::${url.length}`;
let exports = $$modules.get(key);
if (exports == null) {
let code = readnocache(url, 'text');
let func = new Function('exports', 'module', code);
let module = {}; // TODO?
exports = {};
func(exports, module);
$$modules.set(key, exports);
}
return exports;
}
exports.module = module;
///
// TODO: what if this isn't top level?
class Mutable {
constructor(initial) {
this.value = initial;
}
$$setstate(newstate) {
this.value = newstate.value;
}
$$getstate() {
return { value: this.value };
}
}
exports.Mutable = Mutable;
function mutable(obj) {
Object.defineProperty(obj, '$$setstate', {
value: function (newstate) {
Object.assign(this, newstate);
},
enumerable: false
});
Object.defineProperty(obj, '$$getstate', {
value: function () {
return this;
},
enumerable: false
});
return obj;
}
exports.mutable = mutable;
//# sourceMappingURL=io.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,94 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.file = exports.COutputFile = exports.OutputFile = void 0;
var DataType;
(function (DataType) {
DataType[DataType["unknown"] = 0] = "unknown";
DataType[DataType["u8"] = 1] = "u8";
DataType[DataType["s8"] = 2] = "s8";
DataType[DataType["u16"] = 3] = "u16";
DataType[DataType["s16"] = 4] = "s16";
DataType[DataType["u32"] = 5] = "u32";
DataType[DataType["s32"] = 6] = "s32";
DataType[DataType["f32"] = 7] = "f32";
DataType[DataType["f64"] = 8] = "f64";
})(DataType || (DataType = {}));
;
function getArrayDataType(value) {
if (value instanceof Uint8Array) {
return DataType.u8;
}
else if (value instanceof Int8Array) {
return DataType.s8;
}
else if (value instanceof Uint16Array) {
return DataType.u16;
}
else if (value instanceof Int16Array) {
return DataType.s16;
}
else if (value instanceof Uint32Array) {
return DataType.u32;
}
else if (value instanceof Int32Array) {
return DataType.s32;
}
else if (value instanceof Float32Array) {
return DataType.f32;
}
else if (value instanceof Float64Array) {
return DataType.f64;
}
}
class OutputFile {
constructor(path, decls) {
this.path = path;
this.decls = decls;
}
toString() {
return Object.entries(this.decls).map(entry => this.declToText(entry[0], entry[1])).join('\n\n');
}
}
exports.OutputFile = OutputFile;
class COutputFile extends OutputFile {
toString() {
return `#include <stdint.h>\n\n${super.toString()}\n`;
}
dataTypeToString(dtype) {
switch (dtype) {
case DataType.u8: return 'uint8_t';
case DataType.s8: return 'int8_t';
case DataType.u16: return 'uint16_t';
case DataType.s16: return 'int16_t';
case DataType.u32: return 'uint32_t';
case DataType.s32: return 'int32_t';
case DataType.f32: return 'float';
case DataType.f64: return 'double';
default:
throw new Error('Cannot convert data type'); // TODO
}
}
valueToString(value, atype) {
// TODO: round, check value
return value + "";
}
declToText(label, value) {
if (Array.isArray(value) || value['BYTES_PER_ELEMENT']) {
let atype = getArrayDataType(value);
if (atype != null) {
let dtypestr = this.dataTypeToString(atype);
let dtext = value.map(elem => this.valueToString(elem, atype)).join(',');
let len = value.length;
return `${dtypestr} ${label}[${len}] = { ${dtext} };`;
}
}
throw new Error(`Cannot convert array "${label}"`); // TODO
}
}
exports.COutputFile = COutputFile;
// TODO: header file, auto-detect tool?
function file(path, decls) {
return new COutputFile(path, decls);
}
exports.file = file;
//# sourceMappingURL=output.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../../src/common/script/lib/output.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,171 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.key = exports.ScriptUIShortcut = exports.toggle = exports.ScriptUIToggle = exports.button = exports.ScriptUIButton = exports.ScriptUIButtonType = exports.select = exports.ScriptUISelect = exports.ScriptUISelectType = exports.slider = exports.ScriptUISlider = exports.ScriptUISliderType = exports.interact = exports.isInteractive = exports.InteractionRecord = exports.EVENT_KEY = void 0;
const io = __importStar(require("./io"));
// sequence counter
var $$seq = 0;
// if an event is specified, it goes here
exports.EVENT_KEY = "$$event";
// InteractionRecord maps a target object to an interaction ID
// the $$callback is used once per script eval, then gets nulled
// whether or not it's invoked
// event comes from $$data.$$event
class InteractionRecord {
constructor(interacttarget, $$callback) {
this.$$callback = $$callback;
this.lastevent = null;
this.interacttarget = interacttarget || this;
this.interactid = ++$$seq;
}
$$setstate(newstate) {
this.interactid = newstate.interactid;
this.interacttarget.$$interact = this;
let event = io.data.get(exports.EVENT_KEY);
if (event && event.interactid == this.interactid) {
if (this.$$callback) {
this.$$callback(event);
}
this.lastevent = event;
io.data.set(exports.EVENT_KEY, null);
}
this.$$callback = null;
}
$$getstate() {
//TODO: this isn't always cleared before we serialize (e.g. if exception or move element)
//and we do it in checkResult() too
this.$$callback = null;
return { interactid: this.interactid };
}
}
exports.InteractionRecord = InteractionRecord;
function isInteractive(obj) {
return !!(obj.$$interact);
}
exports.isInteractive = isInteractive;
function interact(object, callback) {
// TODO: limit to Bitmap, etc
if (typeof object === 'object') {
return new InteractionRecord(object, callback);
}
throw new Error(`This object is not capable of interaction.`);
}
exports.interact = interact;
class ScriptUISliderType {
constructor(min, max, step) {
this.min = min;
this.max = max;
this.step = step;
this.uitype = 'slider';
this.value = min;
}
}
exports.ScriptUISliderType = ScriptUISliderType;
class ScriptUISlider extends ScriptUISliderType {
initial(value) {
this.value = value;
return this;
}
$$getstate() {
return { value: this.value };
}
}
exports.ScriptUISlider = ScriptUISlider;
function slider(min, max, step) {
return new ScriptUISlider(min, max, step || 1);
}
exports.slider = slider;
///
class ScriptUISelectType {
constructor(options) {
this.options = options;
this.uitype = 'select';
this.index = 0;
this.value = this.options[this.index];
}
}
exports.ScriptUISelectType = ScriptUISelectType;
class ScriptUISelect extends ScriptUISelectType {
initial(index) {
this.index = index;
this.value = this.options[index];
return this;
}
$$getstate() {
return { value: this.value, index: this.index };
}
}
exports.ScriptUISelect = ScriptUISelect;
function select(options) {
return new ScriptUISelect(options);
}
exports.select = select;
///
class ScriptUIButtonType extends InteractionRecord {
constructor(label, callback) {
super(null, callback);
this.label = label;
this.uitype = 'button';
this.$$interact = this;
}
}
exports.ScriptUIButtonType = ScriptUIButtonType;
class ScriptUIButton extends ScriptUIButtonType {
}
exports.ScriptUIButton = ScriptUIButton;
function button(name, callback) {
return new ScriptUIButton(name, callback);
}
exports.button = button;
class ScriptUIToggle extends ScriptUIButton {
// share with InteractionRecord
$$getstate() {
let state = super.$$getstate();
state.enabled = this.enabled;
return state;
}
$$setstate(newstate) {
this.enabled = newstate.enabled;
super.$$setstate(newstate);
}
}
exports.ScriptUIToggle = ScriptUIToggle;
function toggle(name) {
return new ScriptUIToggle(name, function (e) {
this.enabled = !this.enabled;
});
}
exports.toggle = toggle;
///
class ScriptUIShortcut extends InteractionRecord {
constructor(key, callback) {
super(null, callback);
this.key = key;
this.uitype = 'shortcut';
this.$$interact = this;
}
}
exports.ScriptUIShortcut = ScriptUIShortcut;
function key(key, callback) {
return new ScriptUIShortcut(key, callback);
}
exports.key = key;
//# sourceMappingURL=scriptui.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"scriptui.js","sourceRoot":"","sources":["../../../../src/common/script/lib/scriptui.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,470 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Notebook = void 0;
const preact_1 = require("preact");
const env_1 = require("../env");
const util_1 = require("../../util");
const emu_1 = require("../../emu");
const ui_1 = require("../../../ide/ui");
const color = __importStar(require("../lib/color"));
const scriptui = __importStar(require("../lib/scriptui"));
const MAX_STRING_LEN = 100;
const DEFAULT_ASPECT = 1;
function sendInteraction(iobj, type, event, xtraprops) {
let irec = iobj.$$interact;
let ievent = Object.assign({ interactid: irec.interactid, type }, xtraprops);
if (event instanceof PointerEvent) {
const canvas = event.target;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (event.clientX - rect.left) * scaleX;
const y = (event.clientY - rect.top) * scaleY;
ievent.x = Math.floor(x);
ievent.y = Math.floor(y);
// TODO: pressure, etc.
}
else {
console.log("Unknown event type", event);
}
// TODO: add events to queue?
ui_1.current_project.updateDataItems([{
key: scriptui.EVENT_KEY,
value: ievent
}]);
}
class ColorComponent extends preact_1.Component {
render(virtualDom, containerNode, replaceNode) {
let rgb = this.props.rgbavalue & 0xffffff;
let bgr = (0, util_1.rgb2bgr)(rgb);
var htmlcolor = `#${(0, util_1.hex)(bgr, 6)}`;
var textcolor = (rgb & 0x008000) ? '#222' : '#ddd';
var printcolor = (0, util_1.hex)(rgb & 0xffffff, 6); // TODO: show index instead?
return (0, preact_1.h)('div', {
class: 'scripting-item scripting-color',
style: `background-color: ${htmlcolor}; color: ${textcolor}`,
alt: htmlcolor, // TODO
}, [
//h('span', { }, printcolor )
]);
}
}
class BitmapComponent extends preact_1.Component {
constructor(props) {
super(props);
this.ref = (0, preact_1.createRef)(); // TODO: can we use the ref?
this.pressed = false;
}
render(virtualDom, containerNode, replaceNode) {
let props = {
class: 'scripting-item',
ref: this.ref,
width: this.props.width,
height: this.props.height,
style: this.props.bitmap.style
};
let obj = this.props.bitmap;
if (scriptui.isInteractive(obj)) {
return (0, preact_1.h)('canvas', Object.assign({ onPointerMove: (e) => {
sendInteraction(obj, 'move', e, { pressed: this.pressed });
}, onPointerDown: (e) => {
this.pressed = true;
this.canvas.setPointerCapture(e.pointerId);
sendInteraction(obj, 'down', e, { pressed: true });
}, onPointerUp: (e) => {
this.pressed = false;
sendInteraction(obj, 'up', e, { pressed: false });
}, onPointerOut: (e) => {
this.pressed = false;
sendInteraction(obj, 'out', e, { pressed: false });
} }, props));
}
else {
return (0, preact_1.h)('canvas', props);
}
}
componentDidMount() {
this.refresh();
}
componentWillUnmount() {
this.canvas = null;
this.imageData = null;
this.datau32 = null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
this.refresh();
}
prepare() {
this.canvas = this.base;
this.ctx = this.canvas.getContext('2d');
this.imageData = this.ctx.createImageData(this.canvas.width, this.canvas.height);
this.datau32 = new Uint32Array(this.imageData.data.buffer);
}
refresh() {
// preact can reuse this component but it can change shape :^P
if (this.canvas !== this.base
|| this.imageData.width != this.props.width
|| this.imageData.height != this.props.height) {
this.prepare();
}
this.updateCanvas(this.datau32, this.props.bitmap);
this.ctx.putImageData(this.imageData, 0, 0);
}
updateCanvas(vdata, bmp) {
if (bmp['palette']) {
this.updateCanvasIndexed(vdata, bmp);
}
if (bmp['rgba']) {
this.updateCanvasRGBA(vdata, bmp);
}
}
updateCanvasRGBA(vdata, bmp) {
vdata.set(bmp.rgba);
}
updateCanvasIndexed(vdata, bmp) {
let pal = bmp.palette.colors;
for (var i = 0; i < bmp.pixels.length; i++) {
vdata[i] = pal[bmp.pixels[i]];
}
}
}
class ObjectKeyValueComponent extends preact_1.Component {
render(virtualDom, containerNode, replaceNode) {
let expandable = typeof this.props.object === 'object';
let hdrclass = '';
if (expandable)
hdrclass = this.state.expanded ? 'tree-expanded' : 'tree-collapsed';
let propName = this.props.name || null;
return (0, preact_1.h)('div', {
class: 'tree-content',
key: `${this.props.objpath}__tree`
}, [
(0, preact_1.h)('div', {
class: 'tree-header ' + hdrclass,
onClick: expandable ? () => this.toggleExpand() : null
}, [
propName != null ? (0, preact_1.h)('span', { class: 'tree-key' }, [propName, expandable]) : null,
(0, preact_1.h)('span', { class: 'tree-value scripting-item' }, [
getShortName(this.props.object)
])
]),
this.state.expanded ? objectToContentsDiv(this.props.object, this.props.objpath) : null
]);
}
toggleExpand() {
this.setState({ expanded: !this.state.expanded });
}
}
function getShortName(object) {
if (typeof object === 'object') {
try {
var s = object[env_1.PROP_CONSTRUCTOR_NAME] || Object.getPrototypeOf(object).constructor.name;
if (object.length > 0) {
s += `[${object.length}]`;
}
return s;
}
catch (e) {
console.log(e);
return e + "";
}
}
else {
return primitiveToString(object);
}
}
function primitiveToString(obj) {
var text = "";
// is it a function? call it first, if we are expanded
// TODO: only call functions w/ signature
if (obj && obj.$$ && typeof obj.$$ == 'function' && this._content != null) {
obj = obj.$$();
}
// check null first
if (obj == null) {
text = obj + "";
// primitive types
}
else if (typeof obj == 'number') {
if (obj != (obj | 0))
text = obj.toString(); // must be a float
else
text = obj + "\t($" + (0, util_1.hex)(obj) + ")";
}
else if (typeof obj == 'string') {
text = obj;
}
else {
text = JSON.stringify(obj);
}
if (text.length > MAX_STRING_LEN)
text = text.substring(0, MAX_STRING_LEN) + "...";
return text;
}
function isIndexedBitmap(object) {
return object['bpp'] && object['pixels'] && object['palette'];
}
function isRGBABitmap(object) {
return object['rgba'] instanceof Uint32Array;
}
function objectToChildren(object) {
if (color.isPalette(object)) {
return new color.Palette(object.colors).chromas();
}
else if ((0, util_1.isArray)(object)) {
return Array.from(object);
}
else if (object != null) {
return [object];
}
else {
return [];
}
}
function objectToChild(object, index) {
if (color.isPalette(object)) {
return color.from(object.colors[index]);
}
else if ((0, util_1.isArray)(object)) {
return object[index];
}
else if (object != null) {
return object;
}
else {
return null;
}
}
function objectToDiv(object, name, objpath) {
// don't view any keys that start with "$"
if (name && name.startsWith("$")) {
// don't view any values that start with "$$"
if (name.startsWith("$$"))
return;
// don't print if null or undefined
if (object == null)
return;
// don't print key in any case
name = null;
}
// TODO: limit # of items
// TODO: detect table
if (object == null) {
return (0, preact_1.h)('span', {}, object + "");
}
else if (object['uitype']) {
let cons = UI_COMPONENTS[object['uitype']];
if (!cons)
throw new Error(`Unknown UI component type: ${object['uitype']}`);
return (0, preact_1.h)(cons, { iokey: objpath, uiobject: object });
}
else if (object['literaltext']) {
return (0, preact_1.h)("pre", {}, [object['literaltext']]);
}
else if (isIndexedBitmap(object) || isRGBABitmap(object)) {
return (0, preact_1.h)(BitmapComponent, { bitmap: object, width: object.width, height: object.height });
}
else if (color.isChroma(object)) {
return (0, preact_1.h)(ColorComponent, { rgbavalue: color.rgb(object) });
}
else if (color.isPalette(object)) {
// TODO?
if (object.colors.length <= 256) {
let children = [];
let props = { class: '', key: `${objpath}__obj` };
props.class += ' scripting-flex ';
object.colors.forEach((val) => {
children.push((0, preact_1.h)(ColorComponent, { rgbavalue: val }));
});
return (0, preact_1.h)('div', props, children);
}
else {
let { a, b } = (0, util_1.findIntegerFactors)(object.colors.length, 1, 1, DEFAULT_ASPECT);
return objectToDiv({ rgba: object.colors, width: a, height: b }, name, objpath);
}
}
else {
return (0, preact_1.h)(ObjectKeyValueComponent, { name, object, objpath }, []);
}
}
function fixedArrayToDiv(tyarr, bpel, objpath) {
const maxBytes = 0x100;
if (tyarr.length <= maxBytes) {
// TODO
let dumptext = (0, emu_1.dumpRAM)(tyarr, 0, tyarr.length);
return (0, preact_1.h)('pre', {}, dumptext);
}
else {
let children = [];
for (var ofs = 0; ofs < tyarr.length; ofs += maxBytes) {
children.push(objectToDiv(tyarr.slice(ofs, ofs + maxBytes), '$' + (0, util_1.hex)(ofs), `${objpath}.${ofs}`));
}
return (0, preact_1.h)('div', {}, children);
}
}
function objectToContentsDiv(object, objpath) {
// is typed array?
let bpel = object['BYTES_PER_ELEMENT'];
if (typeof bpel === 'number') {
return fixedArrayToDiv(object, bpel, objpath);
}
let objectEntries = Object.entries(object);
let objectDivs = objectEntries.map(entry => objectToDiv(entry[1], entry[0], `${objpath}.${entry[1]}`));
return (0, preact_1.h)('div', { class: 'scripting-flex' }, objectDivs);
}
class UISliderComponent extends preact_1.Component {
render(virtualDom, containerNode, replaceNode) {
let slider = this.props.uiobject;
return (0, preact_1.h)('div', {}, [
this.props.iokey,
(0, preact_1.h)('input', {
type: 'range',
min: slider.min / slider.step,
max: slider.max / slider.step,
value: slider.value / slider.step,
onInput: (ev) => {
let newUIValue = { value: parseFloat(ev.target.value) * slider.step };
this.setState(this.state);
ui_1.current_project.updateDataItems([{ key: this.props.iokey, value: newUIValue }]);
}
}),
(0, preact_1.h)('span', {}, getShortName(slider.value)),
]);
}
}
class UISelectComponent extends preact_1.Component {
constructor() {
super(...arguments);
this.ref = (0, preact_1.createRef)();
}
render(virtualDom, containerNode, replaceNode) {
let select = this.props.uiobject;
let children = objectToChildren(select.options);
this.props.dropdown = children.length > 16;
let showselections = !this.props.dropdown || this.state.expanded;
let seldiv = null;
if (showselections) {
seldiv = (0, preact_1.h)('div', {
class: 'scripting-select scripting-flex',
ref: this.ref,
onClick: (e) => {
// select object -- iterate parents until we find select div, then find index of child
let target = e.target;
while (target.parentElement && target.parentElement != this.ref.current) {
target = target.parentElement;
}
if (target.parentElement) {
const selindex = Array.from(target.parentElement.children).indexOf(target);
if (selindex >= 0 && selindex < children.length) {
let newUIValue = { value: children[selindex], index: selindex };
this.setState({ expanded: false });
ui_1.current_project.updateDataItems([{ key: this.props.iokey, value: newUIValue }]);
}
else {
throw new Error(`Could not find click target of ${this.props.iokey}`);
}
}
}
}, children.map((child, index) => {
let div = objectToDiv(child, null, `${this.props.iokey}__select_${index}`);
let selected = (index == select.index);
return (0, preact_1.h)('div', { class: selected ? 'scripting-selected' : '' }, [div]);
}));
}
if (this.props.dropdown) {
let selectedDiv = objectToDiv(objectToChild(select.options, select.index), null, `${this.props.iokey}__selected`);
return (0, preact_1.h)('div', {
class: 'tree-content',
key: `${this.props.iokey}__tree`
}, [
(0, preact_1.h)('div', {
class: 'tree-header ' + (this.state.expanded ? 'tree-expanded' : 'tree-collapsed'),
onClick: () => this.toggleExpand()
}, [
this.props.iokey,
(0, preact_1.h)('span', { class: 'tree-value scripting-item' }, [
selectedDiv,
//getShortName(select.options)
])
]),
seldiv
]);
}
else {
return seldiv;
}
}
toggleExpand() {
this.setState({ expanded: !this.state.expanded });
}
}
class UIButtonComponent extends preact_1.Component {
render(virtualDom, containerNode, replaceNode) {
let button = this.props.uiobject;
return (0, preact_1.h)('button', {
class: button.enabled ? 'scripting-button scripting-enabled' : 'scripting-button',
onClick: (e) => {
sendInteraction(button, 'click', e, {});
},
}, [
button.label
]);
}
}
class UIShortcutComponent extends preact_1.Component {
render(virtualDom, containerNode, replaceNode) {
let shortcut = this.props.uiobject;
// TODO: needs to fire on container node
return (0, preact_1.h)('div', {
onKeyDown: (e) => {
sendInteraction(shortcut, 'key', e, {});
},
}, []);
}
}
const UI_COMPONENTS = {
'slider': UISliderComponent,
'select': UISelectComponent,
'button': UIButtonComponent,
'shortcut': UIShortcutComponent,
};
///
class Notebook {
constructor(maindoc, maindiv) {
this.maindoc = maindoc;
this.maindiv = maindiv;
maindiv.classList.add('vertical-scroll');
}
updateCells(cells) {
let hTree = cells.map(cell => {
return (0, preact_1.h)('div', {
class: 'scripting-cell',
key: `${cell.id}__cell`
}, [
objectToDiv(cell.object, cell.id, cell.id)
]);
});
(0, preact_1.render)(hTree, this.maindiv);
}
}
exports.Notebook = Notebook;
//# sourceMappingURL=notebook.js.map

File diff suppressed because one or more lines are too long

314
gen/common/teletype.js Normal file
View File

@@ -0,0 +1,314 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TeleTypeWithKeyboard = exports.TeleType = void 0;
class TeleType {
constructor(page, fixed) {
this.ncols = 80;
this.page = page;
this.fixed = fixed;
this.clear();
}
clear() {
this.curline = null;
this.curstyle = 0;
this.reverse = false;
this.col = 0;
this.row = -1;
this.lines = [];
this.ncharsout = 0;
$(this.page).empty();
this.showPrintHead(true);
}
ensureline() {
if (this.curline == null) {
this.curline = this.lines[++this.row];
if (this.curline == null) {
this.curline = $('<div class="transcript-line"/>')[0];
this.page.appendChild(this.curline);
this.lines[this.row] = this.curline;
this.scrollToBottom();
}
}
}
flushline() {
this.curline = null;
this.col = 0;
this.movePrintHead(false);
}
addtext(line, style) {
this.ensureline();
if (line.length) {
// in fixed mode, only do characters
if (this.fixed && line.length > 1) {
for (var i = 0; i < line.length; i++)
this.addtext(line[i], style);
return;
}
// process control codes
if (line.length == 1) {
var ch = line.charCodeAt(0);
switch (ch) {
case 7:
if (this.bell)
this.bell.play();
break;
case 8:
if (this.col > 0)
this.col--;
break;
case 12:
this.formfeed();
break;
case 13:
this.col = 0;
break;
}
if (ch < 32)
return; // ignore non-printables
}
var span = $("<span/>").text(line);
for (var i = 0; i < 8; i++) {
if (style & (1 << i))
span.addClass("transcript-style-" + (1 << i));
}
if (this.reverse)
span.addClass("transcript-reverse");
//span.data('vmip', this.vm.pc);
// in fixed mode, we can overwrite individual characters
if (this.fixed && line.length == 1 && this.col < this.curline.childNodes.length) {
this.curline.replaceChild(span[0], this.curline.childNodes[this.col]);
}
else {
span.appendTo(this.curline);
}
this.col += line.length;
// wrap @ 80 columns (TODO: const)
if (this.fixed && this.col >= this.ncols)
this.flushline();
this.ncharsout += line.length;
this.movePrintHead(true);
}
}
newline() {
this.flushline();
this.ensureline();
}
print(val) {
// split by newlines
var lines = val.split("\n");
for (var i = 0; i < lines.length; i++) {
if (i > 0)
this.newline();
this.addtext(lines[i], this.curstyle);
}
}
move_cursor(col, row) {
if (!this.fixed)
return; // fixed windows only
// ensure enough row elements
while (this.lines.length <= row) {
this.flushline();
this.ensureline();
}
// select row element
this.curline = this.lines[row];
this.row = row;
// get children in row (individual text cells)
var children = $(this.curline).children();
// add whitespace to line?
if (children.length > col) {
this.col = col;
}
else {
while (this.col < col)
this.addtext(' ', this.curstyle);
}
}
setrows(size) {
if (!this.fixed)
return; // fixed windows only
// truncate rows?
var allrows = $(this.page).children();
if (allrows.length > size) {
this.flushline();
allrows.slice(size).remove();
this.lines = this.lines.slice(0, size);
//this.move_cursor(0,0);
}
}
formfeed() {
for (var i = 0; i < 60; i++) {
this.newline();
this.ensureline();
}
}
scrollToBottom() {
this.curline.scrollIntoView();
}
movePrintHead(printing) {
/*
var ph = $("#printhead"); // TODO: speed?
var x = $(this.page).position().left + this.col * ($(this.page).width() / 80) - 200;
ph.stop().animate({left: x}, {duration:20});
//ph.offset({left: x});
if (printing) ph.addClass("printing");
else ph.removeClass("printing");
*/
}
showPrintHead(show) {
/*
var ph = $("#printhead"); // TODO: speed?
if (show) ph.show(); else ph.hide();
*/
}
resize(columns) {
// set font size proportional to window width
var charwidth = $(this.page).width() * 1.6 / columns;
$(this.page).css('font-size', charwidth + 'px');
this.scrollToBottom();
}
saveState() {
return {
curstyle: this.curstyle,
reverse: this.reverse,
col: this.col,
row: this.row,
ncharsout: this.ncharsout,
lines: this.lines.map((line) => line.cloneNode(true)),
};
}
loadState(state) {
this.curstyle = state.curstyle;
this.reverse = state.reverse;
this.col = state.col;
this.row = state.row;
this.ncharsout = state.ncharsout;
$(this.page).empty();
for (var i = 0; i < state.lines.length; i++) {
this.page.appendChild(state.lines[i]);
}
this.lines = state.lines;
this.curline = state.lines[this.row];
}
}
exports.TeleType = TeleType;
class TeleTypeWithKeyboard extends TeleType {
constructor(page, fixed, input) {
super(page, fixed);
this.msecPerLine = 100; // IBM 1403
this.keepinput = true;
this.keephandler = true;
this.uppercaseOnly = false;
this.splitInput = false;
this.focused = true;
this.scrolling = 0;
this.input = input;
this.input.onkeypress = (e) => {
this.sendkey(e);
};
this.input.onfocus = (e) => {
this.focused = true;
console.log('inputline gained focus');
};
$("#workspace").on('click', (e) => {
this.focused = false;
console.log('inputline lost focus');
});
this.page.onclick = (e) => {
this.input.focus();
};
}
clear() {
super.clear();
this.hideinput();
this.waitingfor = null;
}
focusinput() {
this.ensureline();
this.showPrintHead(false);
// don't steal focus while editing
if (this.keepinput)
$(this.input).css('visibility', 'visible');
else
$(this.input).appendTo(this.curline).show()[0];
// scroll to bottom
this.scrollToBottom();
// refocus?
if (this.focused) {
$(this.input).focus();
}
// change size
if (this.waitingfor == 'char')
$(this.input).addClass('transcript-input-char');
else
$(this.input).removeClass('transcript-input-char');
this.lastInputRequestTime = Date.now();
}
hideinput() {
this.showPrintHead(true);
if (this.keepinput)
$(this.input).css('visibility', 'hidden');
else
$(this.input).appendTo($(this.page).parent()).hide();
}
clearinput() {
this.input.value = '';
this.waitingfor = null;
}
cancelinput() {
this.sendinput('');
}
sendkey(e) {
if (this.waitingfor == 'line') {
if (e.key == "Enter") {
this.sendinput(this.input.value.toString());
}
}
else if (this.waitingfor == 'char') {
this.sendchar(e.keyCode);
e.preventDefault();
}
}
sendinput(s) {
if (this.resolveInput) {
var elapsed = Date.now() - this.lastInputRequestTime;
if (this.uppercaseOnly)
s = s.toUpperCase(); // TODO: always uppercase?
this.addtext(s, 4);
this.flushline();
this.clearinput();
this.hideinput(); // keep from losing input handlers
var vals = this.splitInput ? s.split(',') : null;
this.resolveInput({ line: s, vals: vals, elapsed: elapsed / 1000 });
if (!this.keephandler)
this.resolveInput = null;
}
}
sendchar(code) {
this.sendinput(String.fromCharCode(code));
}
ensureline() {
if (!this.keepinput)
$(this.input).hide();
super.ensureline();
}
scrollToBottom() {
if (this.scrolldiv) {
this.scrolling++;
var top = $(this.page).height() + $(this.input).height();
$(this.scrolldiv).stop().animate({ scrollTop: top }, this.msecPerLine, 'swing', () => {
this.scrolling = 0;
this.ncharsout = 0;
});
}
else {
this.input.scrollIntoView();
}
}
isBusy() {
// stop execution when scrolling and printing non-newlines
return this.scrolling > 0 && this.ncharsout > 0;
}
}
exports.TeleTypeWithKeyboard = TeleTypeWithKeyboard;
//# sourceMappingURL=teletype.js.map

File diff suppressed because one or more lines are too long

49
gen/common/toolbar.js Normal file
View File

@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Toolbar = void 0;
const Mousetrap = require("mousetrap");
/// TOOLBAR
class Toolbar {
constructor(parentDiv, focusDiv) {
this.boundkeys = [];
this.mousetrap = focusDiv ? new Mousetrap(focusDiv) : Mousetrap;
this.span = $(document.createElement("span")).addClass("btn_toolbar");
parentDiv.appendChild(this.span[0]);
this.newGroup();
}
destroy() {
if (this.span) {
this.span.remove();
this.span = null;
}
if (this.mousetrap) {
for (var key of this.boundkeys) {
this.mousetrap.unbind(key);
}
this.mousetrap = null;
}
}
newGroup() {
return this.grp = $(document.createElement("span")).addClass("btn_group").appendTo(this.span).hide();
}
add(key, alttext, icon, fn) {
var btn = null;
if (icon) {
btn = $(document.createElement("button")).addClass("btn");
if (icon.startsWith('glyphicon')) {
icon = '<span class="glyphicon ' + icon + '" aria-hidden="true"></span>';
}
btn.html(icon);
btn.prop("title", key ? (alttext + " (" + key + ")") : alttext);
btn.click(fn);
this.grp.append(btn).show();
}
if (key) {
this.mousetrap.bind(key, fn);
this.boundkeys.push(key);
}
return btn;
}
}
exports.Toolbar = Toolbar;
//# sourceMappingURL=toolbar.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../../src/common/toolbar.ts"],"names":[],"mappings":";;;AACA,uCAAwC;AAExC,WAAW;AAEX,MAAa,OAAO;IAMhB,YAAY,SAAqB,EAAE,QAAoB;QAFvD,cAAS,GAAG,EAAE,CAAC;QAGb,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACtE,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IACD,OAAO;QACL,IAAI,IAAI,CAAC,IAAI,EAAE;YACb,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SAClB;QACD,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE;gBAC9B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;aAC5B;YACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;SACvB;IACH,CAAC;IACD,QAAQ;QACN,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACvG,CAAC;IACD,GAAG,CAAC,GAAU,EAAE,OAAc,EAAE,IAAW,EAAE,EAAoB;QAC/D,IAAI,GAAG,GAAG,IAAI,CAAC;QACf,IAAI,IAAI,EAAE;YACR,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC1D,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE;gBAChC,IAAI,GAAG,yBAAyB,GAAG,IAAI,GAAG,8BAA8B,CAAC;aAC1E;YACD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAC,IAAI,GAAC,GAAG,GAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC1D,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACd,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;SAC7B;QACD,IAAI,GAAG,EAAE;YACP,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAC1B;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CAEF;AA9CH,0BA8CG"}

700
gen/common/util.js Normal file
View File

@@ -0,0 +1,700 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.coerceToArray = exports.FileDataCache = exports.findIntegerFactors = exports.escapeHTML = exports.parseXMLPoorly = exports.XMLParseError = exports.parseBool = exports.decodeQueryString = exports.loadScript = exports.byteToASCII = exports.convertDataToString = exports.convertDataToUint8Array = exports.isTypedArray = exports.isArray = exports.getRootBasePlatform = exports.getRootPlatform = exports.getBasePlatform = exports.getWithBinary = exports.rle_unpack = exports.safeident = exports.clamp = exports.RGBA = exports.rgb2bgr = exports.printFlags = exports.safe_extend = exports.compressLZG = exports.isProbablyBinary = exports.removeBOM = exports.byteArrayToUTF8 = exports.byteArrayToString = exports.stringToByteArray = exports.lzgmini = exports.highlightDifferences = exports.invertMap = exports.arrayCompare = exports.toradix = exports.tobin = exports.hex = exports.getFilenamePrefix = exports.getFolderForPath = exports.getFilenameForPath = exports.byte2signed = exports.rpad = exports.lpad = void 0;
function lpad(s, n) {
s += ''; // convert to string
while (s.length < n)
s = " " + s;
return s;
}
exports.lpad = lpad;
function rpad(s, n) {
s += ''; // convert to string
while (s.length < n)
s += " ";
return s;
}
exports.rpad = rpad;
function byte2signed(b) {
b &= 0xff;
return (b < 0x80) ? b : -(256 - b);
}
exports.byte2signed = byte2signed;
function getFilenameForPath(s) {
var toks = s.split('/');
return toks[toks.length - 1];
}
exports.getFilenameForPath = getFilenameForPath;
function getFolderForPath(s) {
return s.substring(0, s.lastIndexOf('/'));
}
exports.getFolderForPath = getFolderForPath;
function getFilenamePrefix(s) {
var pos = s.lastIndexOf('.');
return (pos > 0) ? s.substr(0, pos) : s;
}
exports.getFilenamePrefix = getFilenamePrefix;
function hex(v, nd) {
if (!nd)
nd = 2;
if (nd == 8) {
return hex((v >> 16) & 0xffff, 4) + hex(v & 0xffff, 4);
}
else {
return toradix(v, nd, 16);
}
}
exports.hex = hex;
function tobin(v, nd) {
if (!nd)
nd = 8;
return toradix(v, nd, 2);
}
exports.tobin = tobin;
function toradix(v, nd, radix) {
try {
var s = v.toString(radix).toUpperCase();
while (s.length < nd)
s = "0" + s;
return s;
}
catch (e) {
return v + "";
}
}
exports.toradix = toradix;
function arrayCompare(a, b) {
if (a == null && b == null)
return true;
if (a == null)
return false;
if (b == null)
return false;
if (a.length != b.length)
return false;
for (var i = 0; i < a.length; i++)
if (a[i] != b[i])
return false;
return true;
}
exports.arrayCompare = arrayCompare;
function invertMap(m) {
var r = {};
if (m) {
for (var k in m)
r[m[k]] = k;
}
return r;
}
exports.invertMap = invertMap;
function highlightDifferences(s1, s2) {
var split1 = s1.split(/(\S+\s+)/).filter(function (n) { return n; });
var split2 = s2.split(/(\S+\s+)/).filter(function (n) { return n; });
var i = 0;
var j = 0;
var result = "";
while (i < split1.length && j < split2.length) {
var w1 = split1[i];
var w2 = split2[j];
if (w2 && w2.indexOf("\n") >= 0) {
while (i < s1.length && split1[i].indexOf("\n") < 0)
i++;
}
if (w1 != w2) {
w2 = '<span class="hilite">' + w2 + '</span>';
}
result += w2;
i++;
j++;
}
while (j < split2.length) {
result += split2[j++];
}
return result;
}
exports.highlightDifferences = highlightDifferences;
function lzgmini() {
// Constants
var LZG_HEADER_SIZE = 16;
var LZG_METHOD_COPY = 0;
var LZG_METHOD_LZG1 = 1;
// LUT for decoding the copy length parameter
var LZG_LENGTH_DECODE_LUT = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 35, 48, 72, 128];
// Decoded data (produced by the decode() method)
var outdata = null;
// Calculate the checksum
var calcChecksum = function (data) {
var a = 1;
var b = 0;
var i = LZG_HEADER_SIZE;
while (i < data.length) {
a = (a + (data[i] & 0xff)) & 0xffff;
b = (b + a) & 0xffff;
i++;
}
return (b << 16) | a;
};
// Decode LZG coded data. The function returns the size of the decoded data.
// Use any of the get* methods to retrieve the decoded data.
this.decode = function (data) {
// Start by clearing the decompressed array in this object
outdata = null;
// Check magic ID
if ((data.length < LZG_HEADER_SIZE) || (data[0] != 76) ||
(data[1] != 90) || (data[2] != 71)) {
return null;
}
// what's the length?
var uncomplen = data[6] | (data[5] << 8) | (data[4] << 16) | (data[3] << 24);
// Calculate & check the checksum
var checksum = ((data[11] & 0xff) << 24) |
((data[12] & 0xff) << 16) |
((data[13] & 0xff) << 8) |
(data[14] & 0xff);
if (calcChecksum(data) != checksum) {
return null;
}
var dst = new Array();
// Check which method to use
var method = data[15] & 0xff;
if (method == LZG_METHOD_LZG1) {
// Get marker symbols
var m1 = data[16] & 0xff;
var m2 = data[17] & 0xff;
var m3 = data[18] & 0xff;
var m4 = data[19] & 0xff;
// Main decompression loop
var symbol, b, b2, b3, len, offset;
var dstlen = 0;
var k = LZG_HEADER_SIZE + 4;
var datalen = data.length;
while (k <= datalen) {
symbol = data[k++] & 0xff;
if ((symbol != m1) && (symbol != m2) && (symbol != m3) && (symbol != m4)) {
// Literal copy
dst[dstlen++] = symbol;
}
else {
b = data[k++] & 0xff;
if (b != 0) {
// Decode offset / length parameters
if (symbol == m1) {
// marker1 - "Distant copy"
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
b2 = data[k++] & 0xff;
b3 = data[k++] & 0xff;
offset = (((b & 0xe0) << 11) | (b2 << 8) | b3) + 2056;
}
else if (symbol == m2) {
// marker2 - "Medium copy"
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
b2 = data[k++] & 0xff;
offset = (((b & 0xe0) << 3) | b2) + 8;
}
else if (symbol == m3) {
// marker3 - "Short copy"
len = (b >> 6) + 3;
offset = (b & 63) + 8;
}
else {
// marker4 - "Near copy (incl. RLE)"
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
offset = (b >> 5) + 1;
}
// Copy the corresponding data from the history window
for (i = 0; i < len; i++) {
dst[dstlen] = dst[dstlen - offset];
dstlen++;
}
}
else {
// Literal copy (single occurance of a marker symbol)
dst[dstlen++] = symbol;
}
}
}
}
else if (method == LZG_METHOD_COPY) {
// Plain copy
var dstlen = 0;
var datalen = data.length;
for (var i = LZG_HEADER_SIZE; i < datalen; i++) {
dst[dstlen++] = data[i] & 0xff;
}
}
else {
// Unknown method
return null;
}
// Store the decompressed data in the lzgmini object for later retrieval
if (dst.length < uncomplen)
return null; // data too short
outdata = dst.slice(0, uncomplen);
return outdata;
};
// Get the decoded byte array
this.getByteArray = function () {
return outdata;
};
// Get the decoded string from a Latin 1 (or ASCII) encoded array
this.getStringLatin1 = function () {
return byteArrayToString(outdata);
};
// Get the decoded string from an UTF-8 encoded array
this.getStringUTF8 = function () {
return byteArrayToUTF8(outdata);
};
}
exports.lzgmini = lzgmini;
function stringToByteArray(s) {
var a = new Uint8Array(s.length);
for (var i = 0; i < s.length; i++)
a[i] = s.charCodeAt(i);
return a;
}
exports.stringToByteArray = stringToByteArray;
function byteArrayToString(data) {
var str = "";
if (data != null) {
var charLUT = new Array();
for (var i = 0; i < 256; ++i)
charLUT[i] = String.fromCharCode(i);
var len = data.length;
for (var i = 0; i < len; i++)
str += charLUT[data[i]];
}
return str;
}
exports.byteArrayToString = byteArrayToString;
function byteArrayToUTF8(data) {
var str = "";
var charLUT = new Array();
for (var i = 0; i < 128; ++i)
charLUT[i] = String.fromCharCode(i);
var c;
var len = data.length;
for (var i = 0; i < len;) {
c = data[i++];
if (c < 128) {
str += charLUT[c];
}
else {
if ((c >= 192) && (c < 224)) {
c = ((c & 31) << 6) | (data[i++] & 63);
}
else {
c = ((c & 15) << 12) | ((data[i] & 63) << 6) | (data[i + 1] & 63);
i += 2;
if (c == 0xfeff)
continue; // ignore BOM
}
str += String.fromCharCode(c);
}
}
return str;
}
exports.byteArrayToUTF8 = byteArrayToUTF8;
function removeBOM(s) {
if (s.charCodeAt(0) === 0xFEFF) {
s = s.substr(1);
}
return s;
}
exports.removeBOM = removeBOM;
function isProbablyBinary(path, data) {
var score = 0;
// check extensions
if (path) {
path = path.toUpperCase();
const BINEXTS = ['.CHR', '.BIN', '.DAT', '.PAL', '.NAM', '.RLE', '.LZ4', '.NSF'];
for (var ext of BINEXTS) {
if (path.endsWith(ext))
score++;
}
}
// decode as UTF-8
for (var i = 0; i < (data ? data.length : 0);) {
let c = data[i++];
if ((c & 0x80) == 0) {
// more likely binary if we see a NUL or obscure control character
if (c < 9 || (c >= 14 && c < 26) || c == 0x7f) {
score++;
break;
}
}
else {
// look for invalid unicode sequences
var nextra = 0;
if ((c & 0xe0) == 0xc0)
nextra = 1;
else if ((c & 0xf0) == 0xe0)
nextra = 2;
else if ((c & 0xf8) == 0xf0)
nextra = 3;
else if (c < 0xa0)
score++;
else if (c == 0xff)
score++;
while (nextra--) {
if (i >= data.length || (data[i++] & 0xc0) != 0x80) {
score++;
break;
}
}
}
}
return score > 0;
}
exports.isProbablyBinary = isProbablyBinary;
// need to load liblzg.js first
function compressLZG(em_module, inBuffer, levelArg) {
var level = levelArg || 9;
var inLen = inBuffer.length;
var inPtr = em_module._malloc(inLen + 1);
for (var i = 0; i < inLen; i++) {
em_module.setValue(inPtr + i, inBuffer[i], 'i8');
}
var maxEncSize = em_module._LZG_MaxEncodedSize(inLen);
var outPtr = em_module._malloc(maxEncSize + 1);
var compLen = em_module.ccall('compress_lzg', 'number', ['number', 'number', 'number', 'number', 'number'], [level, inPtr, inLen, maxEncSize, outPtr]);
em_module._free(inPtr);
var outBuffer = new Uint8Array(compLen);
for (var i = 0; i < compLen; i++) {
outBuffer[i] = em_module.getValue(outPtr + i, 'i8');
}
em_module._free(outPtr);
return outBuffer;
}
exports.compressLZG = compressLZG;
// only does primitives, 1D arrays and no recursion
function safe_extend(deep, dest, src) {
// TODO: deep ignored
for (var key in src) {
var val = src[key];
var type = typeof (val);
if (val === null || type == 'undefined') {
dest[key] = val;
}
else if (type == 'function') {
// ignore function
}
else if (type == 'object') {
if (val['slice']) { // array?
dest[key] = val.slice();
}
else {
// ignore object
}
}
else {
dest[key] = val;
}
}
return dest;
}
exports.safe_extend = safe_extend;
function printFlags(val, names, r2l) {
var s = '';
for (var i = 0; i < names.length; i++) {
if (names[i]) {
var bit = 1 << (r2l ? (names.length - 1 - i) : i);
if (i > 0)
s += " ";
s += (val & bit) ? names[i] : "-";
}
}
return s;
}
exports.printFlags = printFlags;
function rgb2bgr(x) {
return ((x & 0xff) << 16) | ((x >> 16) & 0xff) | (x & 0x00ff00);
}
exports.rgb2bgr = rgb2bgr;
function RGBA(r, g, b) {
return (r & 0xff) | ((g & 0xff) << 8) | ((b & 0xff) << 16) | 0xff000000;
}
exports.RGBA = RGBA;
function clamp(minv, maxv, v) {
return (v < minv) ? minv : (v > maxv) ? maxv : v;
}
exports.clamp = clamp;
function safeident(s) {
return s.replace(/\W+/g, "_");
}
exports.safeident = safeident;
function rle_unpack(src) {
var i = 0;
var tag = src[i++];
var dest = [];
var data = tag;
while (i < src.length) {
var ch = src[i++];
if (ch == tag) {
var count = src[i++];
for (var j = 0; j < count; j++)
dest.push(data);
if (count == 0)
break;
}
else {
data = ch;
dest.push(data);
}
}
return new Uint8Array(dest);
}
exports.rle_unpack = rle_unpack;
// firefox doesn't do GET with binary files
// TODO: replace with fetch()?
function getWithBinary(url, success, datatype) {
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = datatype;
oReq.onload = function (oEvent) {
if (oReq.status == 200) {
var data = oReq.response;
if (data instanceof ArrayBuffer) {
data = new Uint8Array(data);
}
success(data);
}
else if (oReq.status == 404) {
success(null);
}
else {
throw Error("Error " + oReq.status + " loading " + url);
}
};
oReq.onerror = function (oEvent) {
success(null);
};
oReq.ontimeout = function (oEvent) {
throw Error("Timeout loading " + url);
};
oReq.send(null);
}
exports.getWithBinary = getWithBinary;
// get platform ID without . emulator
function getBasePlatform(platform) {
return platform.split('.')[0];
}
exports.getBasePlatform = getBasePlatform;
// get platform ID without - specialization
function getRootPlatform(platform) {
return platform.split('-')[0];
}
exports.getRootPlatform = getRootPlatform;
// get platform ID without emulator or specialization
function getRootBasePlatform(platform) {
return getRootPlatform(getBasePlatform(platform));
}
exports.getRootBasePlatform = getRootBasePlatform;
function isArray(obj) {
return obj != null && (Array.isArray(obj) || isTypedArray(obj));
}
exports.isArray = isArray;
function isTypedArray(obj) {
return obj != null && obj['BYTES_PER_ELEMENT'];
}
exports.isTypedArray = isTypedArray;
function convertDataToUint8Array(data) {
return (typeof data === 'string') ? stringToByteArray(data) : data;
}
exports.convertDataToUint8Array = convertDataToUint8Array;
function convertDataToString(data) {
return (data instanceof Uint8Array) ? byteArrayToUTF8(data) : data;
}
exports.convertDataToString = convertDataToString;
function byteToASCII(b) {
if (b < 32)
return String.fromCharCode(b + 0x2400);
else
return String.fromCharCode(b);
}
exports.byteToASCII = byteToASCII;
function loadScript(scriptfn) {
return new Promise((resolve, reject) => {
var script = document.createElement('script');
script.onload = resolve;
script.onerror = reject;
script.src = scriptfn;
document.getElementsByTagName('head')[0].appendChild(script);
});
}
exports.loadScript = loadScript;
function decodeQueryString(qs) {
if (qs.startsWith('?'))
qs = qs.substr(1);
var a = qs.split('&');
if (!a || a.length == 0)
return {};
var b = {};
for (var i = 0; i < a.length; ++i) {
var p = a[i].split('=', 2);
if (p.length == 1)
b[p[0]] = "";
else
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
return b;
}
exports.decodeQueryString = decodeQueryString;
function parseBool(s) {
if (!s)
return false;
if (s == 'false' || s == '0')
return false;
if (s == 'true' || s == '1')
return true;
return s ? true : false;
}
exports.parseBool = parseBool;
///
class XMLParseError extends Error {
}
exports.XMLParseError = XMLParseError;
function escapeXML(s) {
if (s.indexOf('&') >= 0) {
return s.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&gt;/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');
}
else {
return s;
}
}
function parseXMLPoorly(s, openfn, closefn) {
const tag_re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
const attr_re = /\s*(\w+)="(.*?)"\s*/gi;
var fm;
var stack = [];
var top;
function closetop() {
top = stack.pop();
if (top == null || top.type != ident)
throw new XMLParseError("mismatch close tag: " + ident);
if (closefn) {
top.obj = closefn(top);
}
if (stack.length == 0)
throw new XMLParseError("close tag without open: " + ident);
stack[stack.length - 1].children.push(top);
}
function parseattrs(as) {
var am;
var attrs = {};
if (as != null) {
while (am = attr_re.exec(as)) {
attrs[am[1]] = escapeXML(am[2]);
}
}
return attrs;
}
while (fm = tag_re.exec(s)) {
var [_m0, close, ident, attrs, content] = fm;
//console.log(stack.length, close, ident, attrs, content);
if (close) {
closetop();
}
else if (ident) {
var node = { type: ident, text: null, children: [], attrs: parseattrs(attrs), obj: null };
stack.push(node);
if (attrs) {
parseattrs(attrs);
}
if (openfn) {
node.obj = openfn(node);
}
if (attrs && attrs.endsWith('/'))
closetop();
}
else if (content != null) {
if (stack.length == 0)
throw new XMLParseError("content without element");
var txt = escapeXML(content).trim();
if (txt.length)
stack[stack.length - 1].text = txt;
}
}
if (stack.length != 1)
throw new XMLParseError("tag not closed");
if (stack[0].type != '?xml')
throw new XMLParseError("?xml needs to be first element");
return top;
}
exports.parseXMLPoorly = parseXMLPoorly;
function escapeHTML(s) {
return s.replace(/[&]/g, '&amp;').replace(/[<]/g, '&lt;').replace(/[>]/g, '&gt;');
}
exports.escapeHTML = escapeHTML;
// lame factorization for displaying bitmaps
// returns a > b such that a * b == x (or higher), a >= mina, b >= minb
function findIntegerFactors(x, mina, minb, aspect) {
let a = x;
let b = 1;
if (minb > 1 && minb < a) {
a = Math.ceil(x / minb);
b = minb;
}
while (a > b) {
let a2 = a;
let b2 = b;
if ((a & 1) == 0) {
b2 = b * 2;
a2 = a / 2;
}
if ((a % 3) == 0) {
b2 = b * 3;
a2 = a / 3;
}
if ((a % 5) == 0) {
b2 = b * 5;
a2 = a / 5;
}
if (a2 < mina)
break;
if (a2 < b2 * aspect)
break;
a = a2;
b = b2;
}
return { a, b };
}
exports.findIntegerFactors = findIntegerFactors;
class FileDataCache {
constructor() {
this.maxSize = 8000000;
this.reset();
}
get(key) {
return this.cache.get(key);
}
put(key, value) {
this.cache.set(key, value);
this.size += value.length;
if (this.size > this.maxSize) {
console.log('cache reset', this);
this.reset();
}
}
reset() {
this.cache = new Map();
this.size = 0;
}
}
exports.FileDataCache = FileDataCache;
function coerceToArray(arrobj) {
if (Array.isArray(arrobj))
return arrobj;
else if (arrobj != null && typeof arrobj[Symbol.iterator] === 'function')
return Array.from(arrobj);
else if (typeof arrobj === 'object')
return Array.from(Object.values(arrobj));
else
throw new Error(`Expected array or object, got "${arrobj}"`);
}
exports.coerceToArray = coerceToArray;
//# sourceMappingURL=util.js.map

1
gen/common/util.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,933 @@
"use strict";
/*
* js99'er - TI-99/4A emulator written in JavaScript
*
* Created 2014 by Rasmus Moustgaard <rasmus.moustgaard@gmail.com>
*
* TMS9918A VDP emulation.
*
* https://github.com/Rasmus-M/Js99er
* GNU General Public License v2.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SMSVDP = exports.TMS9918A = void 0;
const util_1 = require("../util");
const devices_1 = require("../devices");
var TMS9918A_Mode;
(function (TMS9918A_Mode) {
TMS9918A_Mode[TMS9918A_Mode["GRAPHICS"] = 0] = "GRAPHICS";
TMS9918A_Mode[TMS9918A_Mode["TEXT"] = 1] = "TEXT";
TMS9918A_Mode[TMS9918A_Mode["BITMAP"] = 2] = "BITMAP";
TMS9918A_Mode[TMS9918A_Mode["MULTICOLOR"] = 3] = "MULTICOLOR";
TMS9918A_Mode[TMS9918A_Mode["MODE4"] = 4] = "MODE4";
TMS9918A_Mode[TMS9918A_Mode["BITMAP_TEXT"] = 5] = "BITMAP_TEXT";
TMS9918A_Mode[TMS9918A_Mode["BITMAP_MULTICOLOR"] = 6] = "BITMAP_MULTICOLOR";
TMS9918A_Mode[TMS9918A_Mode["ILLEGAL"] = 7] = "ILLEGAL";
})(TMS9918A_Mode || (TMS9918A_Mode = {}));
;
class TMS9918A {
constructor(fb32, cru, enableFlicker) {
this.probe = new devices_1.NullProbe();
this.ram = new Uint8Array(16384); // VDP RAM
this.registers = new Uint8Array(8);
this.spriteBuffer = new Uint8Array(256);
this.displayOn = false;
this.interruptsOn = false;
this.fb32 = fb32;
this.cru = cru;
this.enableFlicker = enableFlicker;
this.palette = [
(0, util_1.RGBA)(0, 0, 0),
(0, util_1.RGBA)(0, 0, 0),
(0, util_1.RGBA)(33, 200, 66),
(0, util_1.RGBA)(94, 220, 120),
(0, util_1.RGBA)(84, 85, 237),
(0, util_1.RGBA)(125, 118, 252),
(0, util_1.RGBA)(212, 82, 77),
(0, util_1.RGBA)(66, 235, 245),
(0, util_1.RGBA)(252, 85, 84),
(0, util_1.RGBA)(255, 121, 120),
(0, util_1.RGBA)(212, 193, 84),
(0, util_1.RGBA)(230, 206, 128),
(0, util_1.RGBA)(33, 176, 59),
(0, util_1.RGBA)(201, 91, 186),
(0, util_1.RGBA)(204, 204, 204),
(0, util_1.RGBA)(255, 255, 255)
];
}
reset() {
var i;
this.ram.fill(0);
this.registers.fill(0);
this.addressRegister = 0;
this.statusRegister = 0;
this.prefetchByte = 0;
this.latch = false;
this.displayOn = false;
this.interruptsOn = false;
this.screenMode = TMS9918A_Mode.GRAPHICS;
this.bitmapMode = false;
this.textMode = false;
this.colorTable = 0;
this.nameTable = 0;
this.charPatternTable = 0;
this.spriteAttributeTable = 0;
this.spritePatternTable = 0;
this.colorTableMask = 0x3FFF;
this.patternTableMask = 0x3FFF;
this.ramMask = 0x3FFF;
this.fgColor = 0;
this.bgColor = 0;
this.flicker = this.enableFlicker;
this.redrawRequired = true;
this.width = 304;
this.height = 240;
}
drawScanline(y) {
var imageData = this.fb32, width = this.width, imageDataAddr = (y * width), screenMode = this.screenMode, textMode = this.textMode, bitmapMode = this.bitmapMode, drawWidth = !textMode ? 256 : 240, drawHeight = 192, hBorder = (width - drawWidth) >> 1, vBorder = (this.height - drawHeight) >> 1, fgColor = this.fgColor, bgColor = this.bgColor, ram = this.ram, nameTable = this.nameTable, colorTable = this.colorTable, charPatternTable = this.charPatternTable, colorTableMask = this.colorTableMask, patternTableMask = this.patternTableMask, spriteAttributeTable = this.spriteAttributeTable, spritePatternTable = this.spritePatternTable, spriteSize = (this.registers[1] & 0x2) !== 0, spriteMagnify = this.registers[1] & 0x1, spriteDimension = (spriteSize ? 16 : 8) << (spriteMagnify ? 1 : 0), maxSpritesOnLine = this.flicker ? 4 : 32, palette = this.palette, collision = false, fifthSprite = false, fifthSpriteIndex = 31, x, color, rgbColor, name, tableOffset, colorByte, patternByte;
if (y >= vBorder && y < vBorder + drawHeight && this.displayOn) {
var y1 = y - vBorder;
// Pre-process sprites
if (!textMode) {
var spriteBuffer = this.spriteBuffer;
spriteBuffer.fill(0);
var spritesOnLine = 0;
var endMarkerFound = false;
var spriteAttributeAddr = spriteAttributeTable;
var s;
for (s = 0; s < 32 && spritesOnLine <= maxSpritesOnLine && !endMarkerFound; s++) {
var sy = ram[spriteAttributeAddr];
if (sy !== 0xD0) {
if (sy > 0xD0) {
sy -= 256;
}
sy++;
var sy1 = sy + spriteDimension;
var y2 = -1;
if (s < 8 || !bitmapMode) {
if (y1 >= sy && y1 < sy1) {
y2 = y1;
}
}
else {
// Emulate sprite duplication bug
var yMasked = y1 & (((this.registers[4] & 0x03) << 6) | 0x3F);
if (yMasked >= sy && yMasked < sy1) {
y2 = yMasked;
}
else if (y1 >= 64 && y1 < 128 && y1 >= sy && y1 < sy1) {
y2 = y1;
}
}
if (y2 !== -1) {
if (spritesOnLine < maxSpritesOnLine) {
var sx = ram[spriteAttributeAddr + 1];
var sPatternNo = ram[spriteAttributeAddr + 2] & (spriteSize ? 0xFC : 0xFF);
var sColor = ram[spriteAttributeAddr + 3] & 0x0F;
if ((ram[spriteAttributeAddr + 3] & 0x80) !== 0) {
sx -= 32;
}
var sLine = (y2 - sy) >> spriteMagnify;
var sPatternBase = spritePatternTable + (sPatternNo << 3) + sLine;
for (var sx1 = 0; sx1 < spriteDimension; sx1++) {
var sx2 = sx + sx1;
if (sx2 >= 0 && sx2 < drawWidth) {
var sx3 = sx1 >> spriteMagnify;
var sPatternByte = ram[sPatternBase + (sx3 >= 8 ? 16 : 0)];
if ((sPatternByte & (0x80 >> (sx3 & 0x07))) !== 0) {
if (spriteBuffer[sx2] === 0) {
spriteBuffer[sx2] = sColor + 1;
}
else {
collision = true;
}
}
}
}
}
spritesOnLine++;
}
spriteAttributeAddr += 4;
}
else {
endMarkerFound = true;
}
}
if (spritesOnLine > 4) {
fifthSprite = true;
fifthSpriteIndex = s;
}
}
// Draw
var rowOffset = !textMode ? (y1 >> 3) << 5 : (y1 >> 3) * 40;
var lineOffset = y1 & 7;
for (x = 0; x < width; x++) {
if (x >= hBorder && x < hBorder + drawWidth) {
var x1 = x - hBorder;
// Tiles
switch (screenMode) {
case TMS9918A_Mode.GRAPHICS:
name = ram[nameTable + rowOffset + (x1 >> 3)];
colorByte = ram[colorTable + (name >> 3)];
patternByte = ram[charPatternTable + (name << 3) + lineOffset];
color = (patternByte & (0x80 >> (x1 & 7))) !== 0 ? (colorByte & 0xF0) >> 4 : colorByte & 0x0F;
break;
case TMS9918A_Mode.BITMAP:
name = ram[nameTable + rowOffset + (x1 >> 3)];
tableOffset = ((y1 & 0xC0) << 5) + (name << 3);
colorByte = ram[colorTable + (tableOffset & colorTableMask) + lineOffset];
patternByte = ram[charPatternTable + (tableOffset & patternTableMask) + lineOffset];
color = (patternByte & (0x80 >> (x1 & 7))) !== 0 ? (colorByte & 0xF0) >> 4 : colorByte & 0x0F;
break;
case TMS9918A_Mode.MULTICOLOR:
name = ram[nameTable + rowOffset + (x1 >> 3)];
lineOffset = (y1 & 0x1C) >> 2;
patternByte = ram[charPatternTable + (name << 3) + lineOffset];
color = (x1 & 4) === 0 ? (patternByte & 0xF0) >> 4 : patternByte & 0x0F;
break;
case TMS9918A_Mode.TEXT:
name = ram[nameTable + rowOffset + Math.floor(x1 / 6)];
patternByte = ram[charPatternTable + (name << 3) + lineOffset];
color = (patternByte & (0x80 >> (x1 % 6))) !== 0 ? fgColor : bgColor;
break;
case TMS9918A_Mode.BITMAP_TEXT:
name = ram[nameTable + rowOffset + Math.floor(x1 / 6)];
tableOffset = ((y1 & 0xC0) << 5) + (name << 3);
patternByte = ram[charPatternTable + (tableOffset & patternTableMask) + lineOffset];
color = (patternByte & (0x80 >> (x1 % 6))) !== 0 ? fgColor : bgColor;
break;
case TMS9918A_Mode.BITMAP_MULTICOLOR:
name = ram[nameTable + rowOffset + (x1 >> 3)];
lineOffset = (y1 & 0x1C) >> 2;
tableOffset = ((y1 & 0xC0) << 5) + (name << 3);
patternByte = ram[charPatternTable + (tableOffset & patternTableMask) + lineOffset];
color = (x1 & 4) === 0 ? (patternByte & 0xF0) >> 4 : patternByte & 0x0F;
break;
case TMS9918A_Mode.ILLEGAL:
color = (x1 & 4) === 0 ? fgColor : bgColor;
break;
}
if (color === 0) {
color = bgColor;
}
// Sprites
if (!textMode) {
var spriteColor = spriteBuffer[x1] - 1;
if (spriteColor > 0) {
color = spriteColor;
}
}
}
else {
color = bgColor;
}
rgbColor = palette[color];
imageData[imageDataAddr++] = rgbColor;
}
}
// Top/bottom border
else {
rgbColor = palette[bgColor];
for (x = 0; x < width; x++) {
imageData[imageDataAddr++] = rgbColor;
}
}
if (y === vBorder + drawHeight) {
this.statusRegister |= 0x80;
if (this.interruptsOn) {
this.cru.setVDPInterrupt(true);
}
}
if (collision) {
this.statusRegister |= 0x20;
}
if ((this.statusRegister & 0x40) === 0) {
this.statusRegister |= fifthSpriteIndex;
}
if (fifthSprite) {
this.statusRegister |= 0x40;
}
}
setReadAddress(i) {
this.addressRegister = ((i & 0x3f) << 8) | (this.addressRegister & 0x00FF);
this.prefetchByte = this.ram[this.addressRegister++];
this.addressRegister &= 0x3FFF;
}
setWriteAddress(i) {
this.addressRegister = ((i & 0x3f) << 8) | (this.addressRegister & 0x00FF);
}
setVDPWriteRegister(i) {
var regmask = this.registers.length - 1;
this.registers[i & regmask] = this.addressRegister & 0x00FF;
switch (i & regmask) {
// Mode
case 0:
this.updateMode(this.registers[0], this.registers[1]);
break;
case 1:
this.ramMask = (this.registers[1] & 0x80) !== 0 ? 0x3FFF : 0x1FFF;
this.displayOn = (this.registers[1] & 0x40) !== 0;
this.interruptsOn = (this.registers[1] & 0x20) !== 0;
this.updateMode(this.registers[0], this.registers[1]);
break;
// Name table
case 2:
this.nameTable = (this.registers[2] & 0xf) << 10;
break;
// Color table
case 3:
if (this.bitmapMode) {
this.colorTable = (this.registers[3] & 0x80) << 6;
}
else {
this.colorTable = this.registers[3] << 6;
}
this.updateTableMasks();
break;
// Pattern table
case 4:
if (this.bitmapMode) {
this.charPatternTable = (this.registers[4] & 0x4) << 11;
}
else {
this.charPatternTable = (this.registers[4] & 0x7) << 11;
}
this.updateTableMasks();
break;
// Sprite attribute table
case 5:
this.spriteAttributeTable = (this.registers[5] & 0x7f) << 7;
break;
// Sprite pattern table
case 6:
this.spritePatternTable = (this.registers[6] & 0x7) << 11;
break;
// Background
case 7:
this.fgColor = (this.registers[7] & 0xf0) >> 4;
this.bgColor = this.registers[7] & 0x0f;
break;
}
// this.logRegisters();
// this.log.info("Name table: " + this.nameTable.toHexWord());
// this.log.info("Pattern table: " + this.charPatternTable.toHexWord());
}
setVDPWriteCommand3(i) {
this.setVDPWriteRegister(i);
}
writeAddress(i) {
if (!this.latch) {
this.addressRegister = (this.addressRegister & 0xFF00) | i;
}
else {
switch ((i & 0xc0) >> 6) {
// Set read address
case 0:
this.setReadAddress(i);
break;
// Set write address
case 1:
this.setWriteAddress(i);
break;
// Write register
case 2:
this.setVDPWriteRegister(i);
break;
// Color RAM (SMS only)
case 3:
this.setVDPWriteCommand3(i);
break;
}
this.redrawRequired = true;
}
this.latch = !this.latch;
}
updateMode(reg0, reg1) {
this.bitmapMode = (reg0 & 0x02) !== 0;
this.textMode = (reg1 & 0x10) !== 0;
// Check bitmap mode bit, not text or multicolor
if (this.bitmapMode) {
switch ((reg1 & 0x18) >> 3) {
case 0:
// Bitmap mode
this.screenMode = TMS9918A_Mode.BITMAP;
break;
case 1:
// Multicolor mode
this.screenMode = TMS9918A_Mode.BITMAP_MULTICOLOR;
break;
case 2:
// Text mode
this.screenMode = TMS9918A_Mode.BITMAP_TEXT;
break;
case 3:
// Illegal
this.screenMode = TMS9918A_Mode.ILLEGAL;
break;
}
}
else {
switch ((reg1 & 0x18) >> 3) {
case 0:
// Graphics mode 0
this.screenMode = TMS9918A_Mode.GRAPHICS;
break;
case 1:
// Multicolor mode
this.screenMode = TMS9918A_Mode.MULTICOLOR;
break;
case 2:
// Text mode
this.screenMode = TMS9918A_Mode.TEXT;
break;
case 3:
// Illegal
this.screenMode = TMS9918A_Mode.ILLEGAL;
break;
}
}
if (this.bitmapMode) {
this.colorTable = (this.registers[3] & 0x80) << 6;
this.charPatternTable = (this.registers[4] & 0x4) << 11;
this.updateTableMasks();
}
else {
this.colorTable = this.registers[3] << 6;
this.charPatternTable = (this.registers[4] & 0x7) << 11;
}
this.nameTable = (this.registers[2] & 0xf) << 10;
this.spriteAttributeTable = (this.registers[5] & 0x7f) << 7;
this.spritePatternTable = (this.registers[6] & 0x7) << 11;
}
updateTableMasks() {
if (this.screenMode === TMS9918A_Mode.BITMAP) {
this.colorTableMask = ((this.registers[3] & 0x7F) << 6) | 0x3F; // 000CCCCCCC111111
this.patternTableMask = ((this.registers[4] & 0x03) << 11) | (this.colorTableMask & 0x7FF); // 000PPCCCCC111111
// this.log.info("colorTableMask:" + this.colorTableMask);
// this.log.info("patternTableMask:" + this.patternTableMask);
}
else if (this.screenMode === TMS9918A_Mode.BITMAP_TEXT || this.screenMode === TMS9918A_Mode.BITMAP_MULTICOLOR) {
this.colorTableMask = this.ramMask;
this.patternTableMask = ((this.registers[4] & 0x03) << 11) | 0x7FF; // 000PP11111111111
}
else {
this.colorTableMask = this.ramMask;
this.patternTableMask = this.ramMask;
}
}
writeData(i) {
this.probe.logVRAMWrite(this.addressRegister, i);
this.ram[this.addressRegister++] = i;
this.prefetchByte = i;
this.addressRegister &= this.ramMask;
this.latch = false;
this.redrawRequired = true;
}
readStatus() {
var i = this.statusRegister;
this.statusRegister = 0x1F; // TODO: & 0x1f?
if (this.interruptsOn) {
this.cru.setVDPInterrupt(false);
}
this.latch = false;
return i;
}
readData() {
var i = this.prefetchByte;
this.prefetchByte = this.ram[this.addressRegister++];
this.probe.logVRAMRead(this.addressRegister - 1, this.prefetchByte);
this.addressRegister &= this.ramMask;
this.latch = false;
return i;
}
getRAM() {
return this.ram;
}
colorTableSize() {
if (this.screenMode === TMS9918A_Mode.GRAPHICS) {
return 0x20;
}
else if (this.screenMode === TMS9918A_Mode.BITMAP) {
return Math.min(this.colorTableMask + 1, 0x1800);
}
else {
return 0;
}
}
patternTableSize() {
if (this.bitmapMode) {
return Math.min(this.patternTableMask + 1, 0x1800);
}
else {
return 0x800;
}
}
getDebugTables() {
var tables = [
["Pattern Table", this.charPatternTable, this.patternTableSize()],
["Name Table", this.nameTable, 0x300],
["Color Table", this.colorTable, this.colorTableSize()],
["Sprite Patterns", this.spritePatternTable, 64 * 32],
["Sprite Attributes", this.spriteAttributeTable, 4 * 32],
];
return tables;
}
getRegsString() {
const w = 20;
var s = "Registers:";
for (var i = 0; i < this.registers.length; i++) {
s += " " + (0, util_1.hex)(this.registers[i], 2);
}
s += "\n\n";
var tables = this.getDebugTables();
for (var row of tables) {
if (row[2] > 0)
s += (0, util_1.lpad)(row[0], w) + ": $" + (0, util_1.hex)(row[1], 4) + " - $" + (0, util_1.hex)(row[1] + row[2] - 1, 4) + "\n";
}
s += (0, util_1.lpad)("Address Register", w) + ": $" + (0, util_1.hex)(this.addressRegister, 4) + "\n";
s += (0, util_1.lpad)("Status Register", w) + ": $" + (0, util_1.hex)(this.statusRegister, 2) + "\n";
s += (0, util_1.lpad)("Screen Mode", w) + ": " + this.screenMode + "\n";
s += (0, util_1.lpad)("Display On", w) + ": " + this.displayOn + "\n";
if (this.ramMask != 0x3fff)
s += (0, util_1.lpad)("RAM Mask", w) + ": $" + (0, util_1.hex)(this.ramMask) + "\n";
return s;
}
hexView(start, length, anchorAddr) {
var text = "";
var anchorLine = null;
var addr = start;
var line = 0;
for (var i = 0; i < length && addr < 0x4000; addr++, i++) {
if ((i & 0x000F) === 0) {
text += "\n" + (0, util_1.hex)(addr, 4) + ":";
line++;
}
text += " ";
if (anchorAddr && anchorAddr === addr) {
anchorLine = line;
}
var hx = this.ram[addr].toString(16).toUpperCase();
if (hx.length === 1) {
text += "0";
}
text += hx;
}
return { text: text.substr(1), lineCount: line, anchorLine: anchorLine - 1 };
}
getWord(addr) {
return addr < 0x4000 ? this.ram[addr] << 8 | this.ram[addr + 1] : 0;
}
getCharAt(x, y) {
x -= 24;
y -= 24;
if (!this.textMode) {
return this.ram[this.nameTable + Math.floor(x / 8) + Math.floor(y / 8) * 32];
}
else {
return this.ram[this.nameTable + Math.floor((x - 8) / 6) + Math.floor(y / 8) * 40];
}
}
setFlicker(value) {
this.flicker = value;
this.enableFlicker = value;
}
getState() {
return {
ram: this.ram.slice(0),
registers: this.registers.slice(0),
addressRegister: this.addressRegister,
statusRegister: this.statusRegister,
latch: this.latch,
prefetchByte: this.prefetchByte,
displayOn: this.displayOn,
interruptsOn: this.interruptsOn,
screenMode: this.screenMode,
bitmapMode: this.bitmapMode,
textMode: this.textMode,
colorTable: this.colorTable,
nameTable: this.nameTable,
charPatternTable: this.charPatternTable,
spriteAttributeTable: this.spriteAttributeTable,
spritePatternTable: this.spritePatternTable,
colorTableMask: this.colorTableMask,
patternTableMask: this.patternTableMask,
ramMask: this.ramMask,
fgColor: this.fgColor,
bgColor: this.bgColor,
flicker: this.flicker
};
}
restoreState(state) {
this.ram.set(state.ram);
this.registers.set(state.registers);
this.addressRegister = state.addressRegister;
this.statusRegister = state.statusRegister;
this.latch = state.latch;
this.prefetchByte = state.prefetchByte;
this.displayOn = state.displayOn;
this.interruptsOn = state.interruptsOn;
this.screenMode = state.screenMode;
this.bitmapMode = state.bitmapMode;
this.textMode = state.textMode;
this.colorTable = state.colorTable;
this.nameTable = state.nameTable;
this.charPatternTable = state.charPatternTable;
this.spriteAttributeTable = state.spriteAttributeTable;
this.spritePatternTable = state.spritePatternTable;
this.colorTableMask = state.colorTableMask;
this.patternTableMask = state.patternTableMask;
this.ramMask = state.ramMask;
this.fgColor = state.fgColor;
this.bgColor = state.bgColor;
this.flicker = state.flicker;
this.redrawRequired = true;
}
}
exports.TMS9918A = TMS9918A;
;
class SMSVDP extends TMS9918A {
constructor() {
super(...arguments);
this.cram = new Uint8Array(32); // color RAM
this.cpalette = new Uint32Array(32); // color RAM (RGBA)
this.registers = new Uint8Array(16); // 8 more registers (actually only 5)
this.vramUntwiddled = new Uint8Array(0x8000);
this.numVisibleLines = 192;
this.lineCounter = 0; // TODO: state
this.lineInterruptPending = false; // TODO: state
}
reset() {
super.reset();
this.writeToCRAM = false;
this.cram.fill(0);
this.cpalette.fill(0);
this.vramUntwiddled.fill(0);
}
readStatus() {
this.lineInterruptPending = false;
return super.readStatus();
}
updateMode(reg0, reg1) {
if (reg0 & 0x04) {
this.screenMode = TMS9918A_Mode.MODE4;
this.nameTable = ((this.registers[2] & 0xf) << 10) & 0x3800;
this.spriteAttributeTable = (this.registers[5] & 0x7e) << 7;
}
else {
super.updateMode(reg0, reg1);
}
}
setReadAddress(i) {
super.setReadAddress(i);
this.writeToCRAM = false;
}
setWriteAddress(i) {
super.setWriteAddress(i);
this.writeToCRAM = false;
}
setVDPWriteRegister(i) {
super.setVDPWriteRegister(i);
//this.writeToCRAM = false; // TODO?
this.ramMask = 0x3fff;
}
setVDPWriteCommand3(i) {
this.writeToCRAM = true;
//this.addressRegister &= 0x1f; // TODO?
}
writeData(i) {
if (this.writeToCRAM) {
var palindex = this.addressRegister++ & (this.cram.length - 1);
this.cram[palindex] = i;
this.cpalette[palindex] = (0, util_1.RGBA)((i & 3) * 85, ((i >> 2) & 3) * 85, ((i >> 4) & 3) * 85);
this.prefetchByte = i;
this.addressRegister &= this.ramMask;
this.redrawRequired = true;
}
else {
var oldAddress = this.addressRegister;
super.writeData(i);
this.writeTwiddled(oldAddress, i);
}
this.latch = false;
}
writeTwiddled(vdp_addr, val) {
var planarBase = vdp_addr & 0x3ffc;
var twiddledBase = planarBase * 2;
var val0 = this.ram[planarBase];
var val1 = this.ram[planarBase + 1];
var val2 = this.ram[planarBase + 2];
var val3 = this.ram[planarBase + 3];
for (var i = 0; i < 8; ++i) {
var effectiveBit = 7 - i;
var index = (((val0 >>> effectiveBit) & 1))
| (((val1 >>> effectiveBit) & 1) << 1)
| (((val2 >>> effectiveBit) & 1) << 2)
| (((val3 >>> effectiveBit) & 1) << 3);
this.vramUntwiddled[twiddledBase + i] = index;
}
}
getState() {
var state = super.getState();
state['cram'] = this.cram.slice(0);
return state;
}
restoreState(state) {
super.restoreState(state);
this.cram.set(state.cram);
}
drawScanline(y) {
if (this.screenMode == TMS9918A_Mode.MODE4)
this.rasterize_line(y); // special mode 4
else
super.drawScanline(y);
}
findSprites(line) {
var spriteInfo = this.spriteAttributeTable;
var active = [];
var spriteHeight = 8;
var i;
if (this.registers[1] & 2) {
spriteHeight = 16;
}
for (i = 0; i < 64; i++) {
var y = this.ram[spriteInfo + i];
if (y === 208) {
break;
}
if (y >= 240)
y -= 256;
if (line >= y && line < (y + spriteHeight)) {
if (active.length === 8) {
this.statusRegister |= 0x40; // Sprite overflow
break;
}
active.push([
this.ram[spriteInfo + 128 + i * 2],
this.ram[spriteInfo + 128 + i * 2 + 1],
y
]);
}
}
return active;
}
rasterize_background(lineAddr, pixelOffset, tileData, tileDef, transparent) {
lineAddr = lineAddr | 0;
pixelOffset = pixelOffset | 0;
tileData = tileData | 0;
tileDef = (tileDef | 0) * 2;
var i, tileDefInc;
if ((tileData & (1 << 9))) {
tileDefInc = -1;
tileDef += 7;
}
else {
tileDefInc = 1;
}
const paletteOffset = (tileData & (1 << 11)) ? 16 : 0;
var index;
if (transparent && paletteOffset === 0) {
for (i = 0; i < 8; i++) {
index = this.vramUntwiddled[tileDef];
tileDef += tileDefInc;
if (index !== 0)
this.fb32[lineAddr + pixelOffset] = this.cpalette[index];
pixelOffset = (pixelOffset + 1) & 255;
}
}
else {
for (i = 0; i < 8; i++) {
index = this.vramUntwiddled[tileDef] + paletteOffset;
tileDef += tileDefInc;
this.fb32[lineAddr + pixelOffset] = this.cpalette[index];
pixelOffset = (pixelOffset + 1) & 255;
}
}
}
clear_background(lineAddr, pixelOffset) {
lineAddr = lineAddr | 0;
pixelOffset = pixelOffset | 0;
var i;
const rgb = this.cpalette[0];
for (i = 0; i < 8; ++i) {
this.fb32[lineAddr + pixelOffset] = rgb;
pixelOffset = (pixelOffset + 1) & 255;
}
}
rasterize_background_line(lineAddr, pixelOffset, nameAddr, yMod) {
lineAddr = lineAddr | 0;
pixelOffset = pixelOffset | 0;
nameAddr = nameAddr | 0;
const yOffset = (yMod | 0) * 4;
for (var i = 0; i < 32; i++) {
// TODO: static left-hand rows.
var tileData = this.ram[nameAddr + i * 2] | (this.ram[nameAddr + i * 2 + 1] << 8);
var tileNum = tileData & 511;
var tileDef = 32 * tileNum;
if (tileData & (1 << 10)) {
tileDef += 28 - yOffset;
}
else {
tileDef += yOffset;
}
if ((tileData & (1 << 12)) === 0) {
this.rasterize_background(lineAddr, pixelOffset, tileData, tileDef, false);
}
else {
this.clear_background(lineAddr, pixelOffset);
}
pixelOffset = (pixelOffset + 8) & 255;
}
}
rasterize_foreground_line(lineAddr, pixelOffset, nameAddr, yMod) {
lineAddr = lineAddr | 0;
pixelOffset = pixelOffset | 0;
nameAddr = nameAddr | 0;
const yOffset = (yMod | 0) * 4;
for (var i = 0; i < 32; i++) {
// TODO: static left-hand rows.
var tileData = this.ram[nameAddr + i * 2] | (this.ram[nameAddr + i * 2 + 1] << 8);
if ((tileData & (1 << 12)) === 0)
continue;
var tileNum = tileData & 511;
var tileDef = 32 * tileNum;
if (tileData & (1 << 10)) {
tileDef += 28 - yOffset;
}
else {
tileDef += yOffset;
}
this.rasterize_background(lineAddr, ((i * 8) + pixelOffset & 0xff), tileData, tileDef, true);
}
}
rasterize_sprites(line, lineAddr, pixelOffset, sprites) {
lineAddr = lineAddr | 0;
pixelOffset = pixelOffset | 0;
const spriteBase = (this.registers[6] & 4) ? 0x2000 : 0;
// TODO: sprite X-8 shift
// TODO: sprite double size
for (var i = 0; i < 256; ++i) {
var xPos = i; //(i + this.registers[8]) & 0xff;
var spriteFoundThisX = false;
var writtenTo = false;
var minDistToNext = 256;
for (var k = 0; k < sprites.length; k++) {
var sprite = sprites[k];
var offset = xPos - sprite[0];
// Sprite to the right of the current X?
if (offset < 0) {
// Find out how far it would be to skip to this sprite
var distToSprite = -offset;
// Keep the minimum distance to the next sprite to the right.
if (distToSprite < minDistToNext)
minDistToNext = distToSprite;
continue;
}
if (offset >= 8)
continue;
spriteFoundThisX = true;
var spriteLine = line - sprite[2];
var spriteAddr = spriteBase + sprite[1] * 32 + spriteLine * 4;
var untwiddledAddr = spriteAddr * 2 + offset;
var index = this.vramUntwiddled[untwiddledAddr];
if (index === 0) {
continue;
}
if (writtenTo) {
// We have a collision!.
this.statusRegister |= 0x20;
break;
}
this.fb32[lineAddr + ((pixelOffset + i - this.registers[8]) & 0xff)] = this.cpalette[16 + index];
writtenTo = true;
}
if (!spriteFoundThisX && minDistToNext > 1) {
// If we didn't find a sprite on this X, then we can skip ahead by the minimum
// dist to next (minus one to account for loop add)
i += minDistToNext - 1;
}
}
}
border_clear(lineAddr, count) {
lineAddr = lineAddr | 0;
count = count | 0;
const borderIndex = 16 + (this.registers[7] & 0xf);
const borderRGB = this.cpalette[borderIndex];
this.fb32.fill(borderRGB, lineAddr, lineAddr + count);
}
rasterize_line(line) {
line |= 0;
var vdp_regs = this.registers;
var drawWidth = 256;
var drawHeight = this.numVisibleLines; // TODO?
var hBorder = (this.width - drawWidth) >> 1;
var vBorder = (this.height - drawHeight) >> 1; // TODO?
const startAddr = ((line + vBorder) * this.width) | 0;
const lineAddr = (startAddr + hBorder) | 0;
if (!this.displayOn || line < 0 || line >= drawHeight) {
if (line < this.height)
this.border_clear(startAddr, this.width);
else if (line >= 262 - vBorder)
this.border_clear((line - 262 + vBorder) * this.width, this.width);
}
else {
var effectiveLine = line + vdp_regs[9];
if (effectiveLine >= 224) {
effectiveLine -= 224;
}
const sprites = this.findSprites(line);
const pixelOffset = ((vdp_regs[0] & 64) && line < 16) ? 0 : vdp_regs[8];
const nameAddr = this.nameTable + (effectiveLine >>> 3) * 64;
const yMod = effectiveLine & 7;
this.rasterize_background_line(lineAddr, pixelOffset, nameAddr, yMod);
this.rasterize_sprites(line, lineAddr, pixelOffset, sprites);
this.rasterize_foreground_line(lineAddr, pixelOffset, nameAddr, yMod);
this.border_clear(startAddr, hBorder);
this.border_clear(lineAddr + 256, hBorder);
if (vdp_regs[0] & (1 << 5)) {
// Blank out left hand column.
this.border_clear(lineAddr, 8);
}
}
// frame interrupts
if (line == drawHeight) {
this.statusRegister |= 0x80;
if (this.interruptsOn) {
this.cru.setVDPInterrupt(true);
}
}
// line interrupts
if (line <= drawHeight) {
if (this.lineCounter > 0) {
this.lineCounter--;
}
else {
this.lineCounter = this.registers[0xa];
this.lineInterruptPending = true;
}
}
else {
this.lineCounter = this.registers[0xa];
}
if (this.lineInterruptPending) {
if (this.registers[0] & 0x10) {
// TODO this.cru.setVDPInterrupt(true);
}
}
}
getDebugTables() {
if (this.screenMode == TMS9918A_Mode.MODE4) {
var tables = [
["Pattern Table", 0, 512 * 32],
["Name Table", this.nameTable, 32 * 32 * 2],
["Sprite Attributes", this.spriteAttributeTable, 256],
];
return tables;
}
else {
return super.getDebugTables();
}
}
}
exports.SMSVDP = SMSVDP;
;
//# sourceMappingURL=tms9918a.js.map

File diff suppressed because one or more lines are too long

150
gen/common/vlist.js Normal file
View File

@@ -0,0 +1,150 @@
"use strict";
/**
* The MIT License (MIT)
*
* Copyright (C) 2013 Sergi Mansilla
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.VirtualList = void 0;
/**
* Creates a virtually-rendered scrollable list.
* @param {object} config
* @constructor
*/
function VirtualList(config) {
var width = (config && config.w + 'px') || '100%';
var height = (config && config.h + 'px') || '100%';
var itemHeight = this.itemHeight = config.itemHeight;
this.items = config.items;
this.generatorFn = config.generatorFn;
this.totalRows = config.totalRows || (config.items && config.items.length);
var scroller = VirtualList.createScroller(itemHeight * this.totalRows);
this.container = VirtualList.createContainer(width, height);
this.container.appendChild(scroller);
var screenItemsLen = Math.ceil(config.h / itemHeight);
// Cache 4 times the number of items that fit in the container viewport
this.cachedItemsLen = screenItemsLen * 3;
this._renderChunk(this.container, 0);
var self = this;
var lastRepaintY;
var maxBuffer = screenItemsLen * itemHeight;
var lastScrolled = 0;
// As soon as scrolling has stopped, this interval asynchronouslyremoves all
// the nodes that are not used anymore
this.rmNodeInterval = setInterval(function () {
if (Date.now() - lastScrolled > 100) {
var badNodes = document.querySelectorAll('[data-rm="1"]');
for (var i = 0, l = badNodes.length; i < l; i++) {
try {
self.container.removeChild(badNodes[i]);
}
catch (e) {
//
}
}
}
}, 300);
function onScroll(e) {
var scrollTop = e.target.scrollTop; // Triggers reflow
if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
var first = Math.floor(scrollTop / itemHeight) - screenItemsLen;
self._renderChunk(self.container, first < 0 ? 0 : first);
lastRepaintY = scrollTop;
}
lastScrolled = Date.now();
e.preventDefault && e.preventDefault();
}
this.container.addEventListener('scroll', onScroll);
}
exports.VirtualList = VirtualList;
VirtualList.prototype.createRow = function (i) {
var item;
if (this.generatorFn)
item = this.generatorFn(i);
else if (this.items) {
if (typeof this.items[i] === 'string') {
var itemText = document.createTextNode(this.items[i]);
item = document.createElement('div');
item.style.height = this.itemHeight + 'px';
item.appendChild(itemText);
}
else {
item = this.items[i];
}
}
item.classList.add('vrow');
item.setAttribute('data-index', '' + i);
item.style.position = 'absolute';
item.style.top = (i * this.itemHeight) + 'px';
return item;
};
/**
* Renders a particular, consecutive chunk of the total rows in the list. To
* keep acceleration while scrolling, we mark the nodes that are candidate for
* deletion instead of deleting them right away, which would suddenly stop the
* acceleration. We delete them once scrolling has finished.
*
* @param {Node} node Parent node where we want to append the children chunk.
* @param {Number} from Starting position, i.e. first children index.
* @return {void}
*/
VirtualList.prototype._renderChunk = function (node, from) {
var finalItem = from + this.cachedItemsLen;
if (finalItem > this.totalRows)
finalItem = this.totalRows;
// Append all the new rows in a document fragment that we will later append to
// the parent node
var fragment = document.createDocumentFragment();
for (var i = from; i < finalItem; i++) {
fragment.appendChild(this.createRow(i));
}
// Hide and mark obsolete nodes for deletion.
for (var j = 1, l = node.childNodes.length; j < l; j++) {
node.childNodes[j].style.display = 'none';
node.childNodes[j].setAttribute('data-rm', '1');
}
node.appendChild(fragment);
};
VirtualList.createContainer = function (w, h) {
var c = document.createElement('div');
c.classList.add('vlist');
c.style.width = w;
c.style.height = h;
c.style.overflow = 'auto';
c.style.position = 'relative';
c.style.padding = '0';
c.style.border = '1px solid black';
return c;
};
VirtualList.createScroller = function (h) {
var scroller = document.createElement('div');
scroller.style.opacity = '0';
scroller.style.position = 'absolute';
scroller.style.top = '0';
scroller.style.left = '0';
scroller.style.width = '1px';
scroller.style.height = h + 'px';
return scroller;
};
VirtualList.prototype.scrollToItem = function (index) {
this.container.scrollTop = this.itemHeight * index;
};
//# sourceMappingURL=vlist.js.map

1
gen/common/vlist.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"vlist.js","sourceRoot":"","sources":["../../src/common/vlist.ts"],"names":[],"mappings":"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"}

258
gen/common/wasmplatform.js Normal file
View File

@@ -0,0 +1,258 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseWASIMachine = exports.BaseWASMMachine = void 0;
const devices_1 = require("./devices");
const emu_1 = require("./emu");
// WASM Support
// TODO: detangle from c64
class BaseWASMMachine {
constructor(prefix) {
this.maxROMSize = 0x40000;
this.prefix = prefix;
var self = this;
this.cpu = {
getPC: self.getPC.bind(self),
getSP: self.getSP.bind(self),
isStable: self.isStable.bind(self),
reset: self.reset.bind(self),
saveState: () => {
return self.getCPUState();
},
loadState: () => {
console.log("loadState not implemented");
},
connectMemoryBus() {
console.log("connectMemoryBus not implemented");
},
};
}
getImports(wmod) {
return {};
}
async fetchWASM() {
var wasmResponse = await fetch('res/' + this.prefix + '.wasm');
var wasmBinary = await wasmResponse.arrayBuffer();
var wasmCompiled = await WebAssembly.compile(wasmBinary);
var wasmResult = await WebAssembly.instantiate(wasmCompiled, this.getImports(wasmCompiled));
this.instance = wasmResult;
this.exports = wasmResult.exports;
}
async fetchBIOS() {
var biosResponse = await fetch('res/' + this.prefix + '.bios');
var biosBinary = await biosResponse.arrayBuffer();
this.biosptr = this.exports.malloc(biosBinary.byteLength);
this.biosarr = new Uint8Array(this.exports.memory.buffer, this.biosptr, biosBinary.byteLength);
this.loadBIOS(new Uint8Array(biosBinary));
}
async initWASM() {
// init machine instance
this.sys = this.exports.machine_init(this.biosptr);
let statesize = this.exports.machine_get_state_size();
this.stateptr = this.exports.malloc(statesize);
let ctrlstatesize = this.exports.machine_get_controls_state_size();
this.ctrlstateptr = this.exports.malloc(ctrlstatesize);
let cpustatesize = this.exports.machine_get_cpu_state_size();
this.cpustateptr = this.exports.malloc(cpustatesize);
this.romptr = this.exports.malloc(this.maxROMSize);
// create state buffers
// must do this after allocating memory (and everytime we grow memory?)
this.statearr = new Uint8Array(this.exports.memory.buffer, this.stateptr, statesize);
this.ctrlstatearr = new Uint8Array(this.exports.memory.buffer, this.ctrlstateptr, ctrlstatesize);
this.cpustatearr = new Uint8Array(this.exports.memory.buffer, this.cpustateptr, cpustatesize);
// create audio buffer
let sampbufsize = 4096 * 4;
this.audioarr = new Float32Array(this.exports.memory.buffer, this.exports.machine_get_sample_buffer(), sampbufsize);
// create ROM buffer
this.romarr = new Uint8Array(this.exports.memory.buffer, this.romptr, this.maxROMSize);
// enable c64 joystick map to arrow keys (TODO)
//this.exports.c64_set_joystick_type(this.sys, 1);
console.log('machine_init', this.sys, statesize, ctrlstatesize, cpustatesize, sampbufsize);
}
async loadWASM() {
await this.fetchWASM();
this.exports.memory.grow(96); // TODO: need more when probing?
await this.fetchBIOS();
await this.initWASM();
}
getPC() {
return this.exports.machine_cpu_get_pc(this.sys);
}
getSP() {
return this.exports.machine_cpu_get_sp(this.sys);
}
isStable() {
return this.exports.machine_cpu_is_stable(this.sys);
}
loadROM(rom) {
if (rom.length > this.maxROMSize)
throw new emu_1.EmuHalt(`Rom size too big: ${rom.length} bytes`);
this.romarr.set(rom);
this.romlen = rom.length;
console.log('load rom', rom.length, 'bytes');
this.reset(); // TODO?
}
// TODO: can't load after machine_init
loadBIOS(srcArray) {
this.biosarr.set(srcArray);
}
reset() {
this.exports.machine_reset(this.sys);
}
/* TODO: we don't need this because c64_exec does this?
pollControls() {
this.exports.machine_start_frame(this.sys);
}
*/
read(address) {
return this.exports.machine_mem_read(this.sys, address & 0xffff);
}
readConst(address) {
return this.exports.machine_mem_read(this.sys, address & 0xffff);
}
write(address, value) {
this.exports.machine_mem_write(this.sys, address & 0xffff, value & 0xff);
}
getAudioParams() {
return { sampleRate: 44100, stereo: false };
}
connectVideo(pixels) {
this.pixel_dest = pixels;
var pixbuf = this.exports.machine_get_pixel_buffer(this.sys); // save video pointer
this.pixel_src = new Uint32Array(this.exports.memory.buffer, pixbuf, pixels.length);
console.log('connectVideo', pixbuf, pixels.length);
}
syncVideo() {
if (this.exports.machine_update_video) {
this.exports.machine_update_video(this.sys);
}
if (this.pixel_dest != null) {
this.pixel_dest.set(this.pixel_src);
}
}
// assume controls buffer is smaller than cpu buffer
saveControlsState() {
//console.log(1, this.romptr, this.romlen, this.ctrlstateptr, this.romarr.slice(0,4), this.ctrlstatearr.slice(0,4));
this.exports.machine_save_controls_state(this.sys, this.ctrlstateptr);
//console.log(2, this.romptr, this.romlen, this.ctrlstateptr, this.romarr.slice(0,4), this.ctrlstatearr.slice(0,4));
return { controls: this.ctrlstatearr.slice(0) };
}
loadControlsState(state) {
this.ctrlstatearr.set(state.controls);
this.exports.machine_load_controls_state(this.sys, this.ctrlstateptr);
}
connectAudio(audio) {
this.audio = audio;
}
syncAudio() {
if (this.audio != null) {
var n = this.exports.machine_get_sample_count();
for (var i = 0; i < n; i++) {
this.audio.feedSample(this.audioarr[i], 1);
}
}
}
// TODO: tick might advance 1 instruction
advanceFrameClock(trap, cpf) {
var i;
if (trap) {
for (i = 0; i < cpf; i++) {
if (trap()) {
break;
}
this.exports.machine_tick(this.sys);
}
}
else {
this.exports.machine_exec(this.sys, cpf);
i = cpf;
}
this.syncVideo();
this.syncAudio();
return i;
}
copyProbeData() {
if (this.probe && !(this.probe instanceof devices_1.NullProbe)) {
var datalen = this.exports.machine_get_probe_buffer_size();
var dataaddr = this.exports.machine_get_probe_buffer_address();
// TODO: more efficient way to put into probe
var databuf = new Uint32Array(this.exports.memory.buffer, dataaddr, datalen);
this.probe.logNewFrame(); // TODO: machine should do this
this.probe.addLogBuffer(databuf);
}
}
connectProbe(probe) {
this.probe = probe;
}
getDebugTree() {
return this.saveState();
}
}
exports.BaseWASMMachine = BaseWASMMachine;
let stub = function () { console.log(arguments); return 0; };
class BaseWASIMachine extends BaseWASMMachine {
constructor(prefix) {
super(prefix);
}
getImports(wmod) {
var imports = this.wasiInstance.getImports(wmod);
// TODO: eliminate these imports
imports.env = {
system: stub,
__sys_mkdir: stub,
__sys_chmod: stub,
__sys_stat64: stub,
__sys_unlink: stub,
__sys_rename: stub,
__sys_getdents64: stub,
__sys_getcwd: stub,
__sys_rmdir: stub,
emscripten_thread_sleep: stub,
};
return imports;
}
stdoutWrite(buffer) {
console.log('>>>', buffer.toString());
return buffer.length;
}
async loadWASM() {
let WASI = await Promise.resolve().then(() => __importStar(require('@wasmer/wasi')));
let WasmFs = await Promise.resolve().then(() => __importStar(require('@wasmer/wasmfs')));
this.wasmFs = new WasmFs.WasmFs();
let bindings = WASI.WASI.defaultBindings;
bindings.fs = this.wasmFs.fs;
bindings.fs.mkdirSync('/tmp');
bindings.path = bindings.path.default;
this.wasiInstance = new WASI.WASI({
preopenDirectories: { '/tmp': '/tmp' },
env: {},
args: [],
bindings: bindings
});
this.wasmFs.volume.fds[1].write = this.stdoutWrite.bind(this);
this.wasmFs.volume.fds[2].write = this.stdoutWrite.bind(this);
await this.fetchWASM();
this.wasiInstance.start(this.instance);
await this.initWASM();
}
}
exports.BaseWASIMachine = BaseWASIMachine;
//# sourceMappingURL=wasmplatform.js.map

File diff suppressed because one or more lines are too long

53
gen/common/workertypes.js Normal file
View File

@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isOutputResult = exports.isErrorResult = exports.isUnchanged = exports.SourceFile = void 0;
class SourceFile {
constructor(lines, text) {
lines = lines || [];
this.lines = lines;
this.text = text;
this.offset2loc = new Map();
this.line2offset = new Map();
for (var info of lines) {
if (info.offset >= 0) {
// first line wins (is assigned to offset)
// TODO: handle macros/includes w/ multiple offsets per line
if (!this.offset2loc[info.offset])
this.offset2loc[info.offset] = info;
if (!this.line2offset[info.line])
this.line2offset[info.line] = info.offset;
}
}
}
// TODO: smarter about looking for source lines between two addresses
findLineForOffset(PC, lookbehind) {
if (this.offset2loc) {
for (var i = 0; i <= lookbehind; i++) {
var loc = this.offset2loc[PC];
if (loc) {
return loc;
}
PC--;
}
}
return null;
}
lineCount() { return this.lines.length; }
}
exports.SourceFile = SourceFile;
;
;
;
function isUnchanged(result) {
return ('unchanged' in result);
}
exports.isUnchanged = isUnchanged;
function isErrorResult(result) {
return ('errors' in result);
}
exports.isErrorResult = isErrorResult;
function isOutputResult(result) {
return ('output' in result);
}
exports.isOutputResult = isOutputResult;
//# sourceMappingURL=workertypes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"workertypes.js","sourceRoot":"","sources":["../../src/common/workertypes.ts"],"names":[],"mappings":";;;AAmBA,MAAa,UAAU;IAMrB,YAAY,KAAkB,EAAE,IAAW;QACzC,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;QAC7B,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;YACtB,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE;gBACpB,0CAA0C;gBAC1C,4DAA4D;gBAC5D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;oBAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;gBACtC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;oBAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;aAC7C;SACF;IACH,CAAC;IACD,qEAAqE;IACrE,iBAAiB,CAAC,EAAS,EAAE,UAAiB;QAC5C,IAAI,IAAI,CAAC,UAAU,EAAE;YACnB,KAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,IAAE,UAAU,EAAE,CAAC,EAAE,EAAE;gBAChC,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAC9B,IAAI,GAAG,EAAE;oBACP,OAAO,GAAG,CAAC;iBACZ;gBACD,EAAE,EAAE,CAAC;aACN;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,SAAS,KAAY,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;CACjD;AArCD,gCAqCC;AAYA,CAAC;AAOD,CAAC;AAID,CAAC;AAsDF,SAAgB,WAAW,CAAC,MAAoB;IAC9C,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC,CAAC;AACjC,CAAC;AAFD,kCAEC;AAED,SAAgB,aAAa,CAAC,MAAoB;IAChD,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;AAC9B,CAAC;AAFD,sCAEC;AAED,SAAgB,cAAc,CAAC,MAAoB;IACjD,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;AAC9B,CAAC;AAFD,wCAEC"}