support `include statements in verilog; book link changes; paddle/switches; scope transitions

This commit is contained in:
Steven Hugg 2017-11-14 19:12:52 -05:00
parent 4256ee7bc2
commit 4f73cde7cc
13 changed files with 3855 additions and 47 deletions

View File

@ -100,6 +100,10 @@ div.mem_info {
}
.btn_group.view_group {
}
.btn_active {
border-radius:3px;
color: #ffffff;
}
.seg_code { color: #ff9966; }
.seg_data { color: #66ff66; }
.seg_stack { color: #ffff66; }

View File

@ -110,14 +110,14 @@ body {
<a target="_new" 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.jpg" style="float:right"/></a>
Want to learn more?<br>
Get the book<br>
Get the book:<br>
<a target="_new" 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">
Making Games For The Atari 2600</a>
</div>
<div class="booklink" id="booklink_arcade">
<a target="_new" href="https://www.amazon.com/gp/product/B0713RQL8X/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B0713RQL8X&linkCode=as2&tag=pzp-20&linkId=e8e05e34acf1b54d81aced148a67790c">
<img height="72em" src="images/book_arcade.jpg" style="float:right"/></a>
New book!<br>
Get the book:<br>
<a target="_new" href="https://www.amazon.com/Making-8-bit-Arcade-Games-C/dp/1545484759">
Making 8-bit Arcade Games in C</a>
</div>

View File

@ -0,0 +1,38 @@
module clock_divider(
input clk,
output reg clk_div2,
output reg clk_div4,
output reg clk_div8,
output reg clk_div16,
output reg [3:0] counter,
output cntr_div2,
output cntr_div4,
output cntr_div8,
output cntr_div16
);
// simple ripple clock divider
always @(posedge clk)
clk_div2 <= ~clk_div2;
always @(posedge clk_div2)
clk_div4 <= ~clk_div4;
always @(posedge clk_div4)
clk_div8 <= ~clk_div8;
always @(posedge clk_div8)
clk_div16 <= ~clk_div16;
// use bits of (4-bit) counter to divide clocks
always @(posedge clk)
counter <= counter + 1;
assign cntr_div2 = counter[0];
assign cntr_div4 = counter[1];
assign cntr_div8 = counter[2];
assign cntr_div16 = counter[3];
endmodule

View File

@ -1,13 +1,12 @@
module hvsync(
clk, hsync, vsync, inDisplayArea, CounterX, CounterY, pixel);
module hvsync_generator(
clk, hsync, vsync, inDisplayArea, CounterX, CounterY);
input clk;
output hsync, vsync;
output inDisplayArea;
output [8:0] CounterX;
output [8:0] CounterY;
output pixel;
// constant declarations for VGA sync parameters
localparam H_DISPLAY = 256; // horizontal display area
@ -28,11 +27,10 @@ module hvsync(
reg [8:0] CounterX;
reg [8:0] CounterY;
reg pixel;
wire CounterXmaxed = (CounterX==H_MAX);
wire CounterYmaxed = (CounterY==V_MAX);
always @(posedge clk)
always @(posedge clk)
if(CounterXmaxed)
CounterX <= 0;
else
@ -56,8 +54,6 @@ module hvsync(
always @(posedge clk)
begin
inDisplayArea <= (CounterX<H_DISPLAY) && (CounterY<V_DISPLAY);
pixel <= inDisplayArea &&
(((CounterX&7)==0) || ((CounterY&7)==0));
end
assign hsync = ~vga_HS;

19
presets/verilog/lfsr.v Normal file
View File

@ -0,0 +1,19 @@
module LFSR8_11D(
input clk,
output reg [7:0] LFSR = 255 // put here the initial value
);
wire feedback = LFSR[7];
always @(posedge clk)
begin
LFSR[0] <= feedback;
LFSR[1] <= LFSR[0];
LFSR[2] <= LFSR[1] ^ feedback;
LFSR[3] <= LFSR[2] ^ feedback;
LFSR[4] <= LFSR[3] ^ feedback;
LFSR[5] <= LFSR[4];
LFSR[6] <= LFSR[5];
LFSR[7] <= LFSR[6];
end
endmodule

3623
presets/verilog/pong.v Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
`include "hvsync_generator.v"
module test_hvsync_top(clk, hsync, vsync, rgb);
input clk;
output hsync, vsync;
output [2:0] rgb;
wire inDisplayArea;
wire [8:0] CounterX;
wire [8:0] CounterY;
hvsync_generator hvsync_gen(
.clk(clk),
.hsync(hsync),
.vsync(vsync),
.inDisplayArea(inDisplayArea),
.CounterX(CounterX),
.CounterY(CounterY)
);
wire r = inDisplayArea &&
(((CounterX&7)==0) || ((CounterY&7)==0));
wire g = inDisplayArea && CounterY[4];
wire b = inDisplayArea && CounterX[4];
assign rgb = {b,g,r};
endmodule

