smoother scope transition; slowest/fastest buttons; video width tweak
This commit is contained in:
parent
f9158b24eb
commit
b5c74234f3
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
10
src/ui.js
10
src/ui.js
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue