smoother scope transition; slowest/fastest buttons; video width tweak

This commit is contained in:
Steven Hugg 2018-02-26 22:48:36 -06:00
parent f9158b24eb
commit b5c74234f3
8 changed files with 351 additions and 22 deletions

View File

@ -80,9 +80,11 @@ ga('send', 'pageview');
<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="speed_bar" style="display:none">
<button id="dbg_slowest" type="submit" title="Slowest"><span class="glyphicon glyphicon-fast-backward" aria-hidden="true"></span></button>
<button id="dbg_slower" type="submit" title="Slower"><span class="glyphicon glyphicon-backward" aria-hidden="true"></span></button>
<span id="fps_label">60.00</span> fps
<button id="dbg_faster" type="submit" title="Faster"><span class="glyphicon glyphicon-forward" aria-hidden="true"></span></button>
<button id="dbg_fastest" type="submit" title="Faster"><span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span></button>
</span>
<span class="btn_group view_group" id="extra_bar">
<button id="dbg_timing" type="submit" title="See Timing" style="display:none"><span class="glyphicon glyphicon-time" aria-hidden="true"></span></button>

View File

@ -1,3 +1,6 @@
`ifndef FONT_CP437_H
`define FONT_CP437_H
// PC font (code page 437)
module font_cp437_8x8(addr, data);
@ -270,3 +273,5 @@ module font_cp437_8x8(addr, data);
};
end
endmodule
`endif

126
presets/verilog/maze_game.v Normal file
View File

@ -0,0 +1,126 @@
`include "hvsync_generator.v"
`include "font_cp437_8x8.v"
`include "ram.v"
`include "tile_renderer.v"
`include "sprite_scanline_renderer.v"
`include "lfsr.v"
`include "sound_generator.v"
`include "cpu8.v"
`include "cpu16.v"
module maze_game_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;
output [3:0] rgb;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
// video RAM bus
wire [7:0] vram_read;
reg [7:0] vram_write = 0;
reg vram_writeenable = 0;
// multiplex sprite and tile RAM
wire sprite_ram_select = (vpos == 256);
reg [15:0] tile_ram_addr;
reg [6:0] sprite_ram_addr;
// tile and sprite ROM
wire [10:0] tile_rom_addr;
wire [7:0] tile_rom_data;
wire [15:0] sprite_rom_addr;
wire [15:0] sprite_rom_data;
// gfx outputs
wire [3:0] tile_rgb;
wire [3:0] sprite_rgb;
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
// video RAM (16k)
RAM #(14,8) vram(
.clk(clk),
.dout(vram_read),
.din(vram_write),
.addr(sprite_ram_select
? {7'b0111111, sprite_ram_addr}
: tile_ram_addr[13:0]),
.we(vram_writeenable)
);
tile_renderer tile_gen(
.clk(clk),
.reset(reset),
.hpos(hpos),
.vpos(vpos),
.display_on(display_on),
.ram_addr(tile_ram_addr),
.ram_read(vram_read),
.rom_addr(tile_rom_addr),
.rom_data(tile_rom_data),
.rgb(tile_rgb)
);
sprite_scanline_renderer ssr(
.clk(clk),
.reset(reset),
.hpos(hpos),
.vpos(vpos),
.ram_addr(sprite_ram_addr),
.ram_data(vram_read),
.rom_addr(sprite_rom_addr),
.rom_data(sprite_rom_data),
.rgb(sprite_rgb)
);
font_cp437_8x8 tile_rom(
.addr(tile_rom_addr),
.data(tile_rom_data)
);
example_bitmap_rom bitmap_rom(
.addr(sprite_rom_addr),
.data(sprite_rom_data)
);
assign rgb = display_on
? (sprite_rgb>0 ? sprite_rgb : tile_rgb)
: 0;
// CPU RAM (32k x 16 bits)
RAM #(15,16) mram(
.clk(clk),
.dout(cpuram_read),
.din(cpuram_write),
.addr(cpuram_addr[14:0]),
.we(cpuram_writeenable)
);
reg [15:0] cpuram_read;
reg [15:0] cpuram_write;
reg [15:0] cpuram_addr;
reg cpuram_writeenable;
wire busy;
CPU16 cpu(
.clk(clk),
.reset(reset),
.hold(0),
.busy(busy),
.address(cpuram_addr),
.data_in(cpuram_read),
.data_out(cpuram_write),
.write(cpuram_writeenable));
endmodule

View File

@ -1,3 +1,6 @@
`ifndef RAM_H
`define RAM_H
`include "hvsync_generator.v"
module RAM(clk, addr, din, dout, we);
@ -21,3 +24,4 @@ module RAM(clk, addr, din, dout, we);
endmodule
`endif

View File