View File

@ -411,7 +411,7 @@ var SampleAudio = function(clockfreq) {
if (sfrac >= 1) {
sfrac -= 1;
accum = 0;
this.feedSingleSample(accum / sfrac);
this.addSingleSample(accum / sfrac);
}
}
}

View File

@ -1,10 +1,27 @@
"use strict";
var VERILOG_PRESETS = [
{id:'hvsync.v', name:'Hello Verilog'},
{id:'clock_divider.v', name:'Clock Divider'},
{id:'hvsync_generator.v', name:'Video Sync Generator'},
{id:'test_hvsync.v', name:'Test Pattern'},
{id:'lfsr.v', name:'Linear Feedback Shift Register'},
{id:'pong.v', name:'Pong'},
];
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_LEFT, 0, 0x1],
[Keys.VK_RIGHT, 0, 0x2],
[Keys.VK_UP, 0, 0x4],
[Keys.VK_DOWN, 0, 0x8],
[Keys.VK_SPACE, 0, 0x10],
[Keys.VK_SHIFT, 0, 0x20],
[Keys.VK_1, 0, 0x40],
[Keys.VK_2, 0, 0x80],
[Keys.VK_5, 0, 0x100],
[Keys.VK_6, 0, 0x200],
[Keys.VK_7, 0, 0x400],
]);
function VerilatorBase() {
this.VL_RAND_RESET_I = function(bits) { return Math.floor(Math.random() * (1<<bits)); }
@ -71,21 +88,19 @@ function VerilatorBase() {
var VerilogPlatform = function(mainElement, options) {
var self = this;
var video, audio;
var videoWidth=288;
var videoHeight=248;
var videoWidth = 288;
var videoHeight = 248;
var idata, timer;
var gen;
var frameRate = 60;
var AUDIO_FREQ = 15700;
var AUDIO_FREQ = 15750;
var current_output;
var paddle_x = 0;
var paddle_y = 0;
var switches = [0];
this.getPresets = function() { return VERILOG_PRESETS; }
function tick2() {
gen.tick2();
audio.addSingleSample(0+gen.audio); // TODO: sync with audio freq
}
var RGBLOOKUP = [
0xff111111,
0xff1111ff,
@ -97,24 +112,32 @@ var VerilogPlatform = function(mainElement, options) {
0xffffffff,
];
function vidtick() {
gen.tick2();
audio.addSingleSample(0+gen.spkr); // TODO: sync with audio freq
}
function updateVideoFrame() {
var i=0;
for (var y=0; y<videoHeight; y++) {
gen.hpaddle = y > paddle_x ? 1 : 0;
gen.vpaddle = y > paddle_y ? 1 : 0;
for (var x=0; x<videoWidth; x++) {
tick2();
vidtick();
idata[i++] = RGBLOOKUP[gen.rgb];
}
var z=0;
while (gen.hsync && z++<videoWidth) tick2();
while (!gen.hsync && z++<videoWidth) tick2();
while (gen.hsync && z++<videoWidth) vidtick();
while (!gen.hsync && z++<videoWidth) vidtick();
}
var z=0;
while (gen.vsync && z++<videoWidth*80) tick2();
while (!gen.vsync && z++<videoWidth*80) tick2();
while (gen.vsync && z++<videoWidth*80) vidtick();
while (!gen.vsync && z++<videoWidth*80) vidtick();
video.updateFrame();
}
var yposlist = [];
var lasty = [];
function updateScopeFrame() {
var arr = current_output.ports;
@ -125,6 +148,7 @@ var VerilogPlatform = function(mainElement, options) {
}
var COLOR_SIGNAL = 0xff11ff11;
var COLOR_BORDER = 0xff661111;
var COLOR_TRANS_SIGNAL = 0xff116611;
for (var x=0; x<videoWidth; x++) {
gen.clk ^= 1;
gen.eval();
@ -137,11 +161,19 @@ var VerilogPlatform = function(mainElement, options) {
var ys = hi>1 ? v.len*2+8 : 8;
var y2 = y1+ys;
var z = gen[v.name];
var y = y2 - ys*((z-lo)/hi);
var y = Math.round(y2 - ys*((z-lo)/hi));
yposlist[i] = y2;
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;
}
}
lasty[i] = y;
//idata[x + y1*videoWidth] = COLOR_BORDER;
//idata[x + y2*videoWidth] = COLOR_BORDER;
idata[x + Math.round(y)*videoWidth] = COLOR_SIGNAL;
idata[x + y*videoWidth] = COLOR_SIGNAL;
y1 += ys+yb;
}
}
@ -152,7 +184,10 @@ var VerilogPlatform = function(mainElement, options) {
ctx.fillStyle = "white";
for (var i=0; i<arr.length; i++) {
var v = arr[i];
ctx.textAlign = 'left';
ctx.fillText(v.name, 1, yposlist[i]);
//ctx.textAlign = 'right';
//ctx.fillText(""+gen[v.name], videoWidth-1, yposlist[i]);
}
}
@ -160,12 +195,18 @@ var VerilogPlatform = function(mainElement, options) {
// TODO
video = new RasterVideo(mainElement,videoWidth,videoHeight);
video.create();
setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP);
$(video.canvas).mousemove(function(e) {
paddle_x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width());
paddle_y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height() - 20);
});
audio = new SampleAudio(AUDIO_FREQ);
idata = video.getFrameData();
// TODO: 15.7 kHz?
timer = new AnimationTimer(frameRate, function() {
if (!self.isRunning())
return;
gen.switches = switches[0];
if (gen.vsync !== undefined && gen.hsync !== undefined && gen.rgb !== undefined)
updateVideoFrame();
else
@ -173,8 +214,27 @@ var VerilogPlatform = function(mainElement, options) {
});
}
function printErrorCodeContext(e, code) {
if (e.lineNumber && e.message) {
var lines = code.split('\n');
var s = e.message + '\n';
for (var i=0; i<lines.length; i++) {
if (i > e.lineNumber-5 && i < e.lineNumber+5) {
s += lines[i] + '\n';
}
}
console.log(s);
}
}
this.loadROM = function(title, output) {
var mod = new Function('base', output.code);
var mod;
try {
mod = new Function('base', output.code);
} catch (e) {
printErrorCodeContext(e, output.code);
throw e;
}
var base = new VerilatorBase();
gen = new mod(base);
gen.__proto__ = base;

View File

@ -208,7 +208,9 @@ function setLastPreset(id) {
}
function updatePreset(current_preset_id, text) {
if (text.trim().length && (originalFileID != current_preset_id || text != originalText)) {
// TODO: do we have to save all Verilog thingies?
if (text.trim().length &&
(originalFileID != current_preset_id || text != originalText || platform_id=='verilog')) {
store.saveFile(current_preset_id, text);
}
}
@ -389,11 +391,30 @@ function updateSelector() {
});
}
function loadFileDependencies(text) {
var arr = [];
if (platform_id == 'verilog') {
var re = /`include\s+"(.+)"/g;
var m;
while (m = re.exec(text)) {
arr.push({
filename:m[1],
text:store.loadFile(m[1]) // TODO: if missing?
});
}
}
return arr;
}
function setCode(text) {
if (pendingWorkerMessages++ > 0)
return;
worker.postMessage({code:text, platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)});
worker.postMessage({
code:text,
dependencies:loadFileDependencies(text),
platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)
});
toolbar.addClass("is-busy");
$('#compile_spinner').css('visibility', 'visible');
}
@ -437,7 +458,8 @@ function setCompileOutput(data) {
div.appendChild(document.createTextNode("\u24cd"));
var tooltip = document.createElement("span");
tooltip.setAttribute("class", "tooltiptext");
if (lines2errmsg[line]) msg = lines2errmsg[line] + "\n" + msg;
if (lines2errmsg[line])
msg = lines2errmsg[line] + "\n" + msg;
tooltip.appendChild(document.createTextNode(msg));
lines2errmsg[line] = msg;
div.appendChild(tooltip);
@ -1257,7 +1279,7 @@ function initPlatform() {
function showBookLink() {
if (platform_id == 'vcs')
$("#booklink_vcs").show();
else
else if (platform_id == 'mw8080bw' || platform_id == 'vicdual' || platform_id == 'galaxian-scramble' || platform_id == 'vector-z80color' || platform_id == 'williams-z80')
$("#booklink_arcade").show();
}

View File

@ -12,6 +12,17 @@ function parseDecls(text, arr, name, bin, bout) {
ofs:parseInt(m[4]),
});
}
re = new RegExp(name + "(\\d+)[(](\\w+)\\[(\\d+)\\],(\\d+),(\\d+)[)]", 'gm');
var m;
while ((m = re.exec(text))) {
arr.push({
wordlen:parseInt(m[1]),
name:m[2],
arrlen:parseInt(m[3]),
len:parseInt(m[4]),
ofs:parseInt(m[5]),
});
}
}
function buildModule(o) {
@ -22,7 +33,10 @@ function buildModule(o) {
m += "\tself." + o.ports[i].name + ";\n";
}
for (var i=0; i<o.signals.length; i++) {
m += "\tself." + o.signals[i].name + ";\n";
if (o.signals[i].arrlen)
m += "\tvar " + o.signals[i].name + " = self." + o.signals[i].name + " = [];\n";
else
m += "\tself." + o.signals[i].name + ";\n";
}
for (var i=0; i<o.funcs.length; i++) {
m += o.funcs[i];
@ -44,7 +58,9 @@ function translateFunction(text) {
text = text.replace(/\b([0-9]+)U/gi, '$1');
text = text.replace(/\bQData /, 'var ');
text = text.replace(/\bbool /, '');
text = text.replace(/\bint /, 'var ');
text = text.replace(/(\w+ = VL_RAND_RESET_I)/g, 'self.$1');
//text = text.replace(/(\w+\[\w+\] = VL_RAND_RESET_I)/g, 'self.$1');
text = text.replace(/^#/gm, '//#');
text = text.replace(/VL_LIKELY/g, '!!');
text = text.replace(/VL_UNLIKELY/g, '!!');

View File

@ -530,7 +530,7 @@ function parseCA65Listing(code, mapfile) {
return {lines:lines, errors:errors};
}
function assemblelinkCA65(code, platform, warnings) {
function assemblelinkCA65(code, platform) {
var params = PLATFORM_PARAMS[platform];
if (!params) throw Error("Platform not supported: " + platform);
var errors = "";
@ -871,14 +871,6 @@ function compileSDCC(code, platform) {
starttime();
SDCC.callMain(args);
endtime("compile");
/*
// ignore if all are warnings (TODO?)
var nwarnings = 0;
for (var err of msvc_errors) {
if (err.type && err.type.startsWith("warning"))
nwarnings++;
}
*/
// TODO: preprocessor errors w/ correct file
if (msvc_errors.length /* && nwarnings < msvc_errors.length*/) {
return {errors:msvc_errors};
@ -901,7 +893,6 @@ function compileSDCC(code, platform) {
result.asmlines = result.lines;
result.lines = result.srclines;
result.srclines = null;
//result.errors = result.errors.concat(warnings);
return result;
}
@ -1036,11 +1027,21 @@ function detectModuleName(code) {
return m ? m[1] : null;
}
function compileVerilator(code, platform) {
function writeDependencies(depends, FS, errors) {
if (depends) {
for (var i=0; i<depends.length; i++) {
var d = depends[i];
if (d.text)
FS.writeFile(d.filename, d.text, {encoding:'utf8'});
}
}
}
function compileVerilator(code, platform, options) {
loadWASM("verilator_bin");
load("verilator2js");
var errors = [];
var match_fn = makeErrorMatcher(errors, /%Error: (.+?:)?(\d+)?[:]?\s*(.+)/, 2, 3);
var match_fn = makeErrorMatcher(errors, /%(.+?): (.+?:)?(\d+)?[:]?\s*(.+)/i, 3, 4);
var verilator_mod = verilator_bin({
wasmBinary:wasmBlob['verilator_bin'],
noInitialRun:true,
@ -1055,6 +1056,7 @@ function compileVerilator(code, platform) {
var FS = verilator_mod['FS'];
//setupFS(FS);
FS.writeFile(topmod+".v", code);
writeDependencies(options.dependencies, FS, errors);
starttime();
verilator_mod.callMain(["--cc", "-O3", "--top-module", topmod, topmod+".v"]);
endtime("compile");
@ -1106,7 +1108,8 @@ onmessage = function(e) {
var platform = e.data.platform;
var toolfn = TOOLS[e.data.tool];
if (!toolfn) throw "no tool named " + e.data.tool;
var result = toolfn(code, platform);
var dependencies = e.data.dependencies;
var result = toolfn(code, platform, e.data);
result.params = PLATFORM_PARAMS[platform];
if (result) {
postMessage(result);

View File

@ -105,7 +105,7 @@ describe('Worker', function() {
compile('plasm', 'word x = ', 'apple2', done, 0, 0, 1);
});
it('should compile CC65', function(done) {
compile('cc65', 'int main() {\nint x=1;\nreturn x+2;\n}', 'nes-conio', done, 2947, 4);
compile('cc65', 'int main() {\nint x=1;\nreturn x+2;\n}', 'nes-conio', done, 40976, 3);
});
it('should NOT compile CC65', function(done) {
compile('cc65', 'int main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', 'nes-conio', done, 0, 0, 1);