diff --git a/index.html b/index.html index 59bc9aeb..e7bdd9dc 100644 --- a/index.html +++ b/index.html @@ -79,6 +79,11 @@ ga('send', 'pageview'); + @@ -163,7 +168,6 @@ ga('send', 'pageview'); - diff --git a/src/emu.js b/src/emu.js index e2c906ce..4fdab56b 100644 --- a/src/emu.js +++ b/src/emu.js @@ -54,8 +54,8 @@ var RasterVideo = function(mainElement, width, height, options) { arraybuf = new ArrayBuffer(imageData.data.length); buf8 = new Uint8Array(arraybuf); // TODO: Uint8ClampedArray datau32 = new Uint32Array(arraybuf); - */ buf8 = imageData.data; + */ datau32 = new Uint32Array(imageData.data.buffer); } @@ -213,8 +213,9 @@ var AnimationTimer = function(frequencyHz, callback) { var intervalMsec = 1000.0 / frequencyHz; var running; var lastts = 0; - var useReqAnimFrame = window.requestAnimationFrame ? true : false; + var useReqAnimFrame = window.requestAnimationFrame ? (frequencyHz>40) : false; var nframes, startts; // for FPS calc + this.frameRate = frequencyHz; function scheduleFrame() { if (useReqAnimFrame) @@ -222,7 +223,7 @@ var AnimationTimer = function(frequencyHz, callback) { else setTimeout(nextFrame, intervalMsec); } - var nextFrame = function(ts) { + function nextFrame(ts) { if (running) { scheduleFrame(); } diff --git a/src/platform/verilog.js b/src/platform/verilog.js index d972ee5e..0c5b56ba 100644 --- a/src/platform/verilog.js +++ b/src/platform/verilog.js @@ -187,12 +187,11 @@ var VerilogPlatform = function(mainElement, options) { var video, audio; var useAudio = false; var videoWidth = 256+20; - var videoHeight = 240+20; + var videoHeight = 240+16; var maxVideoLines = 262+40; // vertical hold - var idata, timer; + var idata, timer, timerCallback; var gen; - var frameRate = 60; - var AUDIO_FREQ = (256+23+7+23)*262*60; // 4857480 Hz + var cyclesPerFrame = (256+23+7+23)*262; // 4857480/60 Hz var current_output; var paddle_x = 0; var paddle_y = 0; @@ -202,12 +201,19 @@ var VerilogPlatform = function(mainElement, options) { var scope_time_x = 0; // scope cursor var scope_x_offset = 0; var scope_y_offset = 0; + var scope_index_offset = 0; var scope_max_y = 0; + var scope_y_top = 0; + var scopeWidth = videoWidth; + var scopeHeight = videoHeight; + var scopeImageData; + var sdata; // scope data var yposlist = []; var lasty = []; var lastval = []; - var ports_and_signals; + var trace_ports; + var trace_signals; var trace_buffer; var trace_index; var mouse_pressed; @@ -249,8 +255,8 @@ var VerilogPlatform = function(mainElement, options) { if (inspect_obj && inspect_sym) { var COLOR_BIT_OFF = 0xffff6666; var COLOR_BIT_ON = 0xffff9999; - var i = 0; - for (var y=0; y videoWidth*2) { + framehsync = false; + framex = 0; + framey++; + gen.hpaddle = framey > paddle_x ? 1 : 0; + gen.vpaddle = framey > paddle_y ? 1 : 0; + } + if (framey > maxVideoLines || gen.vsync) { + var wasvsync = framevsync; + framevsync = true; + framey = 0; + framex = videoWidth-10; + frameidx = framex; + if (sync && !wasvsync) return; // exit when vsync starts + } else { + framevsync = false; + } + } + } + function updateVideoFrame() { useAudio = (audio != null); debugCond = self.getDebugCallback(); - var i; - // erase top line & start i on offset - for (i=0; i paddle_x ? 1 : 0; - gen.vpaddle = y > paddle_y ? 1 : 0; - for (var x=0; x 10 ? 0xcfffffff : 0x7fdddddd; + for (var i=0; i 45; + var trace = fps < 0.1; + updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace); + //if (trace) displayTraceBuffer(); updateInspectionFrame(); - video.updateFrame(); + if (trace) { + video.getContext().fillStyle = "black"; + video.getContext().fillRect(0, 0, videoWidth, videoHeight/3); + video.updateFrame(0, -framey+videoHeight/6, 0, 0, videoWidth, videoHeight); + scope_index_offset = (trace_index - trace_signals.length*scopeWidth + trace_buffer.length) % trace_buffer.length; + scope_x_offset = 0; + updateScopeOverlay(trace_signals); + var k = 0.1; + scope_y_top = k*videoHeight/3 + scope_y_top*(1-k); + } else { + video.updateFrame(); + scope_index_offset = 0; + scope_y_top = videoHeight; + } updateInspectionPostFrame(); self.restartDebugState(); gen.__unreset(); } + function displayTraceBuffer() { + var skip = trace_signals.length; + var src = trace_index; + for (var dest=0; dest= trace_buffer.length) trace_index = 0; + } + } + function fillTraceBuffer(count) { - var arr = ports_and_signals; - var max_index = Math.min(trace_buffer.length, trace_index + count); + var max_index = Math.min(trace_buffer.length - trace_ports.length, trace_index + count); while (trace_index < max_index) { gen.clk ^= 1; gen.eval(); - for (var i=0; i1 ? v.len*2+8 : 8; var y2 = y1+ys; var z = trace_buffer[j++]; + if (j >= trace_buffer.length) j = 0; var y = Math.round(y2 - ys*((z-lo)/hi)); - yposlist[i] = y2; + yposlist[i] = y2 + scope_y_top; var ly = lasty[i]; if (x > 0 && ly != y) { var dir = ly < y ? 1 : -1; while ((ly += dir) != y && ly >= y1 && ly <= y2) { - idata[x + ly*videoWidth] = COLOR_TRANS_SIGNAL; + sdata[x + ly * scopeWidth] = COLOR_TRANS_SIGNAL; } } - idata[x + y*videoWidth] = lastval[i]==z ? COLOR_SIGNAL : COLOR_BLIP_SIGNAL; + sdata[x + y * scopeWidth] = lastval[i]==z ? COLOR_SIGNAL : COLOR_BLIP_SIGNAL; lasty[i] = y; lastval[i] = z; y1 += ys+yb; } } scope_max_y = y1 - scope_y_offset; - video.updateFrame(); + video.getContext().putImageData(scopeImageData, 0, scope_y_top); // draw labels var ctx = video.getContext(); for (var i=0; i videoHeight) continue; var v = arr[i]; var name = v.name; ctx.fillStyle = name == inspect_sym ? "yellow" : "white"; @@ -395,8 +470,8 @@ var VerilogPlatform = function(mainElement, options) { shadowText(ctx, name, 1, yposlist[i]); if (scope_time_x > 0) { ctx.textAlign = 'right'; - var value = arr.length * scope_time_x + i + jstart; - shadowText(ctx, ""+trace_buffer[value], videoWidth-1, yposlist[i]); + var value = (arr.length * scope_time_x + i + jstart) % trace_buffer.length; + shadowText(ctx, ""+trace_buffer[value], videoWidth-1, yp); } } // draw scope line & label @@ -458,7 +533,7 @@ var VerilogPlatform = function(mainElement, options) { } }); idata = video.getFrameData(); - timer = new AnimationTimer(frameRate, function() { + timerCallback = function() { if (!self.isRunning()) return; gen.switches = switches[0]; @@ -466,8 +541,9 @@ var VerilogPlatform = function(mainElement, options) { updateVideoFrame(); else updateScopeFrame(); - }); + }; trace_buffer = new Uint32Array(0x10000); + self.setFrameRate(60); } this.printErrorCodeContext = function(e, code) { @@ -496,17 +572,23 @@ var VerilogPlatform = function(mainElement, options) { gen = new mod(base); gen.__proto__ = base; current_output = output; - ports_and_signals = current_output.ports; + trace_ports = current_output.ports; + trace_signals = current_output.ports.concat(current_output.signals); trace_index = 0; // power on module this.poweron(); + restartAudio(); + } + + function restartAudio() { // stop/start audio - if (audio && gen.spkr === undefined) { + var hasAudio = gen && gen.spkr !== undefined && frameRate > 1; + if (audio && !hasAudio) { audio.stop(); audio = null; - } else if (!audio && gen.spkr !== undefined) { - audio = new SampleAudio(AUDIO_FREQ); - if (this.isRunning()) + } else if (!audio && hasAudio) { + audio = new SampleAudio(cyclesPerFrame * self.getFrameRate()); + if (self.isRunning()) audio.start(); } } @@ -523,6 +605,25 @@ var VerilogPlatform = function(mainElement, options) { if (audio) audio.start(); } + var frameRate = 0; + + this.setFrameRate = function(rateHz) { + frameRate = rateHz; + var fps = Math.min(60, rateHz*cyclesPerFrame); + if (!timer || timer.frameRate != fps) { + var running = this.isRunning(); + if (timer) timer.stop(); + timer = new AnimationTimer(fps, timerCallback); + if (running) timer.start(); + } + if (audio) { + audio.stop(); + audio = null; + } + restartAudio(); + } + this.getFrameRate = function() { return frameRate; } + this.poweron = function() { gen._ctor_var_reset(); this.reset(); diff --git a/src/ui.js b/src/ui.js index f8b2a4b1..0e5b90e6 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1232,6 +1232,26 @@ function _recordVideo() { f(); } +function setFrameRateUI(fps) { + platform.setFrameRate(fps); + if (fps > 0.01) + $("#fps_label").text(fps.toFixed(2)); + else + $("#fps_label").text("1/"+Math.round(1/fps)); +} + +function _slowerFrameRate() { + var fps = platform.getFrameRate(); + fps = fps/2; + if (fps > 0.00001) setFrameRateUI(fps); +} + +function _fasterFrameRate() { + var fps = platform.getFrameRate(); + fps = Math.min(60, fps*2); + setFrameRateUI(fps); +} + function setupDebugControls(){ $("#dbg_reset").click(resetAndDebug); $("#dbg_pause").click(pause); @@ -1281,6 +1301,11 @@ function setupDebugControls(){ $("#item_debug_expr").hide(); $("#item_download_rom").click(_downloadROMImage); $("#item_record_video").click(_recordVideo); + if (platform.setFrameRate && platform.getFrameRate) { + $("#speed_bar").show(); + $("#dbg_slower").click(_slowerFrameRate); + $("#dbg_faster").click(_fasterFrameRate); + } updateDebugWindows(); } diff --git a/src/worker/verilator2js.js b/src/worker/verilator2js.js index b7a6064b..d5a52e92 100644 --- a/src/worker/verilator2js.js +++ b/src/worker/verilator2js.js @@ -8,7 +8,7 @@ function parseDecls(text, arr, name, bin, bout) { arr.push({ wordlen:parseInt(m[1]), name:m[2], - len:parseInt(m[3]), + len:parseInt(m[3])+1, ofs:parseInt(m[4]), }); } @@ -19,7 +19,7 @@ function parseDecls(text, arr, name, bin, bout) { wordlen:parseInt(m[1]), name:m[2], arrdim:[parseInt(m[3])], - len:parseInt(m[4]), + len:parseInt(m[4])+1, ofs:parseInt(m[5]), }); } @@ -30,7 +30,7 @@ function parseDecls(text, arr, name, bin, bout) { wordlen:parseInt(m[1]), name:m[2], arrdim:[parseInt(m[3]), parseInt(m[4])], - len:parseInt(m[5]), + len:parseInt(m[5])+1, ofs:parseInt(m[6]), }); }