@ -1,4 +1,5 @@
`include "hvsync_generator.v"
`include "ram.v"
module example_bitmap_rom(addr, data);
@ -31,6 +32,7 @@ module example_bitmap_rom(addr, data);
endmodule
module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
ram_addr, ram_data,
rom_addr, rom_data);
parameter NB = 5;
@ -43,7 +45,9 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
input [8:0] hpos;
input [8:0] vpos;
output [3:0] rgb;
output [NB+1:0] ram_addr;
input [7:0] ram_data;
output [15:0] rom_addr;
input [15:0] rom_data;
@ -66,7 +70,7 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
wire [8:0] read_bufidx = {vpos[0], hpos[7:0]};
reg [15:0] out_bitmap;
reg [7:0] out_attr;
wire [NB-1:0] move_index = hpos[NB-1:0];
wire [NB-1:0] load_index = hpos[NB+2:3];
/*
0: read sprite_ypos[i]
@ -85,10 +89,25 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
// reset every frame, don't draw vpos >= 256
if (reset || vpos[8]) begin
// wiggle sprites randomly once per frame
if (vpos == 256 && hpos < N) begin
sprite_xpos[move_index] <= sprite_xpos[move_index] + 8'(($random&3)-1);
sprite_ypos[move_index] <= sprite_ypos[move_index] + 8'(($random&3)-1);
// load sprites from RAM on line 260
// 8 cycles per sprite
if (vpos == 260 && hpos < N*8) begin
case (hpos[2:0])
0: begin
ram_addr <= {load_index, 2'b00};
end
2: begin
sprite_xpos[load_index] <= ram_data;
ram_addr <= {load_index, 2'b01};
end
4: begin
sprite_ypos[load_index] <= ram_data;
ram_addr <= {load_index, 2'b10};
end
6: begin
sprite_attr[load_index] <= ram_data;
end
endcase
end
end else if (hpos < N*2) begin
k <= 0;
@ -183,14 +202,45 @@ module test_scanline_render_top(clk, reset, hsync, vsync, rgb);
.data(rom_data)
);
wire [6:0] ram_addr;
wire [7:0] ram_read;
reg [7:0] ram_write;
reg ram_we;
// 128-byte RAM
RAM #(7,8) ram(
.clk(clk),
.addr(ram_addr),
.dout(ram_read),
.din(ram_write),
.we(ram_we)
);
sprite_scanline_renderer ssr(
.clk(clk),
.reset(reset),
.hpos(hpos),
.vpos(vpos),
.rgb(rgb),
.ram_addr(ram_addr),
.ram_data(ram_read),
.rom_addr(rom_addr),
.rom_data(rom_data)
);
always @(posedge clk) begin
// wiggle sprites randomly once per frame
if (vpos == 256) begin
ram_addr <= hpos[8:2];
// 4 clocks per read/write cycle
if (!hpos[1]) begin
ram_we <= 0;
end else begin
ram_we <= 1;
ram_write <= ram_read + 8'(($random&3)-1);
end
end else
ram_we <= 0;
end
endmodule

View File

@ -0,0 +1,126 @@
`include "hvsync_generator.v"
`include "font_cp437_8x8.v"
`include "ram.v"
module tile_renderer(clk, reset, hpos, vpos, display_on,
rgb,
ram_addr, ram_read,
rom_addr, rom_data);
input clk, reset;
input [8:0] hpos;
input [8:0] vpos;
input display_on;
output [3:0] rgb;
output reg [15:0] ram_addr;
input [7:0] ram_read;
output [10:0] rom_addr;
input [7:0] rom_data;
reg [7:0] page_base = 0; // page table base (8 bits)
reg [15:0] row_base; // row table base (16 bits)
wire [4:0] row = vpos[7:3]; // 5-bit row, vpos / 8
wire [4:0] col = hpos[7:3]; // 5-bit column, hpos / 8
wire [2:0] yofs = vpos[2:0]; // scanline of cell (0-7)
wire [2:0] xofs = hpos[2:0]; // which pixel to draw (0-7)
reg [7:0] char;
reg [7:0] attr;
reg [7:0] next_char;
reg [7:0] next_attr;
// tile ROM address
assign rom_addr = {char, yofs};
// lookup char and attr
always @(posedge clk)
if (hpos[8]) begin
case (hpos[7:0])
// read row_base from page table (2 bytes)
// TODO: why 2 cycles?
0: ram_addr <= {page_base, row, 3'b000};
2: row_base[7:0] <= ram_read;
3: ram_addr <= {page_base, row, 3'b001};
5: row_base[15:8] <= ram_read;
endcase
end else begin
case (hpos[2:0])
0: ram_addr <= row_base + 16'(col);
2: next_char <= ram_read;
3: ram_addr <= row_base + 16'(col) + 32;
5: next_attr <= ram_read;
7: begin
char <= next_char;
attr <= next_attr;
end
endcase
end
// extract bit from ROM output
assign rgb = display_on
? (rom_data[~xofs] ? attr[3:0] : attr[7:4])
: 0;
endmodule
module test_tilerender_top(clk, reset, hsync, vsync, rgb);
input clk, reset;
output hsync, vsync;
output [3:0] rgb;
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
reg [15:0] ram_addr;
wire [7:0] ram_read;
reg [7:0] ram_write = 0;
reg ram_writeenable = 0;
wire [10:0] rom_addr;
wire [7:0] rom_data;
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
.hsync(hsync),
.vsync(vsync),
.display_on(display_on),
.hpos(hpos),
.vpos(vpos)
);
// RAM
RAM #(16,8) ram(
.clk(clk),
.dout(ram_read),
.din(ram_write),
.addr(ram_addr),
.we(ram_writeenable)
);
tile_renderer tile_gen(
.clk(clk),
.reset(reset),
.hpos(hpos),
.vpos(vpos),
.display_on(display_on),
.ram_addr(ram_addr),
.ram_read(ram_read),
.rom_addr(rom_addr),
.rom_data(rom_data),
.rgb(rgb)
);
// tile ROM
font_cp437_8x8 tile_rom(
.addr(rom_addr),
.data(rom_data)
);
endmodule

View File

@ -21,7 +21,9 @@ var VERILOG_PRESETS = [
{id:'cpu8.v', name:'Simple 8-Bit CPU'},
{id:'racing_game_cpu.v', name:'Racing Game with CPU'},
{id:'framebuffer.v', name:'Frame Buffer'},
{id:'tile_renderer.v', name:'Tile Renderer'},
{id:'sprite_scanline_renderer.v', name:'Sprite Scanline Renderer'},
{id:'maze_game.v', name:'Maze Game'},
];
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
@ -186,8 +188,8 @@ var VerilogPlatform = function(mainElement, options) {
var self = this;
var video, audio;
var useAudio = false;
var videoWidth = 256+20;
var videoHeight = 240+16;
var videoWidth = 292;
var videoHeight = 256;
var maxVideoLines = 262+40; // vertical hold
var idata, timer, timerCallback;
var gen;
@ -204,6 +206,7 @@ var VerilogPlatform = function(mainElement, options) {
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;
@ -276,9 +279,9 @@ var VerilogPlatform = function(mainElement, options) {
}
}
var framex=videoWidth-10;
var framex=0;
var framey=0;
var frameidx=videoWidth-10;
var frameidx=0;
var framehsync=false;
var framevsync=false;
@ -306,14 +309,14 @@ var VerilogPlatform = function(mainElement, options) {
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
framex = 0;
frameidx = 0;
} else {
var wasvsync = framevsync;
framevsync = false;
if (sync && wasvsync) return; // exit when vsync ends
}
}
}
@ -333,24 +336,27 @@ var VerilogPlatform = function(mainElement, options) {
}
// paint into frame, synched with vsync if full speed
var sync = fps > 45;
var trace = fps < 0.1;
var trace = fps < 0.02;
updateVideoFrameCycles(cyclesPerFrame * fps/60 + 1, sync, trace);
//if (trace) displayTraceBuffer();
updateInspectionFrame();
if (trace) {
if (scope_a > 0.01) {
video.getContext().fillStyle = "black";
video.getContext().fillRect(0, 0, videoWidth, videoHeight/3);
video.updateFrame(0, -framey+videoHeight/6, 0, 0, videoWidth, videoHeight);
video.getContext().fillRect(0, 0, videoWidth, videoHeight);
var vidyoffset = Math.round(scope_a*(-framey+videoHeight/6));
video.updateFrame(0, vidyoffset, 0, 0, videoWidth, videoHeight);
video.getContext().fillStyle = "white";
video.getContext().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;
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;
}
// 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;
updateInspectionPostFrame();
self.restartDebugState();
gen.__unreset();
@ -631,7 +637,7 @@ var VerilogPlatform = function(mainElement, options) {
this.reset = function() {
gen.__reset();
trace_index = scope_x_offset = 0;
trace_buffer.fill(0);
if (trace_buffer) trace_buffer.fill(0);
dirty = true;
if (video) video.setRotate(gen.rotate ? -90 : 0);
}

View File

@ -1252,6 +1252,14 @@ function _fasterFrameRate() {
setFrameRateUI(fps);
}
function _slowestFrameRate() {
setFrameRateUI(60/65536);
}
function _fastestFrameRate() {
setFrameRateUI(60);
}
function setupDebugControls(){
$("#dbg_reset").click(resetAndDebug);
$("#dbg_pause").click(pause);
@ -1305,6 +1313,8 @@ function setupDebugControls(){
$("#speed_bar").show();
$("#dbg_slower").click(_slowerFrameRate);
$("#dbg_faster").click(_fasterFrameRate);
$("#dbg_slowest").click(_slowestFrameRate);
$("#dbg_fastest").click(_fastestFrameRate);
}
updateDebugWindows();
}