mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-09-27 08:54:48 +00:00
new scope module for verilog
This commit is contained in:
parent
98ee1a2d77
commit
ba55a35325
23
css/ui.css
23
css/ui.css
@ -215,10 +215,22 @@ div.emulator {
|
|||||||
left:50%;
|
left:50%;
|
||||||
top:0;
|
top:0;
|
||||||
width:50%;
|
width:50%;
|
||||||
|
height:100%;
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
margin-top: 20px auto 0;
|
margin-top: 20px auto 0;
|
||||||
|
}
|
||||||
|
div.emuoverlay {
|
||||||
|
position:absolute;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
width:100%;
|
||||||
height:100%;
|
height:100%;
|
||||||
}
|
}
|
||||||
|
div.emuscope {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
div.emuspacer {
|
||||||
|
}
|
||||||
/* has to be here b/c renders differently after first load if in inline style */
|
/* has to be here b/c renders differently after first load if in inline style */
|
||||||
.emuvideo {
|
.emuvideo {
|
||||||
border-radius:20px;
|
border-radius:20px;
|
||||||
@ -300,3 +312,14 @@ div.replaydiv {
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
.gutter {
|
||||||
|
background-color: #555;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50%;
|
||||||
|
}
|
||||||
|
.gutter.gutter-vertical {
|
||||||
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=')
|
||||||
|
}
|
||||||
|
.gutter.gutter-horizontal {
|
||||||
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==')
|
||||||
|
}
|
@ -67,6 +67,7 @@ TODO:
|
|||||||
- update memory browser window if view before 1st compile, update symbols
|
- update memory browser window if view before 1st compile, update symbols
|
||||||
- spinner disappears sometimes (and compiles even when not spinning...) (undo?)
|
- spinner disappears sometimes (and compiles even when not spinning...) (undo?)
|
||||||
- z80 illegal opcode kills platform
|
- z80 illegal opcode kills platform
|
||||||
|
- quantify verilog "graph iterations"
|
||||||
|
|
||||||
|
|
||||||
WEB WORKER FORMAT
|
WEB WORKER FORMAT
|
||||||
|
@ -182,6 +182,8 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
|
|||||||
<div id="javatari-screen" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
<div id="javatari-screen" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
||||||
<div id="javatari-console-panel" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
<div id="javatari-console-panel" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="emuoverlay" class="emuoverlay">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="mem_info" class="mem_info" style="display:none">
|
<div id="mem_info" class="mem_info" style="display:none">
|
||||||
</div>
|
</div>
|
||||||
@ -288,6 +290,8 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
|
|||||||
<!--<script src="jsnes/lib/dynamicaudio-min.js" type="text/javascript" charset="utf-8"></script>-->
|
<!--<script src="jsnes/lib/dynamicaudio-min.js" type="text/javascript" charset="utf-8"></script>-->
|
||||||
<script src="FileSaver.js/FileSaver.min.js"></script>
|
<script src="FileSaver.js/FileSaver.min.js"></script>
|
||||||
<script src="localForage/dist/localforage.nopromises.js"></script>
|
<script src="localForage/dist/localforage.nopromises.js"></script>
|
||||||
|
<script src="lib/mousetrap.min.js"></script>
|
||||||
|
<script src="lib/split.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var exports = {};
|
var exports = {};
|
||||||
@ -317,6 +321,7 @@ function require(modname) {
|
|||||||
<script src="gen/windows.js"></script>
|
<script src="gen/windows.js"></script>
|
||||||
<script src="gen/views.js"></script>
|
<script src="gen/views.js"></script>
|
||||||
<script src="gen/recorder.js"></script>
|
<script src="gen/recorder.js"></script>
|
||||||
|
<script src="gen/waveform.js"></script>
|
||||||
<script src="gen/ui.js"></script>
|
<script src="gen/ui.js"></script>
|
||||||
<!-- <script src="src/audio/votrax.js"></script> -->
|
<!-- <script src="src/audio/votrax.js"></script> -->
|
||||||
|
|
||||||
|
11
lib/mousetrap.min.js
vendored
Normal file
11
lib/mousetrap.min.js
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* mousetrap v1.6.2 craig.is/killing/mice */
|
||||||
|
(function(p,t,h){function u(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function y(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return m[a.which]?m[a.which]:q[a.which]?q[a.which]:String.fromCharCode(a.which).toLowerCase()}function E(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function v(a){return"shift"==a||"ctrl"==a||"alt"==a||
|
||||||
|
"meta"==a}function z(a,b){var d,e=[];var c=a;"+"===c?c=["+"]:(c=c.replace(/\+{2}/g,"+plus"),c=c.split("+"));for(d=0;d<c.length;++d){var k=c[d];A[k]&&(k=A[k]);b&&"keypress"!=b&&B[k]&&(k=B[k],e.push("shift"));v(k)&&e.push(k)}c=k;d=b;if(!d){if(!n){n={};for(var h in m)95<h&&112>h||m.hasOwnProperty(h)&&(n[m[h]]=h)}d=n[c]?"keydown":"keypress"}"keypress"==d&&e.length&&(d="keydown");return{key:k,modifiers:e,action:d}}function C(a,b){return null===a||a===t?!1:a===b?!0:C(a.parentNode,b)}function e(a){function b(a){a=
|
||||||
|
a||{};var b=!1,l;for(l in n)a[l]?b=!0:n[l]=0;b||(w=!1)}function d(a,b,r,g,F,e){var l,D=[],h=r.type;if(!f._callbacks[a])return[];"keyup"==h&&v(a)&&(b=[a]);for(l=0;l<f._callbacks[a].length;++l){var d=f._callbacks[a][l];if((g||!d.seq||n[d.seq]==d.level)&&h==d.action){var c;(c="keypress"==h&&!r.metaKey&&!r.ctrlKey)||(c=d.modifiers,c=b.sort().join(",")===c.sort().join(","));c&&(c=g&&d.seq==g&&d.level==e,(!g&&d.combo==F||c)&&f._callbacks[a].splice(l,1),D.push(d))}}return D}function h(a,b,d,g){f.stopCallback(b,
|
||||||
|
b.target||b.srcElement,d,g)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function c(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=y(a);b&&("keyup"==a.type&&x===b?x=!1:f.handleKey(b,E(a),a))}function k(a,d,r,g){function l(d){return function(){w=d;++n[a];clearTimeout(p);p=setTimeout(b,1E3)}}function e(d){h(r,d,a);"keyup"!==g&&(x=y(d));setTimeout(b,10)}for(var c=n[a]=0;c<d.length;++c){var f=c+1===d.length?e:l(g||
|
||||||
|
z(d[c+1]).action);m(d[c],f,g,a,c)}}function m(a,b,c,g,e){f._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var h=a.split(" ");1<h.length?k(a,h,b,c):(c=z(a,c),f._callbacks[c.key]=f._callbacks[c.key]||[],d(c.key,c.modifiers,{type:c.action},g,a,e),f._callbacks[c.key][g?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:g,level:e,combo:a}))}var f=this;a=a||t;if(!(f instanceof e))return new e(a);f.target=a;f._callbacks={};f._directMap={};var n={},p,x=!1,q=!1,w=!1;f._handleKey=function(a,
|
||||||
|
c,e){var g=d(a,c,e),f;c={};var l=0,k=!1;for(f=0;f<g.length;++f)g[f].seq&&(l=Math.max(l,g[f].level));for(f=0;f<g.length;++f)g[f].seq?g[f].level==l&&(k=!0,c[g[f].seq]=1,h(g[f].callback,e,g[f].combo,g[f].seq)):k||h(g[f].callback,e,g[f].combo);g="keypress"==e.type&&q;e.type!=w||v(a)||g||b(c);q=k&&"keydown"==e.type};f._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)m(a[d],b,c)};u(a,"keypress",c);u(a,"keydown",c);u(a,"keyup",c)}if(p){var m={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
|
||||||
|
18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},q={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},B={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},A={option:"alt",command:"meta","return":"enter",
|
||||||
|
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(h=1;20>h;++h)m[111+h]="f"+h;for(h=0;9>=h;++h)m[h+96]=h.toString();e.prototype.bind=function(a,b,d){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,d);return this};e.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};e.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};e.prototype.reset=function(){this._callbacks={};
|
||||||
|
this._directMap={};return this};e.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||C(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};e.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};e.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(m[b]=a[b]);n=null};e.init=function(){var a=e(t),b;for(b in a)"_"!==b.charAt(0)&&(e[b]=function(b){return function(){return a[b].apply(a,
|
||||||
|
arguments)}}(b))};e.init();p.Mousetrap=e;"undefined"!==typeof module&&module.exports&&(module.exports=e);"function"===typeof define&&define.amd&&define(function(){return e})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
|
2
lib/split.min.js
vendored
Normal file
2
lib/split.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! Split.js - v1.3.5 */
|
||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var e=window,t=e.document,n="addEventListener",i="removeEventListener",r="getBoundingClientRect",s=function(){return!1},o=e.attachEvent&&!e[n],a=["","-webkit-","-moz-","-o-"].filter(function(e){var n=t.createElement("div");return n.style.cssText="width:"+e+"calc(9px)",!!n.style.length}).shift()+"calc",l=function(e){return"string"==typeof e||e instanceof String?t.querySelector(e):e};return function(u,c){function z(e,t,n){var i=A(y,t,n);Object.keys(i).forEach(function(t){return e.style[t]=i[t]})}function h(e,t){var n=B(y,t);Object.keys(n).forEach(function(t){return e.style[t]=n[t]})}function f(e){var t=E[this.a],n=E[this.b],i=t.size+n.size;t.size=e/this.size*i,n.size=i-e/this.size*i,z(t.element,t.size,this.aGutterSize),z(n.element,n.size,this.bGutterSize)}function m(e){var t;this.dragging&&((t="touches"in e?e.touches[0][b]-this.start:e[b]-this.start)<=E[this.a].minSize+M+this.aGutterSize?t=E[this.a].minSize+this.aGutterSize:t>=this.size-(E[this.b].minSize+M+this.bGutterSize)&&(t=this.size-(E[this.b].minSize+this.bGutterSize)),f.call(this,t),c.onDrag&&c.onDrag())}function g(){var e=E[this.a].element,t=E[this.b].element;this.size=e[r]()[y]+t[r]()[y]+this.aGutterSize+this.bGutterSize,this.start=e[r]()[G]}function d(){var t=this,n=E[t.a].element,r=E[t.b].element;t.dragging&&c.onDragEnd&&c.onDragEnd(),t.dragging=!1,e[i]("mouseup",t.stop),e[i]("touchend",t.stop),e[i]("touchcancel",t.stop),t.parent[i]("mousemove",t.move),t.parent[i]("touchmove",t.move),delete t.stop,delete t.move,n[i]("selectstart",s),n[i]("dragstart",s),r[i]("selectstart",s),r[i]("dragstart",s),n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",r.style.userSelect="",r.style.webkitUserSelect="",r.style.MozUserSelect="",r.style.pointerEvents="",t.gutter.style.cursor="",t.parent.style.cursor=""}function S(t){var i=this,r=E[i.a].element,o=E[i.b].element;!i.dragging&&c.onDragStart&&c.onDragStart(),t.preventDefault(),i.dragging=!0,i.move=m.bind(i),i.stop=d.bind(i),e[n]("mouseup",i.stop),e[n]("touchend",i.stop),e[n]("touchcancel",i.stop),i.parent[n]("mousemove",i.move),i.parent[n]("touchmove",i.move),r[n]("selectstart",s),r[n]("dragstart",s),o[n]("selectstart",s),o[n]("dragstart",s),r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",o.style.userSelect="none",o.style.webkitUserSelect="none",o.style.MozUserSelect="none",o.style.pointerEvents="none",i.gutter.style.cursor=j,i.parent.style.cursor=j,g.call(i)}function v(e){e.forEach(function(t,n){if(n>0){var i=F[n-1],r=E[i.a],s=E[i.b];r.size=e[n-1],s.size=t,z(r.element,r.size,i.aGutterSize),z(s.element,s.size,i.bGutterSize)}})}function p(){F.forEach(function(e){e.parent.removeChild(e.gutter),E[e.a].element.style[y]="",E[e.b].element.style[y]=""})}void 0===c&&(c={});var y,b,G,E,w=l(u[0]).parentNode,D=e.getComputedStyle(w).flexDirection,U=c.sizes||u.map(function(){return 100/u.length}),k=void 0!==c.minSize?c.minSize:100,x=Array.isArray(k)?k:u.map(function(){return k}),L=void 0!==c.gutterSize?c.gutterSize:10,M=void 0!==c.snapOffset?c.snapOffset:30,O=c.direction||"horizontal",j=c.cursor||("horizontal"===O?"ew-resize":"ns-resize"),C=c.gutter||function(e,n){var i=t.createElement("div");return i.className="gutter gutter-"+n,i},A=c.elementStyle||function(e,t,n){var i={};return"string"==typeof t||t instanceof String?i[e]=t:i[e]=o?t+"%":a+"("+t+"% - "+n+"px)",i},B=c.gutterStyle||function(e,t){return n={},n[e]=t+"px",n;var n};"horizontal"===O?(y="width","clientWidth",b="clientX",G="left","paddingLeft"):"vertical"===O&&(y="height","clientHeight",b="clientY",G="top","paddingTop");var F=[];return E=u.map(function(e,t){var i,s={element:l(e),size:U[t],minSize:x[t]};if(t>0&&(i={a:t-1,b:t,dragging:!1,isFirst:1===t,isLast:t===u.length-1,direction:O,parent:w},i.aGutterSize=L,i.bGutterSize=L,i.isFirst&&(i.aGutterSize=L/2),i.isLast&&(i.bGutterSize=L/2),"row-reverse"===D||"column-reverse"===D)){var a=i.a;i.a=i.b,i.b=a}if(!o&&t>0){var c=C(t,O);h(c,L),c[n]("mousedown",S.bind(i)),c[n]("touchstart",S.bind(i)),w.insertBefore(c,s.element),i.gutter=c}0===t||t===u.length-1?z(s.element,s.size,L/2):z(s.element,s.size,L);var f=s.element[r]()[y];return f<s.minSize&&(s.minSize=f),t>0&&F.push(i),s}),o?{setSizes:v,destroy:p}:{setSizes:v,getSizes:function(){return E.map(function(e){return e.size})},collapse:function(e){if(e===F.length){var t=F[e-1];g.call(t),o||f.call(t,t.size-t.bGutterSize)}else{var n=F[e];g.call(n),o||f.call(n,n.aGutterSize)}},destroy:p}}});
|
@ -4,6 +4,9 @@ import { Platform, BasePlatform } from "../baseplatform";
|
|||||||
import { PLATFORMS, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
|
import { PLATFORMS, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
|
||||||
import { SampleAudio } from "../audio";
|
import { SampleAudio } from "../audio";
|
||||||
import { safe_extend } from "../util";
|
import { safe_extend } from "../util";
|
||||||
|
import { WaveformView, WaveformProvider, WaveformMeta } from "../waveform";
|
||||||
|
|
||||||
|
declare var Split;
|
||||||
|
|
||||||
var VERILOG_PRESETS = [
|
var VERILOG_PRESETS = [
|
||||||
{id:'clock_divider.v', name:'Clock Divider'},
|
{id:'clock_divider.v', name:'Clock Divider'},
|
||||||
@ -225,27 +228,11 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
var inspect_data = new Uint32Array(videoWidth * videoHeight);
|
var inspect_data = new Uint32Array(videoWidth * videoHeight);
|
||||||
|
|
||||||
// for scope
|
// for scope
|
||||||
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 scope_a = 0; // used for transitions
|
|
||||||
var scopeWidth = videoWidth;
|
|
||||||
var scopeHeight = videoHeight;
|
|
||||||
var scopeImageData;
|
|
||||||
var sdata; // scope data
|
|
||||||
var module_name;
|
var module_name;
|
||||||
var yposlist = [];
|
//var trace_ports;
|
||||||
var lasty = [];
|
|
||||||
var lastval = [];
|
|
||||||
var trace_ports;
|
|
||||||
var trace_signals;
|
var trace_signals;
|
||||||
var trace_buffer;
|
var trace_buffer;
|
||||||
var trace_index;
|
var trace_index;
|
||||||
var mouse_pressed;
|
|
||||||
var dirty = false;
|
|
||||||
|
|
||||||
// for virtual CRT
|
// for virtual CRT
|
||||||
var framex=0;
|
var framex=0;
|
||||||
@ -308,7 +295,12 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
|
|
||||||
// inner Platform class
|
// inner Platform class
|
||||||
|
|
||||||
class _VerilogPlatform extends BasePlatform {
|
class _VerilogPlatform extends BasePlatform implements WaveformProvider {
|
||||||
|
|
||||||
|
waveview : WaveformView;
|
||||||
|
wavediv : JQuery;
|
||||||
|
split;
|
||||||
|
hasvideo : boolean;
|
||||||
|
|
||||||
getPresets() { return VERILOG_PRESETS; }
|
getPresets() { return VERILOG_PRESETS; }
|
||||||
|
|
||||||
@ -321,37 +313,6 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
|
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
|
||||||
var vcanvas = $(video.canvas);
|
var vcanvas = $(video.canvas);
|
||||||
vcanvas.mousemove( (e) => {
|
|
||||||
var new_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 20);
|
|
||||||
var new_y = Math.floor(e.offsetY * video.canvas.height / vcanvas.height() - 20);
|
|
||||||
if (mouse_pressed) {
|
|
||||||
scope_y_offset = clamp(Math.min(0,-scope_max_y+videoHeight), 0, scope_y_offset + new_y - paddle_y);
|
|
||||||
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
|
|
||||||
dirty = true;
|
|
||||||
this.refreshFrame();
|
|
||||||
}
|
|
||||||
paddle_x = clamp(8, 240, new_x);
|
|
||||||
paddle_y = clamp(8, 240, new_y);
|
|
||||||
});
|
|
||||||
vcanvas.mousedown( (e) => {
|
|
||||||
scope_time_x = Math.floor(e.offsetX * video.canvas.width / vcanvas.width() - 16);
|
|
||||||
mouse_pressed = true;
|
|
||||||
//if (e.target.setCapture) e.target.setCapture(); // TODO: pointer capture
|
|
||||||
dirty = true;
|
|
||||||
this.refreshFrame();
|
|
||||||
});
|
|
||||||
vcanvas.mouseup( (e) => {
|
|
||||||
mouse_pressed = false;
|
|
||||||
//if (e.target.setCapture) e.target.releaseCapture(); // TODO: pointer capture
|
|
||||||
dirty = true;
|
|
||||||
this.refreshFrame();
|
|
||||||
});
|
|
||||||
vcanvas.keydown( (e) => {
|
|
||||||
switch (e.keyCode) {
|
|
||||||
case 37: scope_time_x--; dirty=true; this.refreshFrame(); break;
|
|
||||||
case 39: scope_time_x++; dirty=true; this.refreshFrame(); break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
idata = video.getFrameData();
|
idata = video.getFrameData();
|
||||||
timerCallback = () => {
|
timerCallback = () => {
|
||||||
if (!this.isRunning())
|
if (!this.isRunning())
|
||||||
@ -359,8 +320,30 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
gen.switches = switches[0];
|
gen.switches = switches[0];
|
||||||
this.updateFrame();
|
this.updateFrame();
|
||||||
};
|
};
|
||||||
trace_buffer = new Uint32Array(0x10000);
|
|
||||||
this.setFrameRate(60);
|
this.setFrameRate(60);
|
||||||
|
// setup scope
|
||||||
|
trace_buffer = new Uint32Array(0x20000);
|
||||||
|
var overlay = $("#emuoverlay");
|
||||||
|
var topdiv = $('<div class="emuspacer">').appendTo(overlay);
|
||||||
|
this.wavediv = $('<div class="emuscope">').appendTo(overlay);
|
||||||
|
this.split = Split( [topdiv[0], this.wavediv[0]], {
|
||||||
|
minSize: [0,0],
|
||||||
|
sizes: [99,1],
|
||||||
|
direction: 'vertical',
|
||||||
|
gutterSize: 16,
|
||||||
|
onDrag: () => {
|
||||||
|
if (this.waveview) this.waveview.recreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// setup mouse events
|
||||||
|
topdiv.mousemove( (e) => {
|
||||||
|
var x = e.pageX - vcanvas.offset().left;
|
||||||
|
var y = e.pageY - vcanvas.offset().top;
|
||||||
|
var new_x = Math.floor(x * video.canvas.width / vcanvas.width() - 20);
|
||||||
|
var new_y = Math.floor(y * video.canvas.height / vcanvas.height() - 20);
|
||||||
|
paddle_x = clamp(8, 240, new_x);
|
||||||
|
paddle_y = clamp(8, 240, new_y);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setGenInputs() {
|
setGenInputs() {
|
||||||
@ -382,12 +365,19 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
}
|
}
|
||||||
// paint into frame, synched with vsync if full speed
|
// paint into frame, synched with vsync if full speed
|
||||||
var sync = fps > 45;
|
var sync = fps > 45;
|
||||||
var trace = fps < 0.02;
|
var trace = this.isScopeVisible();
|
||||||
this.updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
|
this.updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
|
||||||
//if (trace) displayTraceBuffer();
|
|
||||||
//this.restartDebugState();
|
//this.restartDebugState();
|
||||||
gen.__unreset();
|
gen.__unreset();
|
||||||
this.refreshVideoFrame();
|
this.refreshVideoFrame();
|
||||||
|
// set scope offset
|
||||||
|
if (trace && this.waveview) {
|
||||||
|
this.waveview.setEndTime(Math.floor(trace_index/trace_signals.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isScopeVisible() {
|
||||||
|
return this.split.getSizes()[1] > 2; // TODO?
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: merge with prev func
|
// TODO: merge with prev func
|
||||||
@ -402,47 +392,40 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
|
|
||||||
refreshVideoFrame() {
|
refreshVideoFrame() {
|
||||||
this.updateInspectionFrame();
|
this.updateInspectionFrame();
|
||||||
this.updateAnimateScope();
|
video.updateFrame();
|
||||||
this.updateInspectionPostFrame();
|
this.updateInspectionPostFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshScopeOverlay() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScopeFrame() {
|
||||||
|
this.split.setSizes([0,100]); // ensure scope visible
|
||||||
|
var done = this.fillTraceBuffer(32 * trace_signals.length); // TODO: const
|
||||||
|
if (done)
|
||||||
|
this.pause(); // TODO?
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScope() {
|
||||||
|
// create scope, if visible
|
||||||
|
if (this.isScopeVisible()) {
|
||||||
|
if (!this.waveview) {
|
||||||
|
this.waveview = new WaveformView(this.wavediv[0], this);
|
||||||
|
} else {
|
||||||
|
this.waveview.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateFrame() {
|
updateFrame() {
|
||||||
if (!gen) return;
|
if (!gen) return;
|
||||||
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
|
if (this.hasvideo)
|
||||||
this.updateVideoFrame();
|
this.updateVideoFrame();
|
||||||
else
|
else
|
||||||
this.updateScopeFrame();
|
this.updateScopeFrame();
|
||||||
}
|
this.updateScope();
|
||||||
|
|
||||||
refreshFrame() {
|
|
||||||
if (!gen) return;
|
|
||||||
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
|
|
||||||
this.refreshVideoFrame();
|
|
||||||
else
|
|
||||||
this.refreshScopeOverlay(trace_ports);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAnimateScope() {
|
|
||||||
var fps = this.getFrameRate();
|
|
||||||
var trace = fps < 0.02;
|
|
||||||
var ctx = video.getContext();
|
|
||||||
if (scope_a > 0.01) {
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.fillRect(0, 0, videoWidth, videoHeight);
|
|
||||||
var vidyoffset = Math.round(scope_a*(-framey+videoHeight/6));
|
|
||||||
video.updateFrame(0, vidyoffset, 0, 0, videoWidth, videoHeight);
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillRect(framex, framey+vidyoffset, 1, 1);
|
|
||||||
scope_index_offset = (trace_index - trace_signals.length*scopeWidth + trace_buffer.length) % trace_buffer.length;
|
|
||||||
scope_x_offset = 0;
|
|
||||||
this.refreshScopeOverlay(trace_signals);
|
|
||||||
} else {
|
|
||||||
video.updateFrame();
|
|
||||||
scope_index_offset = 0;
|
|
||||||
}
|
|
||||||
// smooth transition
|
|
||||||
scope_a = scope_a * 0.9 + (trace?1.0:0.0) * 0.1;
|
|
||||||
scope_y_top = (1 - scope_a*0.7) * videoHeight - (1 - scope_a) * scope_y_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInspectionFrame() {
|
updateInspectionFrame() {
|
||||||
@ -471,12 +454,15 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVideoFrameCycles(ncycles, sync, trace) {
|
updateVideoFrameCycles(ncycles:number, sync:boolean, trace:boolean) {
|
||||||
ncycles |= 0;
|
ncycles |= 0;
|
||||||
var inspect = inspect_obj && inspect_sym;
|
var inspect = inspect_obj && inspect_sym;
|
||||||
|
var trace0 = trace_index;
|
||||||
while (ncycles--) {
|
while (ncycles--) {
|
||||||
if (trace)
|
if (trace) {
|
||||||
this.snapshotTrace(true);
|
this.snapshotTrace();
|
||||||
|
if (trace_index == trace0) trace = false; // kill trace when wraps around
|
||||||
|
}
|
||||||
vidtick();
|
vidtick();
|
||||||
if (framex++ < videoWidth) {
|
if (framex++ < videoWidth) {
|
||||||
if (framey < videoHeight) {
|
if (framey < videoHeight) {
|
||||||
@ -513,123 +499,50 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
displayTraceBuffer() {
|
snapshotTrace() {
|
||||||
var skip = trace_signals.length;
|
var arr = trace_signals;
|
||||||
var src = trace_index;
|
|
||||||
for (var dest=0; dest<idata.length; dest+=videoWidth) {
|
|
||||||
for (var i=0; i<skip; i++) {
|
|
||||||
if (--src < 0) src = trace_buffer.length-1;
|
|
||||||
var v = trace_buffer[src];
|
|
||||||
idata[dest+i] = RGBLOOKUP[v & 15]; // TODO?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshotTrace(signals) {
|
|
||||||
var arr = signals ? trace_signals : trace_ports;
|
|
||||||
for (var i=0; i<arr.length; i++) {
|
for (var i=0; i<arr.length; i++) {
|
||||||
var v = arr[i];
|
var v = arr[i];
|
||||||
var z = gen[v.name];
|
var z = gen[v.name];
|
||||||
trace_buffer[trace_index++] = z;
|
if (typeof(z) === 'number')
|
||||||
if (trace_index >= trace_buffer.length) trace_index = 0;
|
trace_buffer[trace_index] = z;
|
||||||
|
trace_index++;
|
||||||
}
|
}
|
||||||
|
if (trace_index >= trace_buffer.length - arr.length)
|
||||||
|
trace_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fillTraceBuffer(count) {
|
fillTraceBuffer(count:number) : boolean {
|
||||||
var max_index = Math.min(trace_buffer.length - trace_ports.length, trace_index + count);
|
var max_index = Math.min(trace_buffer.length - trace_signals.length, trace_index + count);
|
||||||
while (trace_index < max_index) {
|
while (trace_index < max_index) {
|
||||||
gen.clk ^= 1;
|
gen.clk ^= 1;
|
||||||
gen.eval();
|
gen.eval();
|
||||||
this.snapshotTrace(false);
|
this.snapshotTrace();
|
||||||
dirty = true;
|
if (trace_index == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
gen.__unreset();
|
gen.__unreset();
|
||||||
|
return (trace_index == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScopeFrame() {
|
getSignalMetadata() : WaveformMeta[] {
|
||||||
this.fillTraceBuffer(Math.floor(videoWidth/4) * trace_ports.length);
|
return trace_signals;
|
||||||
if (!dirty) return;
|
|
||||||
dirty = false;
|
|
||||||
scope_y_top = 0;
|
|
||||||
this.refreshScopeOverlay(trace_ports);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshScopeOverlay(arr) {
|
getSignalData(index:number, start:number, len:number) : number[] {
|
||||||
if (!sdata) {
|
// TODO: not efficient
|
||||||
scopeImageData = video.getContext().createImageData(scopeWidth,scopeHeight);
|
var skip = this.getSignalMetadata().length;
|
||||||
sdata = new Uint32Array(scopeImageData.data.buffer);
|
var last = trace_buffer.length - trace_signals.length; // TODO: refactor, and not correct
|
||||||
}
|
var wrap = this.hasvideo; // TODO?
|
||||||
var COLOR_BLACK = 0xff000000;
|
var a = [];
|
||||||
var COLOR_SIGNAL = 0xff22ff22;
|
index += skip * start;
|
||||||
var COLOR_BORDER = 0xff662222;
|
while (index < last && a.length < len) {
|
||||||
var COLOR_TRANS_SIGNAL = 0xff226622;
|
a.push(trace_buffer[index]);
|
||||||
var COLOR_BLIP_SIGNAL = 0xff226622;
|
index += skip;
|
||||||
sdata.fill(0xff000000);
|
if (wrap && index >= last) // TODO: what if starts with index==last
|
||||||
var jstart = scope_x_offset * arr.length + scope_index_offset;
|
index = 0;
|
||||||
var j = jstart;
|
|
||||||
for (var x=0; x<scopeWidth; x++) {
|
|
||||||
var yb = 8;
|
|
||||||
var y1 = scope_y_offset;
|
|
||||||
for (var i=0; i<arr.length; i++) {
|
|
||||||
var v = arr[i];
|
|
||||||
var lo = 0; // TODO? v.ofs?
|
|
||||||
var hi = ((1 << v.len)-1);
|
|
||||||
var ys = hi>1 ? 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 + 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) {
|
|
||||||
sdata[x + ly * scopeWidth] = COLOR_TRANS_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.getContext().putImageData(scopeImageData, 0, scope_y_top);
|
|
||||||
// draw labels
|
|
||||||
var ctx = video.getContext();
|
|
||||||
for (var i=0; i<arr.length; i++) {
|
|
||||||
var yp = yposlist[i];
|
|
||||||
if (yp < 20 || yp > videoHeight) continue;
|
|
||||||
var v = arr[i];
|
|
||||||
var name = v.name;
|
|
||||||
ctx.fillStyle = name == inspect_sym ? "yellow" : "white";
|
|
||||||
name = name.replace(/__DOT__/g,'.');
|
|
||||||
name = name.replace(module_name+'.','');
|
|
||||||
ctx.textAlign = 'left';
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
shadowText(ctx, name, 1, yposlist[i]);
|
|
||||||
if (scope_time_x > 0) {
|
|
||||||
ctx.textAlign = 'right';
|
|
||||||
var value = (arr.length * scope_time_x + i + jstart) % trace_buffer.length;
|
|
||||||
shadowText(ctx, ""+trace_buffer[value], videoWidth-1, yp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// draw scope line & label
|
|
||||||
if (scope_time_x > 0) {
|
|
||||||
ctx.fillStyle = "cyan";
|
|
||||||
shadowText(ctx, ""+(scope_time_x+scope_x_offset),
|
|
||||||
(scope_time_x>10)?(scope_time_x-2):(scope_time_x+20), videoHeight-2);
|
|
||||||
ctx.fillRect(scope_time_x, 0, 1, 4000);
|
|
||||||
}
|
|
||||||
// scroll left/right
|
|
||||||
if (scope_time_x >= videoWidth && scope_x_offset < (trace_buffer.length / arr.length) - videoWidth) {
|
|
||||||
scope_x_offset += 1 + (scope_time_x - videoWidth);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
else if (scope_time_x < 0 && scope_x_offset > 0) {
|
|
||||||
scope_x_offset = Math.max(0, scope_x_offset + scope_time_x);
|
|
||||||
dirty = true;
|
|
||||||
}
|
}
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
printErrorCodeContext(e, code) {
|
printErrorCodeContext(e, code) {
|
||||||
@ -664,11 +577,14 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
gen.__proto__ = base;
|
gen.__proto__ = base;
|
||||||
current_output = output;
|
current_output = output;
|
||||||
module_name = output.name ? output.name.substr(1) : "top";
|
module_name = output.name ? output.name.substr(1) : "top";
|
||||||
trace_ports = current_output.ports;
|
//trace_ports = current_output.ports;
|
||||||
trace_signals = current_output.ports.concat(current_output.signals);
|
trace_signals = current_output.ports.concat(current_output.signals); // combine ports + signals
|
||||||
|
trace_signals = trace_signals.filter((v) => { return !v.name.startsWith("__V"); }); // remove __Vclklast etc
|
||||||
trace_index = 0;
|
trace_index = 0;
|
||||||
// power on module
|
// power on module
|
||||||
this.poweron();
|
this.poweron();
|
||||||
|
// query output
|
||||||
|
this.hasvideo = gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// replace program ROM, if using the assembler
|
// replace program ROM, if using the assembler
|
||||||
@ -685,6 +601,11 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
}
|
}
|
||||||
// restart audio
|
// restart audio
|
||||||
this.restartAudio();
|
this.restartAudio();
|
||||||
|
// destroy scope
|
||||||
|
if (this.waveview) {
|
||||||
|
this.waveview.destroy();
|
||||||
|
this.waveview = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restartAudio() {
|
restartAudio() {
|
||||||
@ -735,9 +656,8 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
}
|
}
|
||||||
reset() {
|
reset() {
|
||||||
gen.__reset();
|
gen.__reset();
|
||||||
trace_index = scope_x_offset = 0;
|
trace_index = 0;
|
||||||
if (trace_buffer) trace_buffer.fill(0);
|
if (trace_buffer) trace_buffer.fill(0);
|
||||||
dirty = true;
|
|
||||||
if (video) video.setRotate(gen.rotate ? -90 : 0);
|
if (video) video.setRotate(gen.rotate ? -90 : 0);
|
||||||
}
|
}
|
||||||
tick() {
|
tick() {
|
||||||
@ -769,7 +689,6 @@ var VerilogPlatform = function(mainElement, options) {
|
|||||||
} else {
|
} else {
|
||||||
inspect_obj = inspect_sym = null;
|
inspect_obj = inspect_sym = null;
|
||||||
}
|
}
|
||||||
dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUGGING
|
// DEBUGGING
|
||||||
|
231
src/waveform.ts
Normal file
231
src/waveform.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
|
||||||
|
declare var VirtualList;
|
||||||
|
declare var Mousetrap;
|
||||||
|
|
||||||
|
export interface WaveformMeta {
|
||||||
|
name : string;
|
||||||
|
len : number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WaveformProvider {
|
||||||
|
getSignalMetadata() : WaveformMeta[];
|
||||||
|
getSignalData(index:number, start:number, len:number) : number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WaveformView {
|
||||||
|
parent : HTMLElement;
|
||||||
|
wfp : WaveformProvider;
|
||||||
|
wavelist;
|
||||||
|
meta : WaveformMeta[];
|
||||||
|
lines : HTMLCanvasElement[] = [];
|
||||||
|
zoom : number = 8;
|
||||||
|
t0 : number = 0;
|
||||||
|
tsel : number = -1;
|
||||||
|
pageWidth : number;
|
||||||
|
clocksPerPage : number;
|
||||||
|
clockMax : number;
|
||||||
|
|
||||||
|
constructor(parent:HTMLElement, wfp:WaveformProvider) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.wfp = wfp;
|
||||||
|
this.recreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
wtimer;
|
||||||
|
recreate() {
|
||||||
|
clearTimeout(this.wtimer);
|
||||||
|
this.wtimer = setTimeout(() => {
|
||||||
|
this.destroy();
|
||||||
|
// create new thing
|
||||||
|
this._recreate();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
// remove old thing
|
||||||
|
if (this.wavelist) {
|
||||||
|
$(this.parent).empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_recreate() {
|
||||||
|
this.meta = this.wfp.getSignalMetadata();
|
||||||
|
if (!this.meta) return;
|
||||||
|
var width = this.pageWidth = $(this.parent).width();
|
||||||
|
var rowHeight = 40; // TODO
|
||||||
|
this.clocksPerPage = Math.floor(this.pageWidth/this.zoom) - 1;
|
||||||
|
this.clockMax = 0;
|
||||||
|
this.wavelist = new VirtualList({
|
||||||
|
w: width,
|
||||||
|
h: $(this.parent).height(),
|
||||||
|
itemHeight: rowHeight,
|
||||||
|
totalRows: this.meta.length,
|
||||||
|
generatorFn: (row : number) => {
|
||||||
|
var s = this.meta[row].name;
|
||||||
|
var linediv = document.createElement("div");
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.width = width - 4;
|
||||||
|
canvas.height = rowHeight;
|
||||||
|
linediv.appendChild(canvas); //document.createTextNode(s));
|
||||||
|
linediv.classList.add('waverow');
|
||||||
|
this.lines[row] = canvas;
|
||||||
|
this.refreshRow(row);
|
||||||
|
return linediv;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var wlc = this.wavelist.container;
|
||||||
|
wlc.tabIndex = -1; // make it focusable
|
||||||
|
//wlc.style = "overflow-x: hidden"; // TODO?
|
||||||
|
$(this.parent).append(wlc);
|
||||||
|
var down = false;
|
||||||
|
var selfn = (e) => {
|
||||||
|
this.setSelTime(e.offsetX / this.zoom + this.t0);
|
||||||
|
};
|
||||||
|
$(wlc).mousedown( (e) => {
|
||||||
|
down = true;
|
||||||
|
selfn(e);
|
||||||
|
//if (e['pointerId']) e.target.setPointerCapture(e['pointerId']);
|
||||||
|
});
|
||||||
|
$(wlc).mousemove( (e) => {
|
||||||
|
if (down) selfn(e);
|
||||||
|
});
|
||||||
|
$(wlc).mouseup( (e) => {
|
||||||
|
down = false;
|
||||||
|
//if (e['pointerId']) e.target.releasePointerCapture(e['pointerId']);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('+', (e,combo) => {
|
||||||
|
this.setZoom(this.zoom * 2);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('-', (e,combo) => {
|
||||||
|
this.setZoom(this.zoom / 2);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('left', (e,combo) => {
|
||||||
|
this.setSelTime(this.tsel - 1);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('right', (e,combo) => {
|
||||||
|
this.setSelTime(this.tsel + 1);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('ctrl+left', (e,combo) => {
|
||||||
|
this.setSelTime(this.tsel - this.clocksPerPage/4);
|
||||||
|
});
|
||||||
|
Mousetrap(wlc).bind('ctrl+right', (e,combo) => {
|
||||||
|
this.setSelTime(this.tsel + this.clocksPerPage/4);
|
||||||
|
});
|
||||||
|
$(window).resize(() => {
|
||||||
|
this.recreate();
|
||||||
|
}); // TODO: remove?
|
||||||
|
// assign buttons
|
||||||
|
$("#scope_go_start").click(() => {
|
||||||
|
this.setOrgTime(0);
|
||||||
|
});
|
||||||
|
$("#scope_go_end").click(() => {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
$("#scope_go_fwd").click(() => {
|
||||||
|
this.setOrgTime(this.t0 + this.clocksPerPage/4);
|
||||||
|
});
|
||||||
|
$("#scope_go_back").click(() => {
|
||||||
|
this.setOrgTime(this.t0 - this.clocksPerPage/4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
roundT(t : number) {
|
||||||
|
t = Math.round(t);
|
||||||
|
t = Math.max(0, t); // make sure >= 0
|
||||||
|
t = Math.min(this.clockMax + this.clocksPerPage/2, t); // make sure <= end
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrgTime(t : number) {
|
||||||
|
this.t0 = this.roundT(t);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
setEndTime(t : number) {
|
||||||
|
this.setOrgTime(t - this.clocksPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelTime(t : number) {
|
||||||
|
t = this.roundT(t);
|
||||||
|
if (t > this.t0 + this.clocksPerPage)
|
||||||
|
this.t0 += this.clocksPerPage / 4;
|
||||||
|
if (t < this.t0)
|
||||||
|
this.t0 -= this.clocksPerPage / 4;
|
||||||
|
this.tsel = t;
|
||||||
|
this.setOrgTime(this.t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setZoom(zoom : number) {
|
||||||
|
this.zoom = Math.max(1, zoom);
|
||||||
|
this.clocksPerPage = Math.ceil(this.pageWidth/this.zoom); // TODO: refactor into other one
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
if (!this.meta) this.recreate();
|
||||||
|
if (!this.meta) return;
|
||||||
|
for (var i=0; i<this.meta.length; i++) {
|
||||||
|
this.refreshRow(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRow(row : number) {
|
||||||
|
var canvas = this.lines[row];
|
||||||
|
var meta = this.meta[row];
|
||||||
|
if (!canvas || !meta) return;
|
||||||
|
var isclk = (meta.name == 'clk');
|
||||||
|
var w = canvas.width;
|
||||||
|
var h = canvas.height;
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
ctx.font = "14px Andale Mono, Lucida Console, monospace";
|
||||||
|
// clear to black
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
// draw waveform
|
||||||
|
ctx.strokeStyle = "#66ff66";
|
||||||
|
var b = 4;
|
||||||
|
var h2 = h-16-b;
|
||||||
|
var yrange = ((1<<meta.len)-1) || 0;
|
||||||
|
var data = this.wfp.getSignalData(row, this.t0, Math.ceil(w/this.zoom));
|
||||||
|
this.clockMax = Math.max(this.clockMax, this.t0 + data.length);
|
||||||
|
ctx.beginPath();
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
for (var i=0; i<data.length; i++) {
|
||||||
|
if (i>0)
|
||||||
|
ctx.lineTo(x,y);
|
||||||
|
y = b + (1.0 - data[i]/yrange) * h2;
|
||||||
|
if (!isclk) x += this.zoom*(1/8);
|
||||||
|
if (i==0)
|
||||||
|
ctx.moveTo(x,y);
|
||||||
|
else
|
||||||
|
ctx.lineTo(x,y);
|
||||||
|
if (isclk)
|
||||||
|
x += this.zoom;
|
||||||
|
else
|
||||||
|
x += this.zoom*(7/8);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
// draw selection thingie
|
||||||
|
if (this.tsel >= this.t0) {
|
||||||
|
ctx.strokeStyle = ctx.fillStyle = "#ff66ff";
|
||||||
|
ctx.beginPath();
|
||||||
|
x = (this.tsel - this.t0)*this.zoom + this.zoom/2;
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, h);
|
||||||
|
ctx.stroke();
|
||||||
|
// print value
|
||||||
|
var val = data[this.tsel - this.t0];
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
if (val !== undefined) {
|
||||||
|
ctx.fillText(val.toString(), w-b*2, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// draw labels
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.textAlign = "left";
|
||||||
|
var name = meta.name;
|
||||||
|
name = name.replace(/__DOT__/g, "."); // make nicer name
|
||||||
|
ctx.fillText(name, 5, h-b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
241
testwave.html
Normal file
241
testwave.html
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>8bitworkshop IDE</title>
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
body {
|
||||||
|
overflow: hidden !important;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.waverow {
|
||||||
|
background: #000;
|
||||||
|
color: #99ff99;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="css/ui.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="controls_top">
|
||||||
|
<span class="dropdown">
|
||||||
|
<a class="btn btn-secondary dropdown-toggle" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Menu">
|
||||||
|
<span class="glyphicon glyphicon-menu-hamburger" aria-hidden="true"></span></button>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_new_file">New Project...</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_upload_file">Upload File...</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_reset_file">Revert to Original...</a></li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Download</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_download_file">Download Source File</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_download_rom">Download ROM Image</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_download_zip">Download Project as ZIP</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_download_allzip">Download All Changes as ZIP</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Share</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_record_video">Record Video...</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_share_file">Share Playable Link...</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Debug</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="#" id="item_debug_expr">Break Expression...</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Tools</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" target="_8bws_tools" href="./tools/fontgen/">Bitmap Font Generator</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<hr>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Platform</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Game Consoles</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="?platform=vcs" id="item_platform_vcs">Atari 2600/VCS</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Computers</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="?platform=apple2" id="item_platform_apple2">Apple ][+</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown dropdown-submenu">
|
||||||
|
<a tabindex="-1" href="#">Arcade Systems</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<!--<li><a class="dropdown-item" href="?platform=vcs-mame" id="item_platform_vcs">Atari VCS (MAME)</a></li>-->
|
||||||
|
<!--<li><a class="dropdown-item" href="?platform=apple2" id="item_platform_apple2">Apple ][</a></li>-->
|
||||||
|
<li><a class="dropdown-item" href="?platform=vicdual" id="item_platform_vicdual">VIC Dual</a></li>
|
||||||
|
<li><a class="dropdown-item" href="?platform=mw8080bw" id="item_platform_mw8080bw">Midway 8080</a></li>
|
||||||
|
<li><a class="dropdown-item" href="?platform=galaxian-scramble" id="item_platform_galaxian_scramble">Galaxian/Scramble Hardware</a></li>
|
||||||
|
<li><a class="dropdown-item" href="?platform=vector-z80color" id="item_platform_vector_z80color">Atari Color Vector (Z80)</a></li>
|
||||||
|
<li><a class="dropdown-item" href="?platform=williams-z80" id="item_platform_williams_z80">Williams (Z80)</a></li>
|
||||||
|
<li><a class="dropdown-item" href="?platform=sound_williams-z80" id="item_platform_sound_williams_z80">Williams Sound (Z80)</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<select id="preset_select" name="" title="Project Select">
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<span class="dropdown">
|
||||||
|
<a class="btn btn-secondary dropdown-toggle" id="windowMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Window Select">
|
||||||
|
<span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span></button>
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="windowMenuButton" id="windowMenuList">
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<img id="compile_spinner" src="images/spinner.gif" height="20em" style="visibility:hidden;margin-left:8px;margin-right:8px">
|
||||||
|
<span class="btn_group debug_group" id="debug_bar">
|
||||||
|
<button id="dbg_reset" type="submit" title="Reset and Break"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_pause" type="button" title="Pause"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_go" type="button" title="Run"><span class="glyphicon glyphicon-play" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_step" type="submit" title="Step"><span class="glyphicon glyphicon-step-forward" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_tovsync" type="submit" title="Single Frame"><span class="glyphicon glyphicon-forward" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_toline" type="submit" title="Run To Line"><span class="glyphicon glyphicon-save" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_stepout" type="submit" title="Step Out of Subroutine"><span class="glyphicon glyphicon-hand-up" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_stepback" type="submit" title="Step Backwards"><span class="glyphicon glyphicon-step-backward" aria-hidden="true"></span></button>
|
||||||
|
</span>
|
||||||
|
<span class="btn_group view_group" id="extra_bar">
|
||||||
|
<button id="dbg_timing" type="submit" title="Analyze CPU Timing" style="display:none"><span class="glyphicon glyphicon-time" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_disasm" type="submit" title="Show Disassembly" style="display:none"><span class="glyphicon glyphicon-list" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_memory" type="submit" title="Show Memory" style="display:none"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_profile" type="submit" title="Show Profile" style="display:none"><span class="glyphicon glyphicon-stats" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_bitmap" type="submit" title="Edit Bitmap"><span class="glyphicon glyphicon-camera" aria-hidden="true"></span></button>
|
||||||
|
<button id="dbg_record" type="submit" title="Start/Stop Replay Recording" style="display:none"><span class="glyphicon glyphicon-record" aria-hidden="true"></span></button>
|
||||||
|
</span>
|
||||||
|
<span class="dropdown" style="float:right">
|
||||||
|
<a class="btn btn-secondary dropdown-toggle" id="booksMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
GET BOOKS <span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu pull-right" aria-labelledby="dropdownMenuButton">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item dropdown-link" target="_book_a2600" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&tag=pzp-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B01N4DSRIZ&linkId=04d39e274c06e6c93b93d20a9a977111">
|
||||||
|
<img src="images/book_a2600.png"/>
|
||||||
|
<b>Making Games For The Atari 2600</b><!-- (Print/Kindle Editions)-->
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item dropdown-link" target="_book_arcade" href="https://www.amazon.com/gp/product/1545484759/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1545484759&linkCode=as2&tag=pzp-20&linkId=b27709c022d2ebe639e90316d9f4fd5b">
|
||||||
|
<img src="images/book_arcade.png"/>
|
||||||
|
<b>Making 8-bit Arcade Games in C</b><!-- (Print Edition)-->
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
<span class="btn_group view_group" id="scope_bar">
|
||||||
|
<button id="scope_go_start" type="submit" title="Go to beginning of trace"><span class="glyphicon glyphicon-fast-backward" aria-hidden="true"></span></button>
|
||||||
|
<button id="scope_go_back" type="submit" title="Go back a page"><span class="glyphicon glyphicon-backward" aria-hidden="true"></span></button>
|
||||||
|
<input id="scope_index_text" size="5"></input>
|
||||||
|
<button id="scope_go_fwd" type="submit" title="Go forward a page"><span class="glyphicon glyphicon-forward" aria-hidden="true"></span></button>
|
||||||
|
<button id="scope_go_end" type="submit" title="Go to end of trace"><span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="notebook">
|
||||||
|
<div id="workspace">
|
||||||
|
</div>
|
||||||
|
<div class="emulator" id="emulator">
|
||||||
|
<div class="emuoverlay" id="emuoverlay">
|
||||||
|
<div id="scope" class="emuscope">
|
||||||
|
</div>
|
||||||
|
<div id="scope2" class="emuscope">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="error_alert" class="alert alert-danger alert-dismissable" style="position:absolute;right:0;top:0;display:none">
|
||||||
|
<!--<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>-->
|
||||||
|
<div id="error_alert_msg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="jquery/jquery-2.2.3.min.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
|
||||||
|
<script src="bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<script src="lib/mousetrap.min.js"></script>
|
||||||
|
<script src="lib/split.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var exports = {};
|
||||||
|
function require(modname) {
|
||||||
|
if (modname == 'jquery') return $;
|
||||||
|
else if (modname.startsWith('.')) return exports;
|
||||||
|
else { console.log("Unknown require()", modname); return exports; }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="tss/js/tss/PsgDeviceChannel.js"></script>
|
||||||
|
<script src="tss/js/tss/MasterChannel.js"></script>
|
||||||
|
<script src="tss/js/tss/AudioLooper.js"></script>
|
||||||
|
<script src="tss/js/Log.js"></script>
|
||||||
|
|
||||||
|
<script src="gen/util.js"></script>
|
||||||
|
<script src="src/vlist.js"></script>
|
||||||
|
<script src="gen/emu.js"></script>
|
||||||
|
<script src="gen/baseplatform.js"></script>
|
||||||
|
<script src="gen/waveform.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var wfp = {
|
||||||
|
getSignalMetadata: function() {
|
||||||
|
return [
|
||||||
|
{name:'clk', len:1},
|
||||||
|
{name:'big_signal_name__DOT__which_is_big', len:2},
|
||||||
|
{name:'sig3', len:3},
|
||||||
|
{name:'sig4', len:4},
|
||||||
|
{name:'sig5', len:5},
|
||||||
|
{name:'sig6', len:6},
|
||||||
|
{name:'sig7', len:7},
|
||||||
|
{name:'sig8', len:8},
|
||||||
|
{name:'sig9', len:9},
|
||||||
|
{name:'sig', len:1},{name:'sig', len:1},{name:'sig', len:1},{name:'sig', len:1},{name:'sig', len:1},{name:'sig', len:1},{name:'sig', len:1},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
getSignalData: function(i,s,l) {
|
||||||
|
l += s;
|
||||||
|
var arr = [];
|
||||||
|
while (s++<l) {
|
||||||
|
arr.push(s & (1<<(this.getSignalMetadata()[i].len)-1));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var wfv = new WaveformView($('#scope')[0], wfp);
|
||||||
|
var wfv2 = new WaveformView($('#scope2')[0], wfp);
|
||||||
|
|
||||||
|
Split(['#scope', '#scope2'], {
|
||||||
|
minSize: [50, 50],
|
||||||
|
direction: 'vertical',
|
||||||
|
onDrag: function() {
|
||||||
|
wfv.recreate();
|
||||||
|
wfv2.recreate();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
function update() {
|
||||||
|
wfv.setOrgTime(wfv.t0+1);
|
||||||
|
setTimeout(update, 1000/10);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
*/
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user