updated presets; verilog code dup detect; need to handle local/ include files somehow

This commit is contained in:
Steven Hugg 2018-07-31 14:41:27 -04:00
parent 94a932c08d
commit 2dbc60aa2e
16 changed files with 257 additions and 143 deletions

@ -1 +1 @@
Subproject commit ebf81921f80024e3e94c7a8e1235ab59464fa5ef
Subproject commit 4334727f0e07acd4541b0a7b8f81a5984cd4aafe

View File

@ -34,9 +34,10 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="https://twitter.com/8bitworkshop">@8bitworkshop</a>
<span class="navbar-brand"><a target="_new" href="https://twitter.com/8bitworkshop" class="twitter-follow-button" data-show-count="false">Follow @8bitworkshop</a></span>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<a class="navbar-brand" href="./blog/">Blog</a>
<a class="navbar-brand" href="https://www.amazon.com/default/e/B01N7J10NF/ref=dp_byline_cont_pop_book_1">Books</a>
<a class="navbar-brand" href="https://www.amazon.com/default/e/B01N7J10NF/ref=dp_byline_cont_pop_book_1" target="_blank" onclick="ga('send', 'event', 'books', 'click');">Books</a>
</div>
<div id="navbar" class="hidden-sm hidden-xs">
<form class="navbar-form navbar-right">
@ -80,11 +81,12 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
We've got debugging tools too; single step through your code and use our CPU Cycle Analyzer to
develop that perfect Stella kernel.
</p>
<img class="img-responsive" src="images/atarivcs.jpg">
</div>
<div class="col-md-4">
<h2>Arcade Games</h2>
<p>
You can develop your own games on classic arcade game hardware, using our in-browser C compiler.
You can develop your own games on classic arcade game hardware, using our in-browser C compiler targeting the Z80 CPU.
Platforms include
<a href="redir.html?platform=vicdual">VIC Dual</a>,
<a href="redir.html?platform=mw8080bw">Midway 8080</a>,
@ -92,8 +94,54 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<a href="redir.html?platform=vector-z80color">Atari Vector</a>,
and
<a href="redir.html?platform=williams-z80">Williams</a>.
<img class="img-responsive" src="images/arcadegames.jpg">
</p>
</div>
<div class="col-md-4">
<p>
We're always adding new things here, so subscribe to our mailing list for updates!
</p>
<style> .gumroad-follow-form-embed { zoom: 1; } .gumroad-follow-form-embed:before, .gumroad-follow-form-embed:after { display: table; line-height: 0; content: ""; } .gumroad-follow-form-embed:after { clear: both; } .gumroad-follow-form-embed * { margin: 0; border: 0; padding: 0; outline: 0; box-sizing: border-box !important; float: left !important; } .gumroad-follow-form-embed input { border-radius: 4px; border-top-right-radius: 0; border-bottom-right-radius: 0; font-family: -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 15px; line-height: 20px; background: #fff; border: 1px solid #ddd; border-right: 0; color: #aaa; padding: 10px; box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.02); background-position: top right; background-repeat: no-repeat; text-rendering: optimizeLegibility; font-smoothing: antialiased; -webkit-appearance: none; -moz-appearance: caret; width: 65% !important; height: 40px !important; } .gumroad-follow-form-embed button { border-radius: 4px; border-top-left-radius: 0; border-bottom-left-radius: 0; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12); -webkit-transition: all .05s ease-in-out; transition: all .05s ease-in-out; display: inline-block; padding: 11px 15px 12px; cursor: pointer; color: #fff; font-size: 15px; line-height: 100%; font-family: -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, Arial, sans-serif; background: #36a9ae; border: 1px solid #31989d; filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#5ccfd4, endColorstr=#329ca1, GradientType=0)"; background: -webkit-linear-gradient(#5ccfd4, #329ca1); background: linear-gradient(to bottom, #5ccfd4, #329ca1); height: 40px !important; width: 35% !important; } </style> <form action="https://gumroad.com/follow_from_embed_form" class="form gumroad-follow-form-embed" method="post"> <input name="seller_id" type="hidden" value="1507538753888"> <input name="email" placeholder="Your email address" type="email"> <button data-custom-highlight-color="" type="submit">Subscribe</button> </form>
<p></p>
<!-- Begin MailChimp Signup Form -->
<!--
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
<style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
/* Add your own MailChimp form style overrides in your site stylesheet or in this style block.
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
</style>
<div id="mc_embed_signup">
<form action="https://8bitworkshop.us19.list-manage.com/subscribe/post?u=819ee5274c8c9c1afe2005023&amp;id=75411a270f" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h2>We're always adding new things here, so subscribe to our mailing list for updates!</h2>
<div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
<div class="mc-field-group">
<label for="mce-EMAIL">Email Address <span class="asterisk">*</span>
</label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div class="mc-field-group">
<label for="mce-FNAME">First Name (optional)</label>
<input type="text" value="" name="FNAME" class="" id="mce-FNAME">
</div>
<div class="mc-field-group">
<label for="mce-LNAME">Last Name (optional)</label>
<input type="text" value="" name="LNAME" class="" id="mce-LNAME">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div>
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_819ee5274c8c9c1afe2005023_75411a270f" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div>
</form>
</div>
<script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='LNAME';ftypes[2]='text';fnames[3]='ADDRESS';ftypes[3]='address';fnames[4]='PHONE';ftypes[4]='phone';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
-->
<!--End mc_embed_signup-->
</div>
<!--
<div class="col-md-4">
<h2>Hardware Design</h2>
@ -113,12 +161,12 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<div class="container">
<div class="row">
<div class="col-md-3">
<a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae">
<a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae" onclick="ga('send', 'event', 'books', 'click', 'vcs');">
<img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=1541021304&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=pzp-20" ></a>
<img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1541021304" />
</div>
<div class="col-md-8">
<a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae">
<a target="_blank" href="https://www.amazon.com/gp/product/1541021304/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1541021304&linkCode=as2&tag=pzp-20&linkId=c149f6365c0a676065eb6d7c5f8dd6ae" onclick="ga('send', 'event', 'books', 'click', 'vcs');">
<h3>Making Games For The Atari 2600</h3>
</a>
<p>The Atari 2600 was released in 1977, and now there's finally a book about how to write games for it! You'll learn about the 6502 CPU, NTSC frames, scanlines, cycle counting, players, missiles, collisions, procedural generation, pseudo-3D, and more. While using the manual, take advantage of our Web-based IDE to write 6502 assembly code, and see your code run instantly in the browser. We'll cover the same programming tricks that master programmers used to make classic games. Create your own graphics and sound, and share your games with friends!</p>
@ -126,18 +174,31 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
</div>
<div class="row">
<div class="col-md-3">
<a target="_blank" 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=2f3c42111bffe3466830939fff4053fc">
<a target="_blank" 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=2f3c42111bffe3466830939fff4053fc" onclick="ga('send', 'event', 'books', 'click', 'arcade');">
<img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=US&ASIN=1545484759&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=pzp-20" ></a>
<img src="//ir-na.amazon-adsystem.com/e/ir?t=pzp-20&l=am2&o=1&a=1545484759" />
</div>
<div class="col-md-8">
<a target="_blank" 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=2f3c42111bffe3466830939fff4053fc">
<a target="_blank" 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=2f3c42111bffe3466830939fff4053fc" onclick="ga('send', 'event', 'books', 'click', 'arcade');">
<h3>Making 8-Bit Arcade Games in C</h3>
</a>
<p>With this book, you'll learn all about the hardware of Golden Age 8-bit arcade games produced in the late 1970s to early 1980s. We'll learn how to use the C programming language to write code for the Z80 CPU. The following arcade platforms are covered: * Midway 8080 (Space Invaders) * VIC Dual (Carnival) * Galaxian/Scramble (Namco) * Atari Color Vector * Williams (Defender, Robotron) We'll describe how to create video and sound for each platform. Use the online 8bitworkshop IDE to compile your C programs and play them right in the browser!</p>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6">
</div>
<div class="col-md-3">
</div>
</div>
</div>
<hr>

View File

@ -48,6 +48,9 @@ TODO:
- compile stuck when errors unchanged
- sound mute?
- $error updates source editor
- Revert loads cached files?
- Verilog compile spins forever?
- how to revert included files?
WEB WORKER FORMAT

View File

@ -10,16 +10,18 @@ module ball_absolute_top(clk, reset, hsync, vsync, rgb);
wire [8:0] hpos;
wire [8:0] vpos;
reg [8:0] ball_hpos;
reg [8:0] ball_vpos;
reg [8:0] ball_hpos; // ball current X position
reg [8:0] ball_vpos; // ball current Y position
reg [8:0] ball_horiz_initial = 128;
reg [8:0] ball_horiz_move = -2;
reg [8:0] ball_vert_initial = 128;
reg [8:0] ball_vert_move = 2;
reg [8:0] ball_horiz_move = -2; // ball current X velocity
reg [8:0] ball_vert_move = 2; // ball current Y velocity
localparam BALL_SIZE = 4;
localparam ball_horiz_initial = 128; // ball initial X position
localparam ball_vert_initial = 128; // ball initial Y position
localparam BALL_SIZE = 4; // ball size (in pixels)
// video sync generator
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
@ -34,9 +36,11 @@ module ball_absolute_top(clk, reset, hsync, vsync, rgb);
always @(posedge vsync or posedge reset)
begin
if (reset) begin
// reset ball position to center
ball_hpos <= ball_horiz_initial;
ball_vpos <= ball_vert_initial;
end else begin
// add velocity vector to ball position
ball_hpos <= ball_hpos + ball_horiz_move;
ball_vpos <= ball_vpos + ball_vert_move;
end
@ -54,19 +58,22 @@ module ball_absolute_top(clk, reset, hsync, vsync, rgb);
ball_horiz_move <= -ball_horiz_move;
end
// offset of ball position from video beam
wire [8:0] ball_hdiff = hpos - ball_hpos;
wire [8:0] ball_vdiff = vpos - ball_vpos;
// ball graphics output
wire ball_hgfx = ball_hdiff < BALL_SIZE;
wire ball_vgfx = ball_vdiff < BALL_SIZE;
wire ball_gfx = ball_hgfx && ball_vgfx;
// collide with vertical and horizontal boundaries
// these are set when the ball touches a border
wire ball_vert_collide = ball_vpos >= 240 - BALL_SIZE;
wire ball_horiz_collide = ball_hpos >= 256 - BALL_SIZE;
wire grid_gfx = (((hpos&7)==0) && ((vpos&7)==0));
// combine signals to RGB output
wire grid_gfx = (((hpos&7)==0) && ((vpos&7)==0));
wire r = display_on && (ball_hgfx | ball_gfx);
wire g = display_on && (grid_gfx | ball_gfx);
wire b = display_on && (ball_vgfx | ball_gfx);

View File

@ -13,34 +13,34 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
wire [8:0] hpos;
wire [8:0] vpos;
reg [8:0] paddle_pos;
reg [8:0] paddle_pos; // paddle X position
reg [8:0] ball_x;
reg [8:0] ball_y;
reg ball_dir_x;
reg ball_speed_x;
reg ball_dir_y;
reg [8:0] ball_x; // ball X position
reg [8:0] ball_y; // ball Y position
reg ball_dir_x; // ball X direction (0=left, 1=right)
reg ball_speed_x; // ball speed (0=1 pixel/frame, 1=2 pixels/frame)
reg ball_dir_y; // ball Y direction (0=up, 1=down)
reg brick_array [0:BRICKS_H*BRICKS_V-1]; // 16*8 = 128 bits
wire [3:0] score0;
wire [3:0] score1;
wire [3:0] lives;
reg incscore;
wire [3:0] score0; // score right digit
wire [3:0] score1; // score left digit
wire [3:0] lives; // # lives remaining
reg incscore; // incscore signal
reg declives = 0; // TODO
localparam BRICKS_H = 16;
localparam BRICKS_V = 8;
localparam BRICKS_H = 16; // # of bricks across
localparam BRICKS_V = 8; // # of bricks down
localparam BALL_DIR_LEFT = 0;
localparam BALL_DIR_RIGHT = 1;
localparam BALL_DIR_DOWN = 1;
localparam BALL_DIR_UP = 0;
localparam PADDLE_WIDTH = 31;
localparam BALL_SIZE = 6;
localparam PADDLE_WIDTH = 31; // horizontal paddle size
localparam BALL_SIZE = 6; // square ball size
// video sync generator
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
@ -52,35 +52,37 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
);
// scoreboard
wire score_gfx; // output from score generator
player_stats stats(.reset(reset),
.score0(score0), .score1(score1), .incscore(incscore),
.lives(lives), .declives(declives));
wire score_gfx;
scoreboard_generator score_gen(
.score0(score0), .score1(score1), .lives(lives),
.vpos(vpos), .hpos(hpos),
.board_gfx(score_gfx));
wire [5:0] hcell = hpos[8:3];
wire [5:0] vcell = vpos[8:3];
wire lr_border = hcell==0 || hcell==31;
wire [5:0] hcell = hpos[8:3]; // horizontal brick index
wire [5:0] vcell = vpos[8:3]; // vertical brick index
wire lr_border = hcell==0 || hcell==31; // along horizontal border?
// TODO: unsigned compare doesn't work in JS
wire [8:0] paddle_rel_x = ((hpos-paddle_pos) & 9'h1ff);
// player paddle graphics signal
wire paddle_gfx = (vcell == 28) && (paddle_rel_x < PADDLE_WIDTH);
wire [8:0] ball_rel_x = (hpos-ball_x);
wire [8:0] ball_rel_y = (vpos-ball_y);
// difference between ball position and video beam
wire [8:0] ball_rel_x = (hpos - ball_x);
wire [8:0] ball_rel_y = (vpos - ball_y);
// ball graphics signal
wire ball_gfx = ball_rel_x < BALL_SIZE
&& ball_rel_y < BALL_SIZE;
reg main_gfx;
reg brick_present;
reg [6:0] brick_index;
reg main_gfx; // main graphics signal (bricks and borders)
reg brick_present; // 1 when we are drawing a brick
reg [6:0] brick_index;// index into array of current brick
// brick graphics signal
wire brick_gfx = lr_border || (brick_present && vpos[2:0] != 0 && hpos[3:1] != 4);
// scan bricks: compute brick_index and brick_present flag
@ -92,14 +94,11 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
if (hpos[3:0] == 8) begin
// compute brick index
brick_index <= {vpos[5:3], hpos[7:4]};
//main_gfx <= 0; // 2 pixel horiz spacing between bricks
end
// every 17th pixel
else if (hpos[3:0] == 9) begin
// load brick bit from array
brick_present <= brick_array[brick_index];
end else begin
//main_gfx <= brick_present && vpos[2:0] != 0; // 1 pixel vert. spacing
end
end else begin
brick_present <= 0;
@ -111,6 +110,7 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
if (!hpaddle)
paddle_pos <= vpos;
// 1 when ball signal intersects main (brick + border) signal
wire ball_pixel_collide = main_gfx & ball_gfx;
/* verilator lint_off MULTIDRIVEN */
@ -141,6 +141,7 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
incscore <= 0; // reset incscore
end
// computes position of ball in relation to center of paddle
wire signed [8:0] ball_paddle_dx = ball_x - paddle_pos + 8;
// ball bounce: determine new velocity/direction
@ -189,9 +190,11 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
always @(negedge vsync or posedge reset)
begin
if (reset) begin
// reset ball position to top center
ball_x <= 128;
ball_y <= 180;
end else begin
// move ball horizontal and vertical position
if (ball_dir_x == BALL_DIR_RIGHT)
ball_x <= ball_x + (ball_speed_x?1:0) + 1;
else
@ -223,8 +226,8 @@ module ball_paddle_top(clk, reset, hpaddle, hsync, vsync, rgb);
endcase
end
// combine signals to RGB output
wire grid_gfx = (((hpos&7)==0) || ((vpos&7)==0));
wire r = display_on && (ball_gfx | paddle_gfx);
wire g = display_on && (main_gfx | ball_gfx);
wire b = display_on && (grid_gfx | ball_gfx | brick_present);

View File

@ -9,7 +9,6 @@ module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
wire display_on;
wire [8:0] hpos;
wire [8:0] vpos;
reg ball_reset;
// 9-bit ball timers
reg [8:0] ball_htimer;
@ -26,7 +25,11 @@ module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
// 5-bit constants to load into counters
localparam ball_horiz_prefix = 5'b01100; // 192
localparam ball_vert_prefix = 5'b01111; // 240
// reset ball; will be unset when video beam reaches center position
reg ball_reset;
// video sync generator
hvsync_generator hvsync_gen(
.clk(clk),
.reset(reset),
@ -64,10 +67,10 @@ module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
if (reset)
ball_reset <= 1;
else if (hpos == 128 && vpos == 128)
ball_reset <= 0;
ball_reset <= 0; // un-reset when beam reaches center position
end
// collide with vertical and horizontal boundaries
// collide with vertical and horizontal boundaries?
wire ball_vert_collide = ball_vgfx && vpos >= 240;
wire ball_horiz_collide = ball_hgfx && hpos >= 256 && vpos == 255;
@ -75,7 +78,7 @@ module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
always @(posedge ball_vert_collide or posedge reset)
begin
if (reset)
ball_vert_move <= 4'd9;
ball_vert_move <= 4'd9; // initial vertical velocity
else
ball_vert_move <= (4'd9 ^ 4'd11) ^ ball_vert_move; // change dir.
end
@ -84,13 +87,13 @@ module ball_slip_counter_top(clk, reset, hsync, vsync, rgb);
always @(posedge ball_horiz_collide or posedge reset)
begin
if (reset)
ball_horiz_move <= 4'd10;
ball_horiz_move <= 4'd10; // initial horizontal velocity
else
ball_horiz_move <= (4'd10 ^ 4'd12) ^ ball_horiz_move; // change dir.
end
// compute ball display
wire ball_hgfx = ball_htimer >= 508;
wire ball_hgfx = ball_htimer >= 508; // 512-508 = 4 pixel ball
wire ball_vgfx = ball_vtimer >= 508;
wire ball_gfx = ball_hgfx && ball_vgfx;

View File

@ -3,12 +3,14 @@
`include "hvsync_generator.v"
// module for 10-digit bitmap ROM
module digits10_case(digit, yofs, bits);
input [3:0] digit;
input [2:0] yofs;
output reg [4:0] bits;
input [3:0] digit; // digit 0-9
input [2:0] yofs; // vertical offset (0-4)
output reg [4:0] bits; // output (5 bits)
// combine {digit,yofs} into single ROM address
wire [6:0] caseexpr = {digit,yofs};
always @(*)
@ -79,13 +81,13 @@ endmodule
module digits10_array(digit, yofs, bits);
input [3:0] digit;
input [2:0] yofs;
output [4:0] bits;
input [3:0] digit; // digit 0-9
input [2:0] yofs; // vertical offset (0-4)
output [4:0] bits; // output (5 bits)
reg [4:0] bitarray[0:15][0:4];
reg [4:0] bitarray[0:15][0:4]; // ROM array
assign bits = bitarray[digit][yofs];
assign bits = bitarray[digit][yofs]; // assign module output
integer i,j;
@ -157,6 +159,7 @@ module digits10_array(digit, yofs, bits);
end
endmodule
// test module
module test_numbers_top(clk, reset, hsync, vsync, rgb);
input clk, reset;

View File

@ -10,18 +10,18 @@ module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
output reg [8:0] hpos;
output reg [8:0] vpos;
// constant declarations for TV-simulator sync parameters
// horizontal
// declarations for TV-simulator sync parameters
// horizontal constants
parameter H_DISPLAY = 256; // horizontal display width
parameter H_BACK = 23; // horizontal left border (back porch)
parameter H_FRONT = 7; // horizontal right border (front porch)
parameter H_SYNC = 23; // horizontal sync width
// vertical
// vertical constants
parameter V_DISPLAY = 240; // vertical display height
parameter V_TOP = 5; // vertical top border
parameter V_BOTTOM = 14; // vertical bottom border
parameter V_SYNC = 3; // vertical sync # lines
// derived
// derived constants
parameter H_SYNC_START = H_DISPLAY + H_FRONT;
parameter H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1;
parameter H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;
@ -29,8 +29,8 @@ module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
parameter V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
parameter V_MAX = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1;
wire hmaxxed = (hpos == H_MAX) || reset;
wire vmaxxed = (vpos == V_MAX) || reset;
wire hmaxxed = (hpos == H_MAX) || reset; // set when hpos is maximum
wire vmaxxed = (vpos == V_MAX) || reset; // set when vpos is maximum
// horizontal position counter
always @(posedge clk)
@ -53,6 +53,7 @@ module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
vpos <= vpos + 1;
end
// display_on is set when beam is in "safe" visible frame
assign display_on = (hpos<H_DISPLAY) && (vpos<V_DISPLAY);
endmodule

View File

@ -1,15 +1,15 @@
`ifndef LFSR_V
`define LFSR_V
module LFSR(clk,reset,enable,lfsr);
module LFSR(clk, reset, enable, lfsr);
parameter TAPS = 8'b11101;
parameter INVERT = 0;
localparam NBITS = $size(TAPS);
parameter TAPS = 8'b11101; // bitmask for taps
parameter INVERT = 0; // invert feedback bit?
localparam NBITS = $size(TAPS); // bit width (derived from TAPS)
input clk, reset;
input enable;
output reg [NBITS-1:0] lfsr;
input enable; // only perform shift when enable=1
output reg [NBITS-1:0] lfsr; // shift register
wire feedback = lfsr[NBITS-1] ^ INVERT;

View File

@ -10,9 +10,11 @@ module paddles_top(clk, reset, hsync, vsync, hpaddle, vpaddle, rgb);
wire [8:0] hpos;
wire [8:0] vpos;
// player position (only set at VSYNC)
reg [7:0] player_x;
reg [7:0] player_y;
// paddle position (set continuously during frame)
reg [7:0] paddle_x;
reg [7:0] paddle_y;

View File

@ -12,17 +12,22 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
wire [8:0] hpos;
wire [8:0] vpos;
// player car position (set at VSYNC)
reg [7:0] player_x;
reg [7:0] player_y;
// paddle position (set continuously during frame)
reg [7:0] paddle_x;
reg [7:0] paddle_y;
// enemy car position
reg [7:0] enemy_x = 128;
reg [7:0] enemy_y = 128;
reg enemy_dir = 0;
// enemy car direction, 1=right, 0=left
reg enemy_dir = 0;
reg [15:0] track_pos = 0;
reg [7:0] speed = 31;
reg [15:0] track_pos = 0; // player position along track (16 bits)
reg [7:0] speed = 31; // player velocity along track (8 bits)
// video sync generator
hvsync_generator hvsync_gen(
.clk(clk),
.reset(0),
@ -33,6 +38,14 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.vpos(vpos)
);
// set paddle registers
always @(posedge hsync)
begin
if (!hpaddle) paddle_x <= vpos[7:0];
if (!vpaddle) paddle_y <= vpos[7:0];
end
// wire up car sprite ROM
wire [3:0] car_sprite_yofs;
wire [7:0] car_sprite_bits;
@ -40,16 +53,19 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.yofs(car_sprite_yofs),
.bits(car_sprite_bits));
// signals for player sprite generator
wire player_vstart = {1'd0,player_y} == vpos;
wire player_hstart = {1'd0,player_x} == hpos;
wire player_gfx;
wire player_is_drawing;
// signals for enemy sprite generator
wire enemy_vstart = {1'd0,enemy_y} == vpos;
wire enemy_hstart = {1'd0,enemy_x} == hpos;
wire enemy_gfx;
wire enemy_is_drawing;
// player sprite generator
sprite_renderer player_renderer(
.clk(clk),
.vstart(player_vstart),
@ -60,6 +76,7 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.gfx(player_gfx),
.in_progress(player_is_drawing));
// enemy sprite generator
sprite_renderer enemy_renderer(
.clk(clk),
.vstart(enemy_vstart),
@ -69,17 +86,14 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
.rom_bits(car_sprite_bits),
.gfx(enemy_gfx),
.in_progress(player_is_drawing));
always @(posedge hsync)
begin
if (!hpaddle) paddle_x <= vpos[7:0];
if (!vpaddle) paddle_y <= vpos[7:0];
end
// signals for enemy bouncing off left/right borders
wire enemy_hit_left = (enemy_x == 64);
wire enemy_hit_right = (enemy_x == 192);
wire enemy_hit_edge = enemy_hit_left || enemy_hit_right;
// update player, enemy, track counters
// runs once per frame
always @(posedge vsync)
begin
player_x <= paddle_x;
@ -101,6 +115,7 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
speed <= speed - 1;
end
// set to 1 when player collides with enemy or track
reg frame_collision;
always @(posedge clk)
@ -109,10 +124,12 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
else if (vsync)
frame_collision <= 0;
// track graphics signals
wire track_offside = (hpos[7:5]==0) || (hpos[7:5]==7);
wire track_shoulder = (hpos[7:3]==3) || (hpos[7:3]==28);
wire track_gfx = (vpos[5:1]!=track_pos[5:1]) && track_offside;
// combine signals for RGB output
wire r = display_on && (player_gfx || enemy_gfx || track_shoulder);
wire g = display_on && (player_gfx || track_gfx);
wire b = display_on && (enemy_gfx || track_shoulder);

View File

@ -1,3 +1,4 @@
`include "hvsync_generator.v"
`include "ram.v"
@ -53,14 +54,12 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
output [15:0] rom_addr; // sprite ROM address
input [15:0] rom_data; // sprite ROM data
// copy of sprite data from RAM
// copy of sprite data from RAM (N entries)
reg [7:0] sprite_xpos[0:N-1];
reg [7:0] sprite_ypos[0:N-1];
reg [7:0] sprite_attr[0:N-1];
// mapping of N sprites to M line slots
reg [NB-1:0] sprite_to_line[0:M-1];
// M sprite slots
reg [7:0] line_xpos[0:M-1]; // X pos for M slots
reg [7:0] line_yofs[0:M-1]; // Y pos for M slots
reg [7:0] line_attr[0:M-1]; // attr for M slots
@ -74,6 +73,7 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
reg [8:0] write_ofs;
reg [15:0] out_bitmap;
reg [7:0] out_attr;
reg romload;
// which sprite are we currently reading?
wire [NB-1:0] load_index = hpos[NB:1];
@ -122,43 +122,49 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
endcase
end
end else if (hpos < N*2) begin
// setup vars for next phase
k <= 0;
romload <= 0;
// select the sprites that will appear in this scanline
case (hpos[0])
// compute Y offset of sprite relative to scanline
0: z <= 8'(vpos - sprite_ypos[i]);
// sprite is active if Y offset is 0..15
1: begin
// sprite is active if Y offset is 0..15
if (z < 16) begin
line_xpos[j] <= sprite_xpos[i]; // save X pos
line_yofs[j] <= z; // save Y offset
sprite_to_line[j] <= i; // save main array index
line_attr[j] <= sprite_attr[i]; // save attr
line_active[j] <= 1; // mark sprite active
j <= j + 1; // inc counter
end
i <= i + 1; // inc main array counter
end
endcase
end else if (hpos < N*2+M*24) begin
end else if (hpos < N*2+M*18) begin
// setup vars for next phase
j <= 0;
// divide hpos by 24 (8 setup + 16 render)
if ((hpos[3:0] < 8) ^^ hpos[4]) begin
// render sprites into write buffer
case (hpos[3:0])
// grab index into main sprite array
0: i <= sprite_to_line[k];
// load scanline buffer offset to write
1: write_ofs <= {~vpos[0], sprite_xpos[i]};
// set ROM address and fetch bitmap
2: rom_addr <= {4'b0, sprite_attr[i][7:4], line_yofs[k]};
// fetch 0 if sprite is inactive
3: out_bitmap <= line_active[k] ? rom_data : 0;
// load attribute for sprite
4: out_attr <= sprite_attr[i];
// disable sprite for next scanline
6: line_active[k] <= 0;
// go to next sprite in 2ndary buffer
7: k <= k + 1;
// if sprite shift register is empty, load new sprite
if (out_bitmap == 0) begin
case (romload)
0: begin
// set ROM address and fetch bitmap
rom_addr <= {4'b0, line_attr[k][7:4], line_yofs[k]};
end
1: begin
// load scanline buffer offset to write
write_ofs <= {~vpos[0], line_xpos[k]};
// fetch 0 if sprite is inactive
out_bitmap <= line_active[k] ? rom_data : 0;
// load attribute for sprite
out_attr <= line_attr[k];
// disable sprite for next scanline
line_active[k] <= 0;
// go to next sprite in 2ndary buffer
k <= k + 1;
end
endcase
romload <= !romload;
end else begin
// write color to scanline buffer if low bit == 1
if (out_bitmap[0])

View File

@ -1,4 +1,4 @@

.include "hvsync_generator.v"
.include "font_cp437_8x8.v"
.include "ram.v"
@ -140,11 +140,15 @@ MoveSprites:
mov dx,@SpriteTable
mov cx,#5
MoveSLoop:
mov ax,[dx] ; x/y pos
mov bx,[dx+1] ; attributes/dir
and bx,#3 ; bx = direction (0-3)
mov ax,[dx] ; x/y pos
mov bx,[dx+1] ; direction
rol bx
rol bx
rol bx
rol bx
and bx,#3 ; bx = direction (0-3)
add bx,@DirectionXY
add ax,[bx] ; lookup & add to x/y
add ax,[bx] ; lookup & add to x/y
; make sure we don't collide with wall
mov ex,ax
add ex,#$08
@ -170,19 +174,18 @@ MoveSLoop:
bz DoNotUpdatePos
; update x/y position
mov [dx],ax ; store x/y pos
inc dx
inc dx
NextSLoop:
add dx,#2
dec cx
bnz MoveSLoop
rts
; Choose a new direction
DoNotUpdatePos:
mov bx,[dx+1] ; attributes/dir
inc bx
inc dx
add dx,#1
mov bx,[dx] ; direction
add bx,@$1000
mov [dx],bx
inc dx
sub dx,#1
jmp NextSLoop
HelloWorld:
@ -190,6 +193,7 @@ HelloWorld:
.data 0
SpriteInitData:
.data $b373 $1233
.data $8363 $3456
.data $8373 $4567
.data $8383 $5678

View File

@ -592,27 +592,28 @@ var VerilogPlatform = function(mainElement, options) {
this.loadROM = function(title, output) {
var mod;
if (output.code) {
try {
mod = new Function('base', output.code);
} catch (e) {
this.printErrorCodeContext(e, output.code);
throw e;
// is code identical?
if (current_output && current_output.code == output.code) {
} else {
try {
mod = new Function('base', output.code);
} catch (e) {
this.printErrorCodeContext(e, output.code);
throw e;
}
// compile Verilog code
var base = new VerilatorBase();
gen = new mod(base);
//$.extend(gen, base);
gen.__proto__ = base;
current_output = output;
module_name = output.name ? output.name.substr(1) : "top";
trace_ports = current_output.ports;
trace_signals = current_output.ports.concat(current_output.signals);
trace_index = 0;
// power on module
this.poweron();
}
// compile Verilog code
var base = new VerilatorBase();
gen = new mod(base);
//$.extend(gen, base);
gen.__proto__ = base;
current_output = output;
module_name = output.name ? output.name.substr(1) : "top";
trace_ports = current_output.ports;
trace_signals = current_output.ports.concat(current_output.signals);
trace_index = 0;
// power on module
this.poweron();
} else {
// TODO: :^P
output = {program_rom_variable: title, program_rom: output};
}
// replace program ROM, if using the assembler
if (output.program_rom && output.program_rom_variable) {
@ -624,6 +625,7 @@ var VerilogPlatform = function(mainElement, options) {
} else {
alert("No program_rom variable found (" + output.program_rom_variable + ")");
}
this.reset();
}
// restart audio
restartAudio();

View File

@ -60,7 +60,7 @@ export class CodeProject {
var m;
while (m = re.exec(text)) {
files.push(m[2]);
files.push('local/'+m[2]); // TODO?
//files.push('local/'+m[2]); // TODO: shows up 2x in interface
}
} else {
var re = /^\s*([;#]|[/][/][#])link\s+"(.+?)"/gm;

View File

@ -1120,6 +1120,8 @@ function compileJSASM(asmcode, platform, options, is_inline) {
result.output.program_rom = asmout;
// cpu_platform__DOT__program_rom
result.output.program_rom_variable = jsasm_module_top + "__DOT__program_rom";
result.listings = {};
result.listings[options.path] = {lines:result.lines};
}
return result;
}