mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-01-02 23:30:21 +00:00
updated presets, changed array syntax, ice40 fpga examples
This commit is contained in:
parent
c687006684
commit
706a24c96a
6
fpga/examples/.gitignore
vendored
Normal file
6
fpga/examples/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.asc
|
||||
*.bin
|
||||
*.blif
|
||||
*.log
|
||||
*.out
|
||||
*.vcd
|
22
fpga/examples/Makefile
Normal file
22
fpga/examples/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
%.asc: %.v icestick.pcf
|
||||
yosys -p "synth_ice40 -blif $*.blif" $*.v | tee $*.log
|
||||
arachne-pnr -d 1k -p icestick.pcf $*.blif -o $*.asc
|
||||
|
||||
%.bin: %.asc
|
||||
icetime -c 12 -d hx1k $*.asc
|
||||
icepack $*.asc $*.bin
|
||||
|
||||
%.prog: %.bin
|
||||
iceprog $<
|
||||
|
||||
%.hex: %.asm
|
||||
node ../../src/tools/jsasm.js < $< > $@
|
||||
|
||||
%.vlog: %.asc
|
||||
icebox_vlog $*.asc > $*.vlog
|
||||
|
||||
%.vcd: %.vlog test.vlog
|
||||
iverilog -o $*.out -v /usr/share/yosys/ice40/cells_sim.v $< test.vlog
|
||||
vvp $*.out
|
||||
|
247
fpga/examples/ball_paddle.v
Normal file
247
fpga/examples/ball_paddle.v
Normal file
@ -0,0 +1,247 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "digits10.v"
|
||||
`include "scoreboard.v"
|
||||
|
||||
/*
|
||||
A brick-smashing ball-and-paddle game.
|
||||
*/
|
||||
|
||||
module ball_paddle_top(clk, reset, hpaddle, out);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input hpaddle;
|
||||
wire hsync, vsync;
|
||||
output [1:0] out;
|
||||
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
reg [8:0] paddle_pos; // paddle X position
|
||||
|
||||
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; // 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; // # 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; // horizontal paddle size
|
||||
localparam BALL_SIZE = 6; // square ball size
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// scoreboard
|
||||
wire score_gfx; // output from score generator
|
||||
player_stats stats(.reset(reset),
|
||||
.score0(score0), .score1(score1), .incscore(incscore),
|
||||
.lives(lives), .declives(declives));
|
||||
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]; // 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);
|
||||
|
||||
// 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; // 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
|
||||
always @(posedge clk2)
|
||||
// see if we are scanning brick area
|
||||
if (vpos[8:6] == 1 && !lr_border)
|
||||
begin
|
||||
// every 16th pixel, starting at 8
|
||||
if (hpos[3:0] == 8) begin
|
||||
// compute brick index
|
||||
brick_index <= {vpos[5:3], hpos[7:4]};
|
||||
end
|
||||
// every 17th pixel
|
||||
else if (hpos[3:0] == 9) begin
|
||||
// load brick bit from array
|
||||
brick_present <= brick_array[brick_index];
|
||||
end
|
||||
end else begin
|
||||
brick_present <= 0;
|
||||
end
|
||||
|
||||
// only works when paddle at bottom of screen!
|
||||
// (we don't want to mess w/ paddle position during visible portion)
|
||||
always @(posedge hsync)
|
||||
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 */
|
||||
reg ball_collide_paddle = 0;
|
||||
reg [3:0] ball_collide_bits = 0;
|
||||
/* verilator lint_on MULTIDRIVEN */
|
||||
|
||||
// compute ball collisions with paddle and playfield
|
||||
always @(posedge clk2)
|
||||
if (ball_pixel_collide) begin
|
||||
// did we collide w/ paddle?
|
||||
if (paddle_gfx) begin
|
||||
ball_collide_paddle <= 1;
|
||||
end
|
||||
// ball has 4 collision quadrants
|
||||
if (!ball_rel_x[2] & !ball_rel_y[2]) ball_collide_bits[0] <= 1;
|
||||
if (ball_rel_x[2] & !ball_rel_y[2]) ball_collide_bits[1] <= 1;
|
||||
if (!ball_rel_x[2] & ball_rel_y[2]) ball_collide_bits[2] <= 1;
|
||||
if (ball_rel_x[2] & ball_rel_y[2]) ball_collide_bits[3] <= 1;
|
||||
end
|
||||
|
||||
// compute ball collisions with brick and increment score
|
||||
always @(posedge clk2)
|
||||
if (ball_pixel_collide && brick_present) begin
|
||||
brick_array[brick_index] <= 0;
|
||||
incscore <= 1; // increment score
|
||||
end else begin
|
||||
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
|
||||
always @(posedge vsync or posedge reset)
|
||||
begin
|
||||
if (reset) begin
|
||||
ball_dir_y <= BALL_DIR_DOWN;
|
||||
end else
|
||||
// ball collided with paddle?
|
||||
if (ball_collide_paddle) begin
|
||||
// bounces upward off of paddle
|
||||
ball_dir_y <= BALL_DIR_UP;
|
||||
// which side of paddle, left/right?
|
||||
ball_dir_x <= (ball_paddle_dx < 20) ? BALL_DIR_LEFT : BALL_DIR_RIGHT;
|
||||
// hitting with edge of paddle makes it fast
|
||||
ball_speed_x <= ball_collide_bits[3:0] != 4'b1100;
|
||||
end else begin
|
||||
// collided with playfield
|
||||
// TODO: can still slip through corners
|
||||
// compute left/right bounce
|
||||
casez (ball_collide_bits[3:0])
|
||||
4'b01?1: ball_dir_x <= BALL_DIR_RIGHT; // left edge/corner
|
||||
4'b1101: ball_dir_x <= BALL_DIR_RIGHT; // left corner
|
||||
4'b101?: ball_dir_x <= BALL_DIR_LEFT; // right edge/corner
|
||||
4'b1110: ball_dir_x <= BALL_DIR_LEFT; // right corner
|
||||
default: ;
|
||||
endcase
|
||||
// compute top/bottom bounce
|
||||
casez (ball_collide_bits[3:0])
|
||||
4'b1011: ball_dir_y <= BALL_DIR_DOWN;
|
||||
4'b0111: ball_dir_y <= BALL_DIR_DOWN;
|
||||
4'b001?: ball_dir_y <= BALL_DIR_DOWN;
|
||||
4'b0001: ball_dir_y <= BALL_DIR_DOWN;
|
||||
4'b0100: ball_dir_y <= BALL_DIR_UP;
|
||||
4'b1?00: ball_dir_y <= BALL_DIR_UP;
|
||||
4'b1101: ball_dir_y <= BALL_DIR_UP;
|
||||
4'b1110: ball_dir_y <= BALL_DIR_UP;
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
ball_collide_bits <= 0; // clear all collide bits for frame
|
||||
ball_collide_paddle <= 0;
|
||||
end
|
||||
|
||||
// ball motion: update ball position
|
||||
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
|
||||
ball_x <= ball_x - (ball_speed_x?1:0) - 1;
|
||||
ball_y <= ball_y + (ball_dir_y==BALL_DIR_DOWN?1:-1);
|
||||
end
|
||||
end
|
||||
|
||||
// compute main_gfx
|
||||
always @(*)
|
||||
begin
|
||||
case (vpos[8:3])
|
||||
0: main_gfx = score_gfx; // scoreboard
|
||||
1: main_gfx = score_gfx;
|
||||
2: main_gfx = score_gfx;
|
||||
3: main_gfx = 0;
|
||||
4: main_gfx = 1; // top border
|
||||
8: main_gfx = brick_gfx; // 1st brick row
|
||||
9: main_gfx = brick_gfx; // ...
|
||||
10: main_gfx = brick_gfx;
|
||||
11: main_gfx = brick_gfx;
|
||||
12: main_gfx = brick_gfx;
|
||||
13: main_gfx = brick_gfx;
|
||||
14: main_gfx = brick_gfx;
|
||||
15: main_gfx = brick_gfx; // 8th brick row
|
||||
28: main_gfx = paddle_gfx | lr_border; // paddle
|
||||
29: main_gfx = hpos[0] ^ vpos[0]; // bottom border
|
||||
default: main_gfx = lr_border; // left/right borders
|
||||
endcase
|
||||
end
|
||||
|
||||
// combine signals to RGB output
|
||||
wire r = display_on && (ball_gfx | paddle_gfx);
|
||||
wire g = display_on && (main_gfx | ball_gfx);
|
||||
wire b = display_on && (ball_gfx | brick_present);
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
|
||||
|
||||
endmodule
|
||||
|
@ -1,25 +1,12 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "digits10.v"
|
||||
|
||||
module RAM_1KB_tri(clk, addr, data, we);
|
||||
input clk;
|
||||
input [9:0] addr;
|
||||
inout [7:0] data;
|
||||
input we;
|
||||
|
||||
reg [7:0] mem [1023:0];
|
||||
assign data = !we ? mem[addr] : 8'bz;
|
||||
always @(posedge clk) begin
|
||||
if (we)
|
||||
mem[addr] <= data;
|
||||
end
|
||||
endmodule
|
||||
`include "ram.v"
|
||||
`include "lfsr.v"
|
||||
|
||||
module test_ram2_top(
|
||||
input clk, reset,
|
||||
output hsync, vsync,
|
||||
output [2:0] rgb
|
||||
output [1:0] out
|
||||
);
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
@ -27,19 +14,34 @@ module test_ram2_top(
|
||||
|
||||
reg ram_writeenable = 0;
|
||||
wire [9:0] ram_addr = {row,col};
|
||||
wire [7:0] ram_data = ram_writeenable ? ram_write : 8'bz;
|
||||
wire [7:0] ram_read = ram_writeenable ? 8'bz : ram_data;
|
||||
reg [7:0] ram_write;
|
||||
reg [7:0] ram_read;
|
||||
reg [7:0] ram_write;
|
||||
reg [7:0] rand;
|
||||
|
||||
RAM_1KB_tri ram(
|
||||
.clk(clk),
|
||||
.data(ram_data),
|
||||
reg clk2;
|
||||
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
RAM_sync ram(
|
||||
.clk(clk2),
|
||||
.din(ram_write),
|
||||
.dout(ram_read),
|
||||
.addr(ram_addr),
|
||||
.we(ram_writeenable)
|
||||
);
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
LFSR lfsr(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.enable(!reset),
|
||||
.lfsr(rand)
|
||||
);
|
||||
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
@ -51,22 +53,22 @@ module test_ram2_top(
|
||||
wire [4:0] row = vpos[7:3];
|
||||
wire [4:0] col = hpos[7:3];
|
||||
wire [3:0] digit = ram_read[3:0];
|
||||
wire [2:0] xofs = hpos[2:0];
|
||||
wire [2:0] yofs = vpos[2:0];
|
||||
wire [4:0] bits;
|
||||
wire [7:0] bits; // TODO?
|
||||
|
||||
digits10_array numbers(
|
||||
digits10_case numbers(
|
||||
.digit(digit),
|
||||
.yofs(yofs),
|
||||
.bits(bits)
|
||||
);
|
||||
|
||||
wire r = display_on && 0;
|
||||
wire g = display_on && bits[hpos[2:0] ^ 3'b111];
|
||||
wire b = display_on && 0;
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
always @(posedge clk)
|
||||
if (display_on && vpos[2:0] == 7)
|
||||
wire g = display_on && bits[xofs ^ 3'b111];
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+g+g);
|
||||
|
||||
always @(posedge clk2)
|
||||
if (display_on && vpos[2:0] == 7 && rand[0])
|
||||
case (hpos[2:0])
|
||||
6: begin
|
||||
ram_write <= ram_read + 1;
|
2048
fpga/examples/cp437.hex
Normal file
2048
fpga/examples/cp437.hex
Normal file
File diff suppressed because it is too large
Load Diff
232
fpga/examples/cpu8.v
Normal file
232
fpga/examples/cpu8.v
Normal file
@ -0,0 +1,232 @@
|
||||
|
||||
`ifndef ALU_H
|
||||
`define ALU_H
|
||||
|
||||
// ALU operations
|
||||
`define OP_ZERO 4'h0
|
||||
`define OP_LOAD_A 4'h1
|
||||
`define OP_INC 4'h2
|
||||
`define OP_DEC 4'h3
|
||||
`define OP_ASL 4'h4
|
||||
`define OP_LSR 4'h5
|
||||
`define OP_ROL 4'h6
|
||||
`define OP_ROR 4'h7
|
||||
`define OP_OR 4'h8
|
||||
`define OP_AND 4'h9
|
||||
`define OP_XOR 4'ha
|
||||
`define OP_LOAD_B 4'hb
|
||||
`define OP_ADD 4'hc
|
||||
`define OP_SUB 4'hd
|
||||
`define OP_ADC 4'he
|
||||
`define OP_SBB 4'hf
|
||||
|
||||
// ALU module
|
||||
module ALU(A, B, carry, aluop, Y);
|
||||
|
||||
parameter N = 8; // default width = 8 bits
|
||||
input [N-1:0] A; // A input
|
||||
input [N-1:0] B; // B input
|
||||
input carry; // carry input
|
||||
input [3:0] aluop; // alu operation
|
||||
output [N:0] Y; // Y output + carry
|
||||
|
||||
always @(*)
|
||||
case (aluop)
|
||||
// unary operations
|
||||
`OP_ZERO: Y = 0;
|
||||
`OP_LOAD_A: Y = {1'b0, A};
|
||||
`OP_INC: Y = A + 1;
|
||||
`OP_DEC: Y = A - 1;
|
||||
// unary operations that generate and/or use carry
|
||||
`OP_ASL: Y = {A, 1'b0};
|
||||
`OP_LSR: Y = {A[0], 1'b0, A[N-1:1]};
|
||||
`OP_ROL: Y = {A, carry};
|
||||
`OP_ROR: Y = {A[0], carry, A[N-1:1]};
|
||||
// binary operations
|
||||
`OP_OR: Y = {1'b0, A | B};
|
||||
`OP_AND: Y = {1'b0, A & B};
|
||||
`OP_XOR: Y = {1'b0, A ^ B};
|
||||
`OP_LOAD_B: Y = {1'b0, B};
|
||||
// binary operations that generate and/or use carry
|
||||
`OP_ADD: Y = A + B;
|
||||
`OP_SUB: Y = A - B;
|
||||
`OP_ADC: Y = A + B + (carry?1:0);
|
||||
`OP_SBB: Y = A - B - (carry?1:0);
|
||||
endcase
|
||||
|
||||
endmodule
|
||||
|
||||
/*
|
||||
Bits Description
|
||||
|
||||
00ddaaaa A @ B -> dest
|
||||
01ddaaaa A @ immediate -> dest
|
||||
11ddaaaa A @ read [B] -> dest
|
||||
10000001 swap A <-> B
|
||||
1001nnnn A -> write [nnnn]
|
||||
1010tttt conditional branch
|
||||
|
||||
dd = destination (00=A, 01=B, 10=IP, 11=none)
|
||||
aaaa = ALU operation (@ operator)
|
||||
nnnn = 4-bit constant
|
||||
tttt = flags test for conditional branch
|
||||
*/
|
||||
|
||||
// destinations for COMPUTE instructions
|
||||
`define DEST_A 2'b00
|
||||
`define DEST_B 2'b01
|
||||
`define DEST_IP 2'b10
|
||||
`define DEST_NOP 2'b11
|
||||
|
||||
// instruction macros
|
||||
`define I_COMPUTE(dest,op) { 2'b00, (dest), (op) }
|
||||
`define I_COMPUTE_IMM(dest,op) { 2'b01, (dest), (op) }
|
||||
`define I_COMPUTE_READB(dest,op) { 2'b11, (dest), (op) }
|
||||
`define I_CONST_IMM_A { 2'b01, `DEST_A, `OP_LOAD_B }
|
||||
`define I_CONST_IMM_B { 2'b01, `DEST_B, `OP_LOAD_B }
|
||||
`define I_JUMP_IMM { 2'b01, `DEST_IP, `OP_LOAD_B }
|
||||
`define I_STORE_A(addr) { 4'b1001, (addr) }
|
||||
`define I_BRANCH_IF(zv,zu,cv,cu) { 4'b1010, (zv), (zu), (cv), (cu) }
|
||||
`define I_CLEAR_CARRY { 8'b10001000 }
|
||||
`define I_SWAP_AB { 8'b10000001 }
|
||||
`define I_RESET { 8'b10111111 }
|
||||
// convenience macros
|
||||
`define I_ZERO_A `I_COMPUTE(`DEST_A, `OP_ZERO)
|
||||
`define I_ZERO_B `I_COMPUTE(`DEST_B, `OP_ZERO)
|
||||
`define I_BRANCH_IF_CARRY(carry) `I_BRANCH_IF(1'b0, 1'b0, carry, 1'b1)
|
||||
`define I_BRANCH_IF_ZERO(zero) `I_BRANCH_IF(zero, 1'b1, 1'b0, 1'b0)
|
||||
`define I_CLEAR_ZERO `I_COMPUTE(`DEST_NOP, `OP_ZERO)
|
||||
|
||||
module CPU(clk, reset, address, data_in, data_out, write);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
output [7:0] address;
|
||||
input [7:0] data_in;
|
||||
output [7:0] data_out;
|
||||
output write;
|
||||
|
||||
reg [7:0] IP;
|
||||
reg [7:0] A, B;
|
||||
reg [8:0] Y;
|
||||
reg [2:0] state;
|
||||
|
||||
reg carry;
|
||||
reg zero;
|
||||
wire [1:0] flags = { zero, carry };
|
||||
|
||||
reg [7:0] opcode;
|
||||
wire [3:0] aluop = opcode[3:0];
|
||||
wire [1:0] opdest = opcode[5:4];
|
||||
wire B_or_data = opcode[6];
|
||||
|
||||
localparam S_RESET = 0;
|
||||
localparam S_SELECT = 1;
|
||||
localparam S_DECODE = 2;
|
||||
localparam S_COMPUTE = 3;
|
||||
localparam S_READ_IP = 4;
|
||||
|
||||
ALU alu(
|
||||
.A(A),
|
||||
.B(B_or_data ? data_in : B),
|
||||
.Y(Y),
|
||||
.aluop(aluop),
|
||||
.carry(carry));
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset) begin
|
||||
state <= 0;
|
||||
write <= 0;
|
||||
end else begin
|
||||
case (state)
|
||||
// state 0: reset
|
||||
S_RESET: begin
|
||||
IP <= 8'h80;
|
||||
write <= 0;
|
||||
state <= S_SELECT;
|
||||
end
|
||||
// state 1: select opcode address
|
||||
S_SELECT: begin
|
||||
address <= IP;
|
||||
IP <= IP + 1;
|
||||
write <= 0;
|
||||
state <= S_DECODE;
|
||||
end
|
||||
// state 2: read/decode opcode
|
||||
S_DECODE: begin
|
||||
opcode <= data_in; // (only use opcode next cycle)
|
||||
casez (data_in)
|
||||
// ALU A + B -> dest
|
||||
8'b00??????: begin
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// ALU A + immediate -> dest
|
||||
8'b01??????: begin
|
||||
address <= IP;
|
||||
IP <= IP + 1;
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// ALU A + read [B] -> dest
|
||||
8'b11??????: begin
|
||||
address <= B;
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// A -> write [nnnn]
|
||||
8'b1001????: begin
|
||||
address <= {4'b0, data_in[3:0]};
|
||||
data_out <= A;
|
||||
write <= 1;
|
||||
state <= S_SELECT;
|
||||
end
|
||||
// swap A,B
|
||||
8'b10000001: begin
|
||||
A <= B;
|
||||
B <= A;
|
||||
state <= S_SELECT;
|
||||
end
|
||||
// conditional branch
|
||||
8'b1010????: begin
|
||||
if (
|
||||
(data_in[0] && (data_in[1] == carry)) ||
|
||||
(data_in[2] && (data_in[3] == zero)))
|
||||
begin
|
||||
address <= IP;
|
||||
state <= S_READ_IP;
|
||||
end else begin
|
||||
state <= S_SELECT;
|
||||
end
|
||||
IP <= IP + 1; // skip immediate
|
||||
end
|
||||
// fall-through RESET
|
||||
default: begin
|
||||
state <= S_RESET; // reset
|
||||
end
|
||||
endcase
|
||||
end
|
||||
// state 3: compute ALU op and flags
|
||||
S_COMPUTE: begin
|
||||
// transfer ALU output to destination
|
||||
case (opdest)
|
||||
`DEST_A: A <= Y[7:0];
|
||||
`DEST_B: B <= Y[7:0];
|
||||
`DEST_IP: IP <= Y[7:0];
|
||||
`DEST_NOP: ;
|
||||
endcase
|
||||
// set carry for certain operations (4-7,12-15)
|
||||
if (aluop[2]) carry <= Y[8];
|
||||
// set zero flag
|
||||
zero <= ~|Y[7:0];
|
||||
// repeat CPU loop
|
||||
state <= S_SELECT;
|
||||
end
|
||||
// state 4: read new IP from memory (immediate mode)
|
||||
S_READ_IP: begin
|
||||
IP <= data_in;
|
||||
state <= S_SELECT;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
52
fpga/examples/crttest.v
Normal file
52
fpga/examples/crttest.v
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
A simple test pattern using the hvsync_generator module.
|
||||
|
||||
12000000/15734/2
|
||||
381.33977373840091521545
|
||||
381-256-23-7 = 95
|
||||
*/
|
||||
|
||||
module test_hvsync_top(clk, reset, out, led);
|
||||
|
||||
input clk, reset;
|
||||
output [1:0] out;
|
||||
wire hsync;
|
||||
wire vsync;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
reg [5:0] frame;
|
||||
output led;
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire r = display_on && (((hpos&7)==0) || (((vpos+frame)&7)==0));
|
||||
wire g = display_on && vpos[4];
|
||||
wire b = display_on && hpos[4];
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
|
||||
|
||||
always @(posedge vsync) begin
|
||||
frame <= frame + 1;
|
||||
end
|
||||
|
||||
assign led = frame[5];
|
||||
|
||||
endmodule
|
||||
|
||||
// TODO: PWM grey scales
|
207
fpga/examples/digits10.v
Normal file
207
fpga/examples/digits10.v
Normal file
@ -0,0 +1,207 @@
|
||||
|
||||
`ifndef DIGITS10_H
|
||||
`define DIGITS10_H
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
ROM module with 5x5 bitmaps for the digits 0-9.
|
||||
|
||||
digits10_case - Uses the case statement.
|
||||
digits10_array - Uses an array and initial block.
|
||||
|
||||
These two modules are functionally equivalent.
|
||||
*/
|
||||
|
||||
// module for 10-digit bitmap ROM
|
||||
module digits10_case(digit, yofs, 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 @(*)
|
||||
case (caseexpr)/*{w:5,h:5,count:10}*/
|
||||
7'o00: bits = 5'b11111;
|
||||
7'o01: bits = 5'b10001;
|
||||
7'o02: bits = 5'b10001;
|
||||
7'o03: bits = 5'b10001;
|
||||
7'o04: bits = 5'b11111;
|
||||
|
||||
7'o10: bits = 5'b01100;
|
||||
7'o11: bits = 5'b00100;
|
||||
7'o12: bits = 5'b00100;
|
||||
7'o13: bits = 5'b00100;
|
||||
7'o14: bits = 5'b11111;
|
||||
|
||||
7'o20: bits = 5'b11111;
|
||||
7'o21: bits = 5'b00001;
|
||||
7'o22: bits = 5'b11111;
|
||||
7'o23: bits = 5'b10000;
|
||||
7'o24: bits = 5'b11111;
|
||||
|
||||
7'o30: bits = 5'b11111;
|
||||
7'o31: bits = 5'b00001;
|
||||
7'o32: bits = 5'b11111;
|
||||
7'o33: bits = 5'b00001;
|
||||
7'o34: bits = 5'b11111;
|
||||
|
||||
7'o40: bits = 5'b10001;
|
||||
7'o41: bits = 5'b10001;
|
||||
7'o42: bits = 5'b11111;
|
||||
7'o43: bits = 5'b00001;
|
||||
7'o44: bits = 5'b00001;
|
||||
|
||||
7'o50: bits = 5'b11111;
|
||||
7'o51: bits = 5'b10000;
|
||||
7'o52: bits = 5'b11111;
|
||||
7'o53: bits = 5'b00001;
|
||||
7'o54: bits = 5'b11111;
|
||||
|
||||
7'o60: bits = 5'b11111;
|
||||
7'o61: bits = 5'b10000;
|
||||
7'o62: bits = 5'b11111;
|
||||
7'o63: bits = 5'b10001;
|
||||
7'o64: bits = 5'b11111;
|
||||
|
||||
7'o70: bits = 5'b11111;
|
||||
7'o71: bits = 5'b00001;
|
||||
7'o72: bits = 5'b00001;
|
||||
7'o73: bits = 5'b00001;
|
||||
7'o74: bits = 5'b00001;
|
||||
|
||||
7'o100: bits = 5'b11111;
|
||||
7'o101: bits = 5'b10001;
|
||||
7'o102: bits = 5'b11111;
|
||||
7'o103: bits = 5'b10001;
|
||||
7'o104: bits = 5'b11111;
|
||||
|
||||
7'o110: bits = 5'b11111;
|
||||
7'o111: bits = 5'b10001;
|
||||
7'o112: bits = 5'b11111;
|
||||
7'o113: bits = 5'b00001;
|
||||
7'o114: bits = 5'b11111;
|
||||
|
||||
default: bits = 0;
|
||||
endcase
|
||||
endmodule
|
||||
|
||||
module digits10_array(digit, yofs, 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[16][5]; // ROM array (16 x 5 x 5 bits)
|
||||
|
||||
assign bits = bitarray[digit][yofs]; // assign module output
|
||||
|
||||
integer i,j;
|
||||
|
||||
initial begin/*{w:5,h:5,count:10}*/
|
||||
bitarray[0][0] = 5'b11111;
|
||||
bitarray[0][1] = 5'b10001;
|
||||
bitarray[0][2] = 5'b10001;
|
||||
bitarray[0][3] = 5'b10001;
|
||||
bitarray[0][4] = 5'b11111;
|
||||
|
||||
bitarray[1][0] = 5'b01100;
|
||||
bitarray[1][1] = 5'b00100;
|
||||
bitarray[1][2] = 5'b00100;
|
||||
bitarray[1][3] = 5'b00100;
|
||||
bitarray[1][4] = 5'b11111;
|
||||
|
||||
bitarray[2][0] = 5'b11111;
|
||||
bitarray[2][1] = 5'b00001;
|
||||
bitarray[2][2] = 5'b11111;
|
||||
bitarray[2][3] = 5'b10000;
|
||||
bitarray[2][4] = 5'b11111;
|
||||
|
||||
bitarray[3][0] = 5'b11111;
|
||||
bitarray[3][1] = 5'b00001;
|
||||
bitarray[3][2] = 5'b11111;
|
||||
bitarray[3][3] = 5'b00001;
|
||||
bitarray[3][4] = 5'b11111;
|
||||
|
||||
bitarray[4][0] = 5'b10001;
|
||||
bitarray[4][1] = 5'b10001;
|
||||
bitarray[4][2] = 5'b11111;
|
||||
bitarray[4][3] = 5'b00001;
|
||||
bitarray[4][4] = 5'b00001;
|
||||
|
||||
bitarray[5][0] = 5'b11111;
|
||||
bitarray[5][1] = 5'b10000;
|
||||
bitarray[5][2] = 5'b11111;
|
||||
bitarray[5][3] = 5'b00001;
|
||||
bitarray[5][4] = 5'b11111;
|
||||
|
||||
bitarray[6][0] = 5'b11111;
|
||||
bitarray[6][1] = 5'b10000;
|
||||
bitarray[6][2] = 5'b11111;
|
||||
bitarray[6][3] = 5'b10001;
|
||||
bitarray[6][4] = 5'b11111;
|
||||
|
||||
bitarray[7][0] = 5'b11111;
|
||||
bitarray[7][1] = 5'b00001;
|
||||
bitarray[7][2] = 5'b00001;
|
||||
bitarray[7][3] = 5'b00001;
|
||||
bitarray[7][4] = 5'b00001;
|
||||
|
||||
bitarray[8][0] = 5'b11111;
|
||||
bitarray[8][1] = 5'b10001;
|
||||
bitarray[8][2] = 5'b11111;
|
||||
bitarray[8][3] = 5'b10001;
|
||||
bitarray[8][4] = 5'b11111;
|
||||
|
||||
bitarray[9][0] = 5'b11111;
|
||||
bitarray[9][1] = 5'b10001;
|
||||
bitarray[9][2] = 5'b11111;
|
||||
bitarray[9][3] = 5'b00001;
|
||||
bitarray[9][4] = 5'b11111;
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
// test module
|
||||
module test_numbers_top(clk, reset, hsync, vsync, rgb);
|
||||
|
||||
input clk, reset;
|
||||
output hsync, vsync;
|
||||
output [2:0] rgb;
|
||||
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire [3:0] digit = hpos[7:4];
|
||||
wire [2:0] xofs = hpos[3:1];
|
||||
wire [2:0] yofs = vpos[3:1];
|
||||
wire [4:0] bits;
|
||||
|
||||
digits10_array numbers(
|
||||
.digit(digit),
|
||||
.yofs(yofs),
|
||||
.bits(bits)
|
||||
);
|
||||
|
||||
wire r = display_on && 0;
|
||||
wire g = display_on && bits[xofs ^ 3'b111];
|
||||
wire b = display_on && 0;
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
21
fpga/examples/font_cp437_8x8.v
Normal file
21
fpga/examples/font_cp437_8x8.v
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
`ifndef FONT_CP437_H
|
||||
`define FONT_CP437_H
|
||||
|
||||
// PC font (code page 437)
|
||||
|
||||
module font_cp437_8x8(addr, data);
|
||||
|
||||
input [10:0] addr;
|
||||
output [7:0] data;
|
||||
|
||||
reg [7:0] bitarray[0:2047];
|
||||
|
||||
assign data = bitarray[addr];
|
||||
|
||||
initial begin/*{w:8,h:8,bpp:1,count:256}*/
|
||||
$readmemh("cp437.hex", bitarray);
|
||||
end
|
||||
endmodule
|
||||
|
||||
`endif
|
69
fpga/examples/hvsync_generator.v
Normal file
69
fpga/examples/hvsync_generator.v
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
`ifndef HVSYNC_GENERATOR_H
|
||||
`define HVSYNC_GENERATOR_H
|
||||
|
||||
/*
|
||||
Video sync generator, used to drive a simulated CRT.
|
||||
To use:
|
||||
- Wire the hsync and vsync signals to top level outputs
|
||||
- Add a 3-bit (or more) "rgb" output to the top level
|
||||
*/
|
||||
|
||||
module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
output reg hsync, vsync;
|
||||
output display_on;
|
||||
output reg [8:0] hpos;
|
||||
output reg [8:0] vpos;
|
||||
|
||||
// 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 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 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;
|
||||
parameter V_SYNC_START = V_DISPLAY + V_BOTTOM;
|
||||
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; // set when hpos is maximum
|
||||
wire vmaxxed = (vpos == V_MAX) || reset; // set when vpos is maximum
|
||||
|
||||
// horizontal position counter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END);
|
||||
if(hmaxxed)
|
||||
hpos <= 0;
|
||||
else
|
||||
hpos <= hpos + 1;
|
||||
end
|
||||
|
||||
// vertical position counter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END);
|
||||
if(hmaxxed)
|
||||
if (vmaxxed)
|
||||
vpos <= 0;
|
||||
else
|
||||
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
|
||||
|
||||
`endif
|
16
fpga/examples/icestick.pcf
Normal file
16
fpga/examples/icestick.pcf
Normal file
@ -0,0 +1,16 @@
|
||||
# full iCEstick pinout:
|
||||
# http://www.pighixxx.com/test/portfolio-items/icestick/
|
||||
|
||||
#set_io --warn-no-port RX 9
|
||||
#set_io --warn-no-port TX 8
|
||||
#set_io LED1 99
|
||||
#set_io LED5 95
|
||||
|
||||
set_io clk 21
|
||||
|
||||
set_io out[0] 80
|
||||
set_io out[1] 81
|
||||
|
||||
set_io --warn-no-port hpaddle 79
|
||||
set_io --warn-no-port vpaddle 78
|
||||
|
31
fpga/examples/lfsr.v
Normal file
31
fpga/examples/lfsr.v
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
`ifndef LFSR_V
|
||||
`define LFSR_V
|
||||
|
||||
/*
|
||||
Configurable Linear Feedback Shift Register.
|
||||
*/
|
||||
|
||||
module LFSR(clk, reset, enable, lfsr);
|
||||
|
||||
parameter NBITS = 8; // bit width
|
||||
parameter TAPS = 8'b11101; // bitmask for taps
|
||||
parameter INVERT = 0; // invert feedback bit?
|
||||
|
||||
input clk, reset;
|
||||
input enable; // only perform shift when enable=1
|
||||
output reg [NBITS-1:0] lfsr; // shift register
|
||||
|
||||
wire feedback = lfsr[NBITS-1] ^ INVERT;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
lfsr <= {lfsr[NBITS-2:0], 1'b1}; // reset loads with all 1s
|
||||
else if (enable)
|
||||
lfsr <= {lfsr[NBITS-2:0], 1'b0} ^ (feedback ? TAPS : 0);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
111
fpga/examples/racing.asm
Normal file
111
fpga/examples/racing.asm
Normal file
@ -0,0 +1,111 @@
|
||||
.arch ../../presets/verilog/femto8 ; FEMTO-8 architecture
|
||||
.org 128 ; origin = 128 ($80)
|
||||
.len 128 ; length = 128 ($80)
|
||||
|
||||
; define constants
|
||||
.define PADDLE_X 0
|
||||
.define PADDLE_Y 1
|
||||
.define PLAYER_X 2
|
||||
.define PLAYER_Y 3
|
||||
.define ENEMY_X 4
|
||||
.define ENEMY_Y 5
|
||||
.define ENEMY_DIR 6
|
||||
.define SPEED 7
|
||||
.define TRACKPOS_LO 8
|
||||
.define TRACKPOS_HI 9
|
||||
|
||||
.define IN_HPOS $40
|
||||
.define IN_VPOS $41
|
||||
.define IN_FLAGS $42
|
||||
|
||||
.define F_DISPLAY 1
|
||||
.define F_HPADDLE 2
|
||||
.define F_VPADDLE 4
|
||||
.define F_HSYNC 8
|
||||
.define F_VSYNC 16
|
||||
.define F_COLLIDE 32
|
||||
|
||||
Start:
|
||||
lda #128 ; load initial positions
|
||||
sta PLAYER_X ; player_x = 128
|
||||
sta ENEMY_X ; enemy_x = 128
|
||||
sta ENEMY_Y ; enemy_y = 128
|
||||
lda #180
|
||||
sta PLAYER_Y ; player_y = 180
|
||||
zero A
|
||||
sta SPEED ; player speed = 0
|
||||
inc A
|
||||
sta ENEMY_DIR ; enemy dir = 1 (right)
|
||||
; read horizontal paddle position
|
||||
DisplayLoop:
|
||||
; lda #F_HPADDLE ; paddle flag -> A
|
||||
; ldb #IN_FLAGS ; addr of IN_FLAGS -> B
|
||||
; and none,[B] ; read B, AND with A
|
||||
; bz DisplayLoop ; loop until paddle flag set
|
||||
ldb #IN_VPOS
|
||||
mov A,[B] ; load vertical position -> A
|
||||
sta PLAYER_X ; store player x position
|
||||
; wait for vsync
|
||||
lda #F_VSYNC
|
||||
ldb #IN_FLAGS
|
||||
WaitForVsyncOn:
|
||||
and none,[B]
|
||||
bz WaitForVsyncOn ; wait until VSYNC on
|
||||
WaitForVsyncOff:
|
||||
and none,[B]
|
||||
bnz WaitForVsyncOff ; wait until VSYNC off
|
||||
; check collision
|
||||
lda #F_COLLIDE
|
||||
ldb #IN_FLAGS
|
||||
and none,[B] ; collision flag set?
|
||||
bz NoCollision ; skip ahead if not
|
||||
lda #16
|
||||
sta SPEED ; speed = 16
|
||||
NoCollision:
|
||||
; update speed
|
||||
ldb #SPEED
|
||||
mov A,[B] ; speed -> A
|
||||
inc A ; increment speed
|
||||
bz MaxSpeed ; speed wraps to 0?
|
||||
sta SPEED ; no, store speed
|
||||
MaxSpeed:
|
||||
mov A,[B] ; reload speed -> A
|
||||
lsr A
|
||||
lsr A
|
||||
lsr A
|
||||
lsr A ; divide speed by 16
|
||||
; add to lo byte of track pos
|
||||
ldb #TRACKPOS_LO
|
||||
add B,[B] ; B <- speed/16 + trackpos_lo
|
||||
swapab ; swap A <-> B
|
||||
sta TRACKPOS_LO ; A -> trackpos_lo
|
||||
swapab ; swap A <-> B again
|
||||
bcc NoCarry ; carry flag from earlier add op
|
||||
; add to hi byte of track pos
|
||||
ldb #TRACKPOS_HI
|
||||
mov B,[B] ; B <- trackpos_hi
|
||||
inc b ; increment B
|
||||
swapab ; swap A <-> B
|
||||
sta TRACKPOS_HI ; A -> trackpos_hi
|
||||
swapab ; swap A <-> B again
|
||||
NoCarry:
|
||||
; update enemy vert pos
|
||||
ldb #ENEMY_Y
|
||||
add A,[B]
|
||||
sta ENEMY_Y ; enemy_y = enemy_y + speed/16
|
||||
; update enemy horiz pos
|
||||
ldb #ENEMY_X
|
||||
mov A,[B]
|
||||
ldb #ENEMY_DIR
|
||||
add A,[B]
|
||||
sta ENEMY_X ; enemy_x = enemy_x + enemy_dir
|
||||
sub A,#64
|
||||
and A,#127 ; A <- (enemy_x-64) & 127
|
||||
bnz SkipXReverse ; skip if enemy_x is in range
|
||||
; load ENEMY_DIR and negate
|
||||
zero A
|
||||
sub A,[B]
|
||||
sta ENEMY_DIR ; enemy_dir = -enemy_dir
|
||||
SkipXReverse:
|
||||
; back to display loop
|
||||
jmp DisplayLoop
|
128
fpga/examples/racing.hex
Normal file
128
fpga/examples/racing.hex
Normal file
@ -0,0 +1,128 @@
|
||||
4b
|
||||
80
|
||||
92
|
||||
94
|
||||
95
|
||||
4b
|
||||
b4
|
||||
93
|
||||
0
|
||||
97
|
||||
2
|
||||
96
|
||||
5b
|
||||
41
|
||||
cb
|
||||
92
|
||||
4b
|
||||
10
|
||||
5b
|
||||
42
|
||||
f9
|
||||
ac
|
||||
94
|
||||
f9
|
||||
a4
|
||||
97
|
||||
4b
|
||||
20
|
||||
5b
|
||||
42
|
||||
f9
|
||||
ac
|
||||
a4
|
||||
4b
|
||||
10
|
||||
97
|
||||
5b
|
||||
7
|
||||
cb
|
||||
2
|
||||
ac
|
||||
ab
|
||||
97
|
||||
cb
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
5b
|
||||
8
|
||||
dc
|
||||
81
|
||||
98
|
||||
81
|
||||
a1
|
||||
bf
|
||||
5b
|
||||
9
|
||||
db
|
||||
12
|
||||
81
|
||||
99
|
||||
81
|
||||
5b
|
||||
5
|
||||
cc
|
||||
95
|
||||
5b
|
||||
4
|
||||
cb
|
||||
5b
|
||||
6
|
||||
cc
|
||||
94
|
||||
4d
|
||||
40
|
||||
49
|
||||
7f
|
||||
a4
|
||||
d3
|
||||
0
|
||||
cd
|
||||
96
|
||||
6b
|
||||
8c
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
158
fpga/examples/racing_game.v
Normal file
158
fpga/examples/racing_game.v
Normal file
@ -0,0 +1,158 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "sprite_bitmap.v"
|
||||
`include "sprite_renderer.v"
|
||||
|
||||
/*
|
||||
A simple racing game with two sprites and a scrolling playfield.
|
||||
This version does not use a CPU; all logic is straight Verilog.
|
||||
*/
|
||||
|
||||
module racing_game_top(clk, reset, out, hpaddle, vpaddle);
|
||||
|
||||
input clk, reset;
|
||||
input hpaddle, vpaddle;
|
||||
output [1:0] out;
|
||||
wire hsync, vsync;
|
||||
wire display_on;
|
||||
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;
|
||||
// enemy car direction, 1=right, 0=left
|
||||
reg enemy_dir = 0;
|
||||
|
||||
reg [15:0] track_pos = 0; // player position along track (16 bits)
|
||||
reg [7:0] speed = 31; // player velocity along track (8 bits)
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
// video sync generator
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// set paddle registers
|
||||
always @(posedge hsync)
|
||||
begin
|
||||
if (!hpaddle) paddle_x <= vpos[7:0];
|
||||
if (!vpaddle) paddle_y <= vpos[7:0];
|
||||
end
|
||||
|
||||
|
||||
// select player or enemy access to ROM
|
||||
wire player_load = (hpos >= 256) && (hpos < 260);
|
||||
wire enemy_load = (hpos >= 260);
|
||||
// wire up car sprite ROM
|
||||
// multiplex between player and enemy ROM address
|
||||
wire [3:0] player_sprite_yofs;
|
||||
wire [3:0] enemy_sprite_yofs;
|
||||
wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs;
|
||||
wire [7:0] car_sprite_bits;
|
||||
car_bitmap car(
|
||||
.yofs(car_sprite_yofs),
|
||||
.bits(car_sprite_bits));
|
||||
|
||||
// signals for player sprite generator
|
||||
wire player_vstart = {1'b0,player_y} == vpos;
|
||||
wire player_hstart = {1'b0,player_x} == hpos;
|
||||
wire player_gfx;
|
||||
wire player_is_drawing;
|
||||
|
||||
// signals for enemy sprite generator
|
||||
wire enemy_vstart = {1'b0,enemy_y} == vpos;
|
||||
wire enemy_hstart = {1'b0,enemy_x} == hpos;
|
||||
wire enemy_gfx;
|
||||
wire enemy_is_drawing;
|
||||
|
||||
// player sprite generator
|
||||
sprite_renderer player_renderer(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.vstart(player_vstart),
|
||||
.load(player_load),
|
||||
.hstart(player_hstart),
|
||||
.rom_addr(player_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(player_gfx),
|
||||
.in_progress(player_is_drawing));
|
||||
|
||||
// enemy sprite generator
|
||||
sprite_renderer enemy_renderer(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.vstart(enemy_vstart),
|
||||
.load(enemy_load),
|
||||
.hstart(enemy_hstart),
|
||||
.rom_addr(enemy_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(enemy_gfx),
|
||||
.in_progress(enemy_is_drawing));
|
||||
|
||||
// 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 <= 64; //paddle_x;
|
||||
player_y <= 180;
|
||||
track_pos <= track_pos + {11'b0,speed[7:4]};
|
||||
enemy_y <= enemy_y + {3'b0, speed[7:4]};
|
||||
if (enemy_hit_edge)
|
||||
enemy_dir <= !enemy_dir;
|
||||
if (enemy_dir ^ enemy_hit_edge)
|
||||
enemy_x <= enemy_x + 1;
|
||||
else
|
||||
enemy_x <= enemy_x - 1;
|
||||
// collision check?
|
||||
if (frame_collision)
|
||||
speed <= 16;
|
||||
else if (speed < ~paddle_y)
|
||||
speed <= speed + 1;
|
||||
else if (speed > 16)
|
||||
speed <= speed - 1;
|
||||
end
|
||||
|
||||
// set to 1 when player collides with enemy or track
|
||||
reg frame_collision;
|
||||
|
||||
always @(posedge clk2)
|
||||
if (player_gfx && (enemy_gfx || track_gfx))
|
||||
frame_collision <= 1;
|
||||
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 && (enemy_gfx || track_shoulder);
|
||||
wire g = display_on && (player_gfx || track_gfx);
|
||||
wire b = display_on && (player_gfx || track_shoulder);
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
|
||||
|
||||
endmodule
|
169
fpga/examples/racing_game_cpu.v
Normal file
169
fpga/examples/racing_game_cpu.v
Normal file
@ -0,0 +1,169 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "sprite_bitmap.v"
|
||||
`include "sprite_renderer.v"
|
||||
`include "cpu8.v"
|
||||
|
||||
/*
|
||||
A simple racing game with two sprites and a scrolling playfield.
|
||||
This version uses the 8-bit CPU.
|
||||
*/
|
||||
|
||||
// uncomment to see scope view
|
||||
//`define DEBUG
|
||||
|
||||
module racing_game_top(clk, reset, out, hpaddle, vpaddle);
|
||||
|
||||
input clk, reset;
|
||||
input hpaddle, vpaddle;
|
||||
output [1:0] out;
|
||||
wire hsync, vsync;
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
parameter PADDLE_X = 0; // paddle X coordinate
|
||||
parameter PADDLE_Y = 1; // paddle Y coordinate
|
||||
parameter PLAYER_X = 2; // player X coordinate
|
||||
parameter PLAYER_Y = 3; // player Y coordinate
|
||||
parameter ENEMY_X = 4; // enemy X coordinate
|
||||
parameter ENEMY_Y = 5; // enemy Y coordinate
|
||||
parameter ENEMY_DIR = 6; // enemy direction (1, -1)
|
||||
parameter SPEED = 7; // player speed
|
||||
parameter TRACKPOS_LO = 8; // track position (lo byte)
|
||||
parameter TRACKPOS_HI = 9; // track position (hi byte)
|
||||
|
||||
parameter IN_HPOS = 8'h40; // CRT horizontal position
|
||||
parameter IN_VPOS = 8'h41; // CRT vertical position
|
||||
// flags: [0, 0, collision, vsync, hsync, vpaddle, hpaddle, display_on]
|
||||
parameter IN_FLAGS = 8'h42;
|
||||
|
||||
reg [7:0] ram[0:15]; // 16 bytes of RAM
|
||||
reg [7:0] rom[0:127]; // 128 bytes of ROM
|
||||
|
||||
wire [7:0] address_bus; // CPU address bus
|
||||
reg [7:0] to_cpu; // data bus to CPU
|
||||
wire [7:0] from_cpu; // data bus from CPU
|
||||
wire write_enable; // write enable bit from CPU
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
// 8-bit CPU module
|
||||
CPU cpu(.clk(clk2),
|
||||
.reset(reset),
|
||||
.address(address_bus),
|
||||
.data_in(to_cpu),
|
||||
.data_out(from_cpu),
|
||||
.write(write_enable));
|
||||
|
||||
// RAM write from CPU
|
||||
always @(posedge clk2)
|
||||
if (write_enable)
|
||||
ram[address_bus[5:0]] <= from_cpu;
|
||||
|
||||
// RAM read from CPU
|
||||
always @(*)
|
||||
casez (address_bus)
|
||||
// RAM
|
||||
8'b00??????: to_cpu = ram[address_bus[5:0]];
|
||||
// special read registers
|
||||
IN_HPOS: to_cpu = hpos[7:0];
|
||||
IN_VPOS: to_cpu = vpos[7:0];
|
||||
IN_FLAGS: to_cpu = {2'b0, frame_collision,
|
||||
vsync, hsync, vpaddle, hpaddle, display_on};
|
||||
// ROM
|
||||
8'b1???????: to_cpu = rom[address_bus[6:0]];
|
||||
default: to_cpu = 8'bxxxxxxxx;
|
||||
endcase
|
||||
|
||||
// video sync generator
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// flags for player sprite renderer module
|
||||
wire player_vstart = {1'b0,ram[PLAYER_Y]} == vpos;
|
||||
wire player_hstart = {1'b0,ram[PLAYER_X]} == hpos;
|
||||
wire player_gfx;
|
||||
wire player_is_drawing;
|
||||
|
||||
// flags for enemy sprite renderer module
|
||||
wire enemy_vstart = {1'b0,ram[ENEMY_Y]} == vpos;
|
||||
wire enemy_hstart = {1'b0,ram[ENEMY_X]} == hpos;
|
||||
wire enemy_gfx;
|
||||
wire enemy_is_drawing;
|
||||
|
||||
// select player or enemy access to ROM
|
||||
wire player_load = (hpos >= 256) && (hpos < 260);
|
||||
wire enemy_load = (hpos >= 260);
|
||||
// wire up car sprite ROM
|
||||
// multiplex between player and enemy ROM address
|
||||
wire [3:0] player_sprite_yofs;
|
||||
wire [3:0] enemy_sprite_yofs;
|
||||
wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs;
|
||||
wire [7:0] car_sprite_bits;
|
||||
car_bitmap car(
|
||||
.yofs(car_sprite_yofs),
|
||||
.bits(car_sprite_bits));
|
||||
|
||||
// player sprite renderer
|
||||
sprite_renderer player_renderer(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.vstart(player_vstart),
|
||||
.hstart(player_hstart),
|
||||
.load(player_load),
|
||||
.rom_addr(player_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(player_gfx),
|
||||
.in_progress(player_is_drawing));
|
||||
|
||||
// enemy sprite renderer
|
||||
sprite_renderer enemy_renderer(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.vstart(enemy_vstart),
|
||||
.hstart(enemy_hstart),
|
||||
.load(enemy_load),
|
||||
.rom_addr(enemy_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(enemy_gfx),
|
||||
.in_progress(enemy_is_drawing));
|
||||
|
||||
// collision detection logic
|
||||
reg frame_collision;
|
||||
always @(posedge clk2)
|
||||
if (player_gfx && (enemy_gfx || track_gfx))
|
||||
frame_collision <= 1;
|
||||
else if (vpos==0)
|
||||
frame_collision <= 0;
|
||||
|
||||
// track graphics
|
||||
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]!=ram[TRACKPOS_LO][5:1]) && track_offside;
|
||||
|
||||
// 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);
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+g+(r|b));
|
||||
|
||||
//////////// CPU program code
|
||||
|
||||
initial begin
|
||||
$readmemh("racing.hex", rom);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
80
fpga/examples/ram.v
Normal file
80
fpga/examples/ram.v
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
`ifndef RAM_H
|
||||
`define RAM_H
|
||||
|
||||
/*
|
||||
RAM_sync - Synchronous RAM module.
|
||||
RAM_async - Asynchronous RAM module.
|
||||
RAM_async_tristate - Async RAM module with bidirectional data bus.
|
||||
|
||||
Module parameters:
|
||||
|
||||
A - number of address bits (default = 10)
|
||||
D - number of data bits (default = 8)
|
||||
*/
|
||||
|
||||
module RAM_sync(clk, addr, din, dout, we);
|
||||
|
||||
parameter A = 10; // # of address bits
|
||||
parameter D = 8; // # of data bits
|
||||
|
||||
input clk; // clock
|
||||
input [A-1:0] addr; // address
|
||||
input [D-1:0] din; // data input
|
||||
output [D-1:0] dout; // data output
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
mem[addr] <= din; // write memory from din
|
||||
dout <= mem[addr]; // read memory to dout (sync)
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module RAM_async(clk, addr, din, dout, we);
|
||||
|
||||
parameter A = 10; // # of address bits
|
||||
parameter D = 8; // # of data bits
|
||||
|
||||
input clk; // clock
|
||||
input [A-1:0] addr; // address
|
||||
input [D-1:0] din; // data input
|
||||
output [D-1:0] dout; // data output
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
mem[addr] <= din; // write memory from din
|
||||
end
|
||||
|
||||
assign dout = mem[addr]; // read memory to dout (async)
|
||||
|
||||
endmodule
|
||||
|
||||
module RAM_async_tristate(clk, addr, data, we);
|
||||
|
||||
parameter A = 10; // # of address bits
|
||||
parameter D = 8; // # of data bits
|
||||
|
||||
input clk; // clock
|
||||
input [A-1:0] addr; // address
|
||||
inout [D-1:0] data; // data in/out
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
mem[addr] <= data; // write memory from data
|
||||
end
|
||||
|
||||
assign data = !we ? mem[addr] : {D{1'bz}}; // read memory to data (async)
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
114
fpga/examples/scoreboard.v
Normal file
114
fpga/examples/scoreboard.v
Normal file
@ -0,0 +1,114 @@
|
||||
|
||||
`ifndef SCOREBOARD_H
|
||||
`define SCOREBOARD_H
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "digits10.v"
|
||||
|
||||
/*
|
||||
player_stats - Holds two-digit score and one-digit lives counter.
|
||||
scoreboard_generator - Outputs video signal with score/lives digits.
|
||||
*/
|
||||
|
||||
module player_stats(reset, score0, score1, lives, incscore, declives);
|
||||
|
||||
input reset;
|
||||
output reg [3:0] score0;
|
||||
output reg [3:0] score1;
|
||||
input incscore;
|
||||
output reg [3:0] lives;
|
||||
input declives;
|
||||
|
||||
always @(posedge incscore or posedge reset)
|
||||
begin
|
||||
if (reset) begin
|
||||
score0 <= 0;
|
||||
score1 <= 0;
|
||||
end else if (score0 == 9) begin
|
||||
score0 <= 0;
|
||||
score1 <= score1 + 1;
|
||||
end else begin
|
||||
score0 <= score0 + 1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge declives or posedge reset)
|
||||
begin
|
||||
if (reset)
|
||||
lives <= 3;
|
||||
else if (lives != 0)
|
||||
lives <= lives - 1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module scoreboard_generator(score0, score1, lives, vpos, hpos, board_gfx);
|
||||
|
||||
input [3:0] score0;
|
||||
input [3:0] score1;
|
||||
input [3:0] lives;
|
||||
input [8:0] vpos;
|
||||
input [8:0] hpos;
|
||||
output board_gfx;
|
||||
|
||||
reg [3:0] score_digit;
|
||||
reg [4:0] score_bits;
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
case (hpos[7:5])
|
||||
1: score_digit = score1;
|
||||
2: score_digit = score0;
|
||||
6: score_digit = lives;
|
||||
default: score_digit = 15; // no digit
|
||||
endcase
|
||||
end
|
||||
|
||||
digits10_array digits(
|
||||
.digit(score_digit),
|
||||
.yofs(vpos[4:2]),
|
||||
.bits(score_bits)
|
||||
);
|
||||
|
||||
assign board_gfx = score_bits[hpos[4:2] ^ 3'b111];
|
||||
|
||||
endmodule
|
||||
|
||||
module scoreboard_top(clk, reset, hsync, vsync, rgb);
|
||||
|
||||
input clk, reset;
|
||||
output hsync, vsync;
|
||||
output [2:0] rgb;
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
wire board_gfx;
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
scoreboard_generator scoreboard_gen(
|
||||
.score0(0),
|
||||
.score1(1),
|
||||
.lives(3),
|
||||
.vpos(vpos),
|
||||
.hpos(hpos),
|
||||
.board_gfx(board_gfx)
|
||||
);
|
||||
|
||||
wire r = display_on && board_gfx;
|
||||
wire g = display_on && board_gfx;
|
||||
wire b = display_on && board_gfx;
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
44
fpga/examples/sprite_bitmap.v
Normal file
44
fpga/examples/sprite_bitmap.v
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
`ifndef SPRITE_BITMAP_H
|
||||
`define SPRITE_BITMAP_H
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
Simple sprite renderer example.
|
||||
|
||||
car_bitmap - ROM for a car sprite.
|
||||
sprite_bitmap_top - Example sprite rendering module.
|
||||
*/
|
||||
|
||||
module car_bitmap(yofs, bits);
|
||||
|
||||
input [3:0] yofs;
|
||||
output [7:0] bits;
|
||||
|
||||
reg [7:0] bitarray[0:15];
|
||||
|
||||
assign bits = bitarray[yofs];
|
||||
|
||||
initial begin/*{w:8,h:16}*/
|
||||
bitarray[0] = 8'b0;
|
||||
bitarray[1] = 8'b1100;
|
||||
bitarray[2] = 8'b11001100;
|
||||
bitarray[3] = 8'b11111100;
|
||||
bitarray[4] = 8'b11101100;
|
||||
bitarray[5] = 8'b11100000;
|
||||
bitarray[6] = 8'b1100000;
|
||||
bitarray[7] = 8'b1110000;
|
||||
bitarray[8] = 8'b110000;
|
||||
bitarray[9] = 8'b110000;
|
||||
bitarray[10] = 8'b110000;
|
||||
bitarray[11] = 8'b1101110;
|
||||
bitarray[12] = 8'b11101110;
|
||||
bitarray[13] = 8'b11111110;
|
||||
bitarray[14] = 8'b11101110;
|
||||
bitarray[15] = 8'b101110;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
97
fpga/examples/sprite_renderer.v
Normal file
97
fpga/examples/sprite_renderer.v
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
`ifndef SPRITE_RENDERER_H
|
||||
`define SPRITE_RENDERER_H
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "sprite_bitmap.v"
|
||||
|
||||
/*
|
||||
Displays a 16x16 sprite (8 bits mirrored left/right).
|
||||
*/
|
||||
|
||||
module sprite_renderer(clk, reset,
|
||||
vstart, load, hstart, rom_addr, rom_bits,
|
||||
gfx, in_progress);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input vstart; // start drawing (top border)
|
||||
input load; // ok to load sprite data?
|
||||
input hstart; // start drawing scanline (left border)
|
||||
output reg [3:0] rom_addr; // select ROM address
|
||||
input [7:0] rom_bits; // input bits from ROM
|
||||
output reg gfx; // output pixel
|
||||
output in_progress; // 0 if waiting for vstart
|
||||
|
||||
reg [2:0] state; // current state #
|
||||
reg [3:0] ycount; // number of scanlines drawn so far
|
||||
reg [3:0] xcount; // number of horiz. pixels in this line
|
||||
|
||||
reg [7:0] outbits; // register to store bits from ROM
|
||||
|
||||
// states for state machine
|
||||
localparam WAIT_FOR_VSTART = 0;
|
||||
localparam WAIT_FOR_LOAD = 1;
|
||||
localparam LOAD1_SETUP = 2;
|
||||
localparam LOAD1_FETCH = 3;
|
||||
localparam WAIT_FOR_HSTART = 4;
|
||||
localparam DRAW = 5;
|
||||
|
||||
// assign in_progress output bit
|
||||
assign in_progress = state != WAIT_FOR_VSTART;
|
||||
|
||||
always @(posedge clk or posedge reset)
|
||||
if (reset) begin
|
||||
state <= WAIT_FOR_VSTART;
|
||||
end else begin
|
||||
case (state)
|
||||
WAIT_FOR_VSTART: begin
|
||||
ycount <= 0; // initialize vertical count
|
||||
gfx <= 0; // default pixel value (off)
|
||||
// wait for vstart, then next state
|
||||
if (vstart)
|
||||
state <= WAIT_FOR_LOAD;
|
||||
end
|
||||
WAIT_FOR_LOAD: begin
|
||||
xcount <= 0; // initialize horiz. count
|
||||
gfx <= 0;
|
||||
// wait for load, then next state
|
||||
if (load)
|
||||
state <= LOAD1_SETUP;
|
||||
end
|
||||
LOAD1_SETUP: begin
|
||||
rom_addr <= ycount; // load ROM address
|
||||
state <= LOAD1_FETCH;
|
||||
end
|
||||
LOAD1_FETCH: begin
|
||||
outbits <= rom_bits; // latch bits from ROM
|
||||
state <= WAIT_FOR_HSTART;
|
||||
end
|
||||
WAIT_FOR_HSTART: begin
|
||||
// wait for hstart, then start drawing
|
||||
if (hstart)
|
||||
state <= DRAW;
|
||||
end
|
||||
DRAW: begin
|
||||
// get pixel, mirroring graphics left/right
|
||||
gfx <= outbits[xcount<8 ? xcount[2:0] : ~xcount[2:0]];
|
||||
xcount <= xcount + 1;
|
||||
// finished drawing horizontal slice?
|
||||
if (xcount == 15) begin // pre-increment value
|
||||
ycount <= ycount + 1;
|
||||
// finished drawing sprite?
|
||||
if (ycount == 15) // pre-increment value
|
||||
state <= WAIT_FOR_VSTART; // done drawing sprite
|
||||
else
|
||||
state <= WAIT_FOR_LOAD; // done drawing this scanline
|
||||
end
|
||||
end
|
||||
// unknown state -- reset
|
||||
default: begin
|
||||
state <= WAIT_FOR_VSTART;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
353
fpga/examples/sprite_rotation.v
Normal file
353
fpga/examples/sprite_rotation.v
Normal file
@ -0,0 +1,353 @@
|
||||
|
||||
`ifndef SPRITE_ROTATION_H
|
||||
`define SPRITE_ROTATION_H
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
tank_bitmap - ROM for tank bitmaps (5 different rotations)
|
||||
sprite_renderer2 - Displays a 16x16 sprite.
|
||||
tank_controller - Handles display and movement for one tank.
|
||||
*/
|
||||
|
||||
module tank_bitmap(addr, bits);
|
||||
|
||||
input [7:0] addr;
|
||||
output [7:0] bits;
|
||||
|
||||
reg [15:0] bitarray[0:255];
|
||||
|
||||
assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0];
|
||||
|
||||
initial begin/*{w:16,h:16,bpw:16,count:5}*/
|
||||
bitarray['h00] = 16'b11110000000;
|
||||
bitarray['h01] = 16'b11110000000;
|
||||
bitarray['h02] = 16'b1100000000;
|
||||
bitarray['h03] = 16'b1100000000;
|
||||
bitarray['h04] = 16'b111101101111000;
|
||||
bitarray['h05] = 16'b111101101111000;
|
||||
bitarray['h06] = 16'b111111111111000;
|
||||
bitarray['h07] = 16'b111111111111000;
|
||||
bitarray['h08] = 16'b111111111111000;
|
||||
bitarray['h09] = 16'b111111111111000;
|
||||
bitarray['h0a] = 16'b111111111111000;
|
||||
bitarray['h0b] = 16'b111100001111000;
|
||||
bitarray['h0c] = 16'b111100001111000;
|
||||
bitarray['h0d] = 16'b0;
|
||||
bitarray['h0e] = 16'b0;
|
||||
bitarray['h0f] = 16'b0;
|
||||
|
||||
bitarray['h10] = 16'b111000000000;
|
||||
bitarray['h11] = 16'b1111000000000;
|
||||
bitarray['h12] = 16'b1111000000000;
|
||||
bitarray['h13] = 16'b11000000000;
|
||||
bitarray['h14] = 16'b11101110000;
|
||||
bitarray['h15] = 16'b1101110000;
|
||||
bitarray['h16] = 16'b111101111110000;
|
||||
bitarray['h17] = 16'b111101111111000;
|
||||
bitarray['h18] = 16'b111111111111000;
|
||||
bitarray['h19] = 16'b11111111111000;
|
||||
bitarray['h1a] = 16'b11111111111100;
|
||||
bitarray['h1b] = 16'b11111111111100;
|
||||
bitarray['h1c] = 16'b11111001111100;
|
||||
bitarray['h1d] = 16'b1111001110000;
|
||||
bitarray['h1e] = 16'b1111000000000;
|
||||
bitarray['h1f] = 16'b1100000000000;
|
||||
|
||||
bitarray['h20] = 16'b0;
|
||||
bitarray['h21] = 16'b0;
|
||||
bitarray['h22] = 16'b11000011000000;
|
||||
bitarray['h23] = 16'b111000111100000;
|
||||
bitarray['h24] = 16'b111101111110000;
|
||||
bitarray['h25] = 16'b1110111111000;
|
||||
bitarray['h26] = 16'b111111111100;
|
||||
bitarray['h27] = 16'b11111111110;
|
||||
bitarray['h28] = 16'b11011111111110;
|
||||
bitarray['h29] = 16'b111111111111100;
|
||||
bitarray['h2a] = 16'b111111111001000;
|
||||
bitarray['h2b] = 16'b11111110000000;
|
||||
bitarray['h2c] = 16'b1111100000000;
|
||||
bitarray['h2d] = 16'b111110000000;
|
||||
bitarray['h2e] = 16'b11110000000;
|
||||
bitarray['h2f] = 16'b1100000000;
|
||||
|
||||
bitarray['h30] = 16'b0;
|
||||
bitarray['h31] = 16'b0;
|
||||
bitarray['h32] = 16'b110000000;
|
||||
bitarray['h33] = 16'b100001111000000;
|
||||
bitarray['h34] = 16'b1110001111110000;
|
||||
bitarray['h35] = 16'b1111010111111100;
|
||||
bitarray['h36] = 16'b1111111111111111;
|
||||
bitarray['h37] = 16'b1111111111111;
|
||||
bitarray['h38] = 16'b11111111110;
|
||||
bitarray['h39] = 16'b101111111110;
|
||||
bitarray['h3a] = 16'b1111111101100;
|
||||
bitarray['h3b] = 16'b11111111000000;
|
||||
bitarray['h3c] = 16'b1111111100000;
|
||||
bitarray['h3d] = 16'b11111110000;
|
||||
bitarray['h3e] = 16'b111100000;
|
||||
bitarray['h3f] = 16'b1100000;
|
||||
|
||||
bitarray['h40] = 16'b0;
|
||||
bitarray['h41] = 16'b0;
|
||||
bitarray['h42] = 16'b0;
|
||||
bitarray['h43] = 16'b111111111000;
|
||||
bitarray['h44] = 16'b111111111000;
|
||||
bitarray['h45] = 16'b111111111000;
|
||||
bitarray['h46] = 16'b111111111000;
|
||||
bitarray['h47] = 16'b1100001111100000;
|
||||
bitarray['h48] = 16'b1111111111100000;
|
||||
bitarray['h49] = 16'b1111111111100000;
|
||||
bitarray['h4a] = 16'b1100001111100000;
|
||||
bitarray['h4b] = 16'b111111111000;
|
||||
bitarray['h4c] = 16'b111111111000;
|
||||
bitarray['h4d] = 16'b111111111000;
|
||||
bitarray['h4e] = 16'b111111111000;
|
||||
bitarray['h4f] = 16'b0;
|
||||
end
|
||||
endmodule
|
||||
|
||||
// 16x16 sprite renderer that supports rotation
|
||||
module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits,
|
||||
hmirror, vmirror,
|
||||
gfx, busy);
|
||||
|
||||
input clk, vstart, load, hstart;
|
||||
input hmirror, vmirror;
|
||||
output [4:0] rom_addr;
|
||||
input [7:0] rom_bits;
|
||||
output gfx;
|
||||
output busy;
|
||||
|
||||
assign busy = state != WAIT_FOR_VSTART;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [3:0] ycount;
|
||||
reg [3:0] xcount;
|
||||
|
||||
reg [15:0] outbits;
|
||||
|
||||
localparam WAIT_FOR_VSTART = 0;
|
||||
localparam WAIT_FOR_LOAD = 1;
|
||||
localparam LOAD1_SETUP = 2;
|
||||
localparam LOAD1_FETCH = 3;
|
||||
localparam LOAD2_SETUP = 4;
|
||||
localparam LOAD2_FETCH = 5;
|
||||
localparam WAIT_FOR_HSTART = 6;
|
||||
localparam DRAW = 7;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
case (state)
|
||||
WAIT_FOR_VSTART: begin
|
||||
ycount <= 0;
|
||||
// set a default value (blank) for pixel output
|
||||
// note: multiple non-blocking assignments are vendor-specific
|
||||
gfx <= 0;
|
||||
if (vstart) state <= WAIT_FOR_LOAD;
|
||||
end
|
||||
WAIT_FOR_LOAD: begin
|
||||
xcount <= 0;
|
||||
gfx <= 0;
|
||||
if (load) state <= LOAD1_SETUP;
|
||||
end
|
||||
LOAD1_SETUP: begin
|
||||
rom_addr <= {vmirror?~ycount:ycount, 1'b0};
|
||||
state <= LOAD1_FETCH;
|
||||
end
|
||||
LOAD1_FETCH: begin
|
||||
outbits[7:0] <= rom_bits;
|
||||
state <= LOAD2_SETUP;
|
||||
end
|
||||
LOAD2_SETUP: begin
|
||||
rom_addr <= {vmirror?~ycount:ycount, 1'b1};
|
||||
state <= LOAD2_FETCH;
|
||||
end
|
||||
LOAD2_FETCH: begin
|
||||
outbits[15:8] <= rom_bits;
|
||||
state <= WAIT_FOR_HSTART;
|
||||
end
|
||||
WAIT_FOR_HSTART: begin
|
||||
if (hstart) state <= DRAW;
|
||||
end
|
||||
DRAW: begin
|
||||
// mirror graphics left/right
|
||||
gfx <= outbits[hmirror ? ~xcount[3:0] : xcount[3:0]];
|
||||
xcount <= xcount + 1;
|
||||
if (xcount == 15) begin // pre-increment value
|
||||
ycount <= ycount + 1;
|
||||
if (ycount == 15) // pre-increment value
|
||||
state <= WAIT_FOR_VSTART; // done drawing sprite
|
||||
else
|
||||
state <= WAIT_FOR_LOAD; // done drawing this scanline
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// converts 0..15 rotation value to bitmap index / mirror bits
|
||||
module rotation_selector(rotation, bitmap_num, hmirror, vmirror);
|
||||
|
||||
input [3:0] rotation; // angle (0..15)
|
||||
output [2:0] bitmap_num; // bitmap index (0..4)
|
||||
output hmirror, vmirror; // horiz & vert mirror bits
|
||||
|
||||
always @(*)
|
||||
case (rotation[3:2]) // 4 quadrants
|
||||
0: begin // 0..3 -> 0..3
|
||||
bitmap_num = {1'b0, rotation[1:0]};
|
||||
hmirror = 0;
|
||||
vmirror = 0;
|
||||
end
|
||||
1: begin // 4..7 -> 4..1
|
||||
bitmap_num = -rotation[2:0];
|
||||
hmirror = 0;
|
||||
vmirror = 1;
|
||||
end
|
||||
2: begin // 8-11 -> 0..3
|
||||
bitmap_num = {1'b0, rotation[1:0]};
|
||||
hmirror = 1;
|
||||
vmirror = 1;
|
||||
end
|
||||
3: begin // 12-15 -> 4..1
|
||||
bitmap_num = -rotation[2:0];
|
||||
hmirror = 1;
|
||||
vmirror = 0;
|
||||
end
|
||||
endcase
|
||||
|
||||
endmodule
|
||||
|
||||
// tank controller module -- handles rendering and movement
|
||||
module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
||||
sprite_addr, sprite_bits, gfx,
|
||||
playfield,
|
||||
switch_left, switch_right, switch_up);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input hsync;
|
||||
input vsync;
|
||||
input [8:0] hpos;
|
||||
input [8:0] vpos;
|
||||
output [7:0] sprite_addr;
|
||||
input [7:0] sprite_bits;
|
||||
output gfx;
|
||||
input playfield;
|
||||
input switch_left, switch_right, switch_up;
|
||||
|
||||
parameter initial_x = 128;
|
||||
parameter initial_y = 120;
|
||||
parameter initial_rot = 0;
|
||||
|
||||
wire hmirror, vmirror;
|
||||
wire busy;
|
||||
wire collision_gfx = gfx && playfield;
|
||||
|
||||
reg [11:0] player_x_fixed;
|
||||
wire [7:0] player_x = player_x_fixed[11:4];
|
||||
wire [3:0] player_x_frac = player_x_fixed[3:0];
|
||||
|
||||
reg [11:0] player_y_fixed;
|
||||
wire [7:0] player_y = player_y_fixed[11:4];
|
||||
wire [3:0] player_y_frac = player_y_fixed[3:0];
|
||||
|
||||
reg [3:0] player_rot;
|
||||
reg [3:0] player_speed;
|
||||
reg [3:0] frame = 0;
|
||||
|
||||
wire vstart = {1'b0,player_y} == vpos;
|
||||
wire hstart = {1'b0,player_x} == hpos;
|
||||
|
||||
sprite_renderer2 renderer(
|
||||
.clk(clk),
|
||||
.vstart(vstart),
|
||||
.load(hsync),
|
||||
.hstart(hstart),
|
||||
.hmirror(hmirror),
|
||||
.vmirror(vmirror),
|
||||
.rom_addr(sprite_addr[4:0]),
|
||||
.rom_bits(sprite_bits),
|
||||
.gfx(gfx),
|
||||
.busy(busy));
|
||||
|
||||
rotation_selector rotsel(
|
||||
.rotation(player_rot),
|
||||
.bitmap_num(sprite_addr[7:5]),
|
||||
.hmirror(hmirror),
|
||||
.vmirror(vmirror));
|
||||
|
||||
always @(posedge vsync or posedge reset)
|
||||
begin
|
||||
if (reset) begin
|
||||
player_rot <= initial_rot;
|
||||
player_speed <= 0;
|
||||
end else begin
|
||||
frame <= frame + 1; // increment frame counter
|
||||
if (frame[0]) begin // only update every other frame
|
||||
if (switch_left)
|
||||
player_rot <= player_rot - 1; // turn left
|
||||
else if (switch_right)
|
||||
player_rot <= player_rot + 1; // turn right
|
||||
if (switch_up) begin
|
||||
if (player_speed != 15) // max accel
|
||||
player_speed <= player_speed + 1;
|
||||
end else
|
||||
player_speed <= 0; // stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// set if collision; cleared at vsync
|
||||
reg collision_detected;
|
||||
|
||||
always @(posedge clk)
|
||||
if (vstart)
|
||||
collision_detected <= 0;
|
||||
else if (collision_gfx)
|
||||
collision_detected <= 1;
|
||||
|
||||
// sine lookup (4 bits input, 4 signed bits output)
|
||||
function signed [3:0] sin_16x4;
|
||||
input [3:0] in; // input angle 0..15
|
||||
integer y;
|
||||
case (in[1:0]) // 4 values per quadrant
|
||||
0: y = 0;
|
||||
1: y = 3;
|
||||
2: y = 5;
|
||||
3: y = 6;
|
||||
endcase
|
||||
case (in[3:2]) // 4 quadrants
|
||||
0: sin_16x4 = 4'(y);
|
||||
1: sin_16x4 = 4'(7-y);
|
||||
2: sin_16x4 = 4'(-y);
|
||||
3: sin_16x4 = 4'(y-7);
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
always @(posedge hsync or posedge reset)
|
||||
if (reset) begin
|
||||
// set initial position
|
||||
player_x_fixed <= initial_x << 4;
|
||||
player_y_fixed <= initial_y << 4;
|
||||
end else begin
|
||||
// collision detected? move backwards
|
||||
if (collision_detected && vpos[3:1] == 0) begin
|
||||
if (vpos[0])
|
||||
player_x_fixed <= player_x_fixed + 12'(sin_16x4(player_rot+8));
|
||||
else
|
||||
player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+12));
|
||||
end else
|
||||
// forward movement
|
||||
if (vpos < 9'(player_speed)) begin
|
||||
if (vpos[0])
|
||||
player_x_fixed <= player_x_fixed + 12'(sin_16x4(player_rot));
|
||||
else
|
||||
player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+4));
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
183
fpga/examples/sprite_scanline_renderer.v
Normal file
183
fpga/examples/sprite_scanline_renderer.v
Normal file
@ -0,0 +1,183 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "ram.v"
|
||||
|
||||
/*
|
||||
sprite_scanline_renderer - Module that renders multiple
|
||||
sprites whose attributes are fetched from shared RAM,
|
||||
and whose bitmaps are stored in ROM. Made to be paired
|
||||
with the FEMTO-16 CPU.
|
||||
*/
|
||||
|
||||
module example_bitmap_rom(addr, data);
|
||||
|
||||
input [15:0] addr;
|
||||
output [15:0] data;
|
||||
|
||||
reg [15:0] bitarray[0:255];
|
||||
|
||||
assign data = bitarray[addr & 15];
|
||||
|
||||
initial begin/*{w:16,h:16,bpw:16,count:1}*/
|
||||
bitarray['h00] = 16'b11110000000;
|
||||
bitarray['h01] = 16'b100001000000;
|
||||
bitarray['h02] = 16'b1111111100000;
|
||||
bitarray['h03] = 16'b1111111100000;
|
||||
bitarray['h04] = 16'b11110000000;
|
||||
bitarray['h05] = 16'b11111111110000;
|
||||
bitarray['h06] = 16'b111100001111000;
|
||||
bitarray['h07] = 16'b1111101101111100;
|
||||
bitarray['h08] = 16'b1101100001101111;
|
||||
bitarray['h09] = 16'b1101111111100110;
|
||||
bitarray['h0a] = 16'b1001111111100000;
|
||||
bitarray['h0b] = 16'b1111111100000;
|
||||
bitarray['h0c] = 16'b1110011100000;
|
||||
bitarray['h0d] = 16'b1100001100000;
|
||||
bitarray['h0e] = 16'b1100001100000;
|
||||
bitarray['h0f] = 16'b11100001110000;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
|
||||
ram_addr, ram_data, ram_busy,
|
||||
rom_addr, rom_data);
|
||||
|
||||
parameter NB = 5; // 2^NB == number of sprites
|
||||
parameter MB = 3; // 2^MB == slots per scanline
|
||||
|
||||
localparam N = 1<<NB; // number of sprites
|
||||
localparam M = 1<<MB; // slots per scanline
|
||||
|
||||
input clk, reset; // clock and reset inputs
|
||||
input [8:0] hpos; // horiz. sync pos
|
||||
input [8:0] vpos; // vert. sync pos
|
||||
output [3:0] rgb; // rgb output
|
||||
|
||||
output [NB:0] ram_addr; // RAM for sprite data
|
||||
input [15:0] ram_data; // (2 words per sprite)
|
||||
output ram_busy; // set when accessing RAM
|
||||
|
||||
output [15:0] rom_addr; // sprite ROM address
|
||||
input [15:0] rom_data; // sprite ROM data
|
||||
|
||||
// copy of sprite data from RAM (N entries)
|
||||
reg [7:0] sprite_xpos[0:N-1]; // X positions
|
||||
reg [7:0] sprite_ypos[0:N-1]; // Y positions
|
||||
reg [7:0] sprite_attr[0:N-1]; // attributes
|
||||
|
||||
// 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
|
||||
reg line_active[0:M-1]; // slot active?
|
||||
|
||||
// temporary counters
|
||||
reg [NB-1:0] i; // read index for main array (0..N-1)
|
||||
reg [MB-1:0] j; // write index for slots (0..M-1)
|
||||
reg [MB-1:0] k; // read index for slots (0..M-1)
|
||||
reg [7:0] z; // relative y offset of sprite
|
||||
reg [8:0] write_ofs; // write index of scanline buffer
|
||||
reg [15:0] out_bitmap; // shift register while writing scanline
|
||||
reg [7:0] out_attr; // attribute while writing scanline
|
||||
reg romload; // 1 when ROM address bus is stable
|
||||
|
||||
// which sprite are we currently reading?
|
||||
wire [NB-1:0] load_index = hpos[NB:1];
|
||||
|
||||
// RGB dual scanline buffer
|
||||
reg [3:0] scanline[0:511];
|
||||
|
||||
// which offset in scanline buffer to read?
|
||||
wire [8:0] read_bufidx = {vpos[0], hpos[7:0]};
|
||||
|
||||
// always block (every clock cycle)
|
||||
always @(posedge clk) begin
|
||||
|
||||
ram_busy <= 0;
|
||||
// reset every frame, don't draw vpos >= 256
|
||||
if (reset || vpos[8]) begin
|
||||
// load sprites from RAM on line 260
|
||||
// 8 cycles per sprite
|
||||
// do first sprite twice b/c CPU might still be busy
|
||||
if (vpos == 260 && hpos < N*2+8) begin
|
||||
ram_busy <= 1;
|
||||
case (hpos[0])
|
||||
0: begin
|
||||
ram_addr <= {load_index, 1'b0};
|
||||
// load X and Y position (2 cycles ago)
|
||||
sprite_xpos[load_index] <= ram_data[7:0];
|
||||
sprite_ypos[load_index] <= ram_data[15:8];
|
||||
end
|
||||
1: begin
|
||||
ram_addr <= {load_index, 1'b1};
|
||||
// load attribute (2 cycles ago)
|
||||
sprite_attr[load_index] <= ram_data[7:0];
|
||||
end
|
||||
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]);
|
||||
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
|
||||
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*18) begin
|
||||
// setup vars for next phase
|
||||
j <= 0;
|
||||
// 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])
|
||||
scanline[write_ofs] <= out_attr[3:0];
|
||||
// shift bits right
|
||||
out_bitmap <= out_bitmap >> 1;
|
||||
// increment to next scanline pixel
|
||||
write_ofs <= write_ofs + 1;
|
||||
end
|
||||
end else begin
|
||||
// clear counters
|
||||
i <= 0;
|
||||
end
|
||||
|
||||
// read and clear buffer
|
||||
rgb <= scanline[read_bufidx];
|
||||
scanline[read_bufidx] <= 0;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
77
fpga/examples/spritetest.v
Normal file
77
fpga/examples/spritetest.v
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "sprite_bitmap.v"
|
||||
`include "sprite_renderer.v"
|
||||
|
||||
/*
|
||||
A simple racing game with two sprites and a scrolling playfield.
|
||||
This version does not use a CPU; all logic is straight Verilog.
|
||||
*/
|
||||
|
||||
module racing_game_top(clk, reset, out);
|
||||
|
||||
input clk, reset;
|
||||
output [1:0] out;
|
||||
|
||||
wire hsync, vsync;
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
// player car position (set at VSYNC)
|
||||
reg [7:0] player_x;
|
||||
reg [7:0] player_y;
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
// video sync generator
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// select player or enemy access to ROM
|
||||
wire player_load = (hpos >= 256);
|
||||
// wire up car sprite ROM
|
||||
wire [3:0] car_sprite_yofs;
|
||||
wire [7:0] car_sprite_bits;
|
||||
car_bitmap car(
|
||||
.yofs(car_sprite_yofs),
|
||||
.bits(car_sprite_bits));
|
||||
|
||||
// signals for player sprite generator
|
||||
wire player_vstart = player_y == vpos;
|
||||
wire player_hstart = player_x == hpos;
|
||||
wire player_gfx;
|
||||
wire player_is_drawing;
|
||||
|
||||
// player sprite generator
|
||||
sprite_renderer player_renderer(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.vstart(player_vstart),
|
||||
.load(player_load),
|
||||
.hstart(player_hstart),
|
||||
.rom_addr(car_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(player_gfx),
|
||||
.in_progress(player_is_drawing));
|
||||
|
||||
// runs once per frame
|
||||
always @(posedge vsync)
|
||||
begin
|
||||
player_x <= player_x + 1;
|
||||
player_y <= player_y + 1;
|
||||
end
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : display_on ? (1+player_gfx+(player_vstart|player_hstart|player_is_drawing)) : 1;
|
||||
|
||||
endmodule
|
47
fpga/examples/starfield.v
Normal file
47
fpga/examples/starfield.v
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "lfsr.v"
|
||||
|
||||
/*
|
||||
Scrolling starfield generator using a period (2^16-1) LFSR.
|
||||
*/
|
||||
|
||||
module starfield_top(clk, reset, out);
|
||||
|
||||
input clk, reset;
|
||||
output [1:0] out;
|
||||
wire hsync, vsync;
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
wire [15:0] lfsr;
|
||||
|
||||
reg clk2;
|
||||
always @(posedge clk) begin
|
||||
clk2 <= !clk2;
|
||||
end
|
||||
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire star_enable = !hpos[8] & !vpos[8];
|
||||
|
||||
// LFSR with period = 2^16-1 = 256*256-1
|
||||
LFSR #(16,16'b1000000001011,0) lfsr_gen(
|
||||
.clk(clk2),
|
||||
.reset(reset),
|
||||
.enable(star_enable),
|
||||
.lfsr(lfsr));
|
||||
|
||||
wire star_on = &lfsr[15:9];
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : star_on ? (1+lfsr[1]+lfsr[2]) : 1;
|
||||
|
||||
endmodule
|
34
fpga/examples/test.vlog
Normal file
34
fpga/examples/test.vlog
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
module test;
|
||||
|
||||
reg clk;
|
||||
reg reset;
|
||||
reg hpaddle;
|
||||
reg vpaddle;
|
||||
wire out0;
|
||||
wire out1;
|
||||
|
||||
chip chip(
|
||||
.io_7_0_0(clk),
|
||||
.io_0_8_1(reset),
|
||||
.io_13_4_0(hpaddle),
|
||||
.io_13_3_1(vpaddle),
|
||||
.io_13_6_0(out0),
|
||||
.io_13_4_1(out1)
|
||||
);
|
||||
|
||||
always #2 clk = !clk;
|
||||
|
||||
initial begin
|
||||
$dumpfile("racing_game_cpu.vcd");
|
||||
$dumpvars(0,test);
|
||||
|
||||
#1 clk = 0;
|
||||
#5 reset = 1;
|
||||
#10 reset = 0;
|
||||
|
||||
#100000 $finish();
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
32
fpga/examples/test_hvsync.v
Normal file
32
fpga/examples/test_hvsync.v
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
|
||||
/*
|
||||
A simple test pattern using the hvsync_generator module.
|
||||
*/
|
||||
|
||||
module test_hvsync_top(clk, reset, hsync, vsync, rgb);
|
||||
|
||||
input clk, reset;
|
||||
output hsync, vsync;
|
||||
output [2:0] rgb;
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(0),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire r = display_on && (((hpos&7)==0) || ((vpos&7)==0));
|
||||
wire g = display_on && vpos[4];
|
||||
wire b = display_on && hpos[4];
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
endmodule
|
89
fpga/examples/tile_renderer.v
Normal file
89
fpga/examples/tile_renderer.v
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
/*
|
||||
Displays a 32x30 grid of 8x8 tiles, whose attributes are
|
||||
fetched from RAM, and whose bitmap patterns are in ROM.
|
||||
*/
|
||||
|
||||
module tile_renderer(clk, reset, hpos, vpos,
|
||||
rgb,
|
||||
ram_addr, ram_read, ram_busy,
|
||||
rom_addr, rom_data);
|
||||
|
||||
input clk, reset;
|
||||
input [8:0] hpos;
|
||||
input [8:0] vpos;
|
||||
output [3:0] rgb;
|
||||
|
||||
// start loading cells from RAM at this hpos value
|
||||
// first column read will be ((HLOAD-2) % 32)
|
||||
parameter HLOAD = 272;
|
||||
|
||||
output reg [15:0] ram_addr;
|
||||
input [15:0] ram_read;
|
||||
output reg ram_busy;
|
||||
|
||||
output [10:0] rom_addr;
|
||||
input [7:0] rom_data;
|
||||
|
||||
reg [7:0] page_base = 8'h7e; // page table base (8 bits)
|
||||
reg [15:0] row_base; // row table base (16 bits)
|
||||
reg [4:0] row;
|
||||
//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 [15:0] cur_cell;
|
||||
wire [7:0] cur_char = cur_cell[7:0];
|
||||
wire [7:0] cur_attr = cur_cell[15:8];
|
||||
|
||||
// tile ROM address
|
||||
assign rom_addr = {cur_char, yofs};
|
||||
|
||||
reg [15:0] row_buffer[0:31];
|
||||
|
||||
// lookup char and attr
|
||||
always @(posedge clk) begin
|
||||
// reset row to 0 when last row displayed
|
||||
if (vpos == 248) begin
|
||||
row <= 0;
|
||||
end
|
||||
// time to read a row?
|
||||
if (vpos[2:0] == 7) begin
|
||||
// read row_base from page table (2 bytes)
|
||||
case (hpos)
|
||||
// assert busy 5 cycles before first RAM read
|
||||
HLOAD-8: ram_busy <= 1;
|
||||
// read page base for row
|
||||
HLOAD-3: ram_addr <= {page_base, 3'b000, row};
|
||||
HLOAD-1: row_base <= ram_read;
|
||||
// deassert BUSY and increment row counter
|
||||
HLOAD+34: begin
|
||||
ram_busy <= 0;
|
||||
row <= row + 1;
|
||||
end
|
||||
endcase
|
||||
// load row of tile data from RAM
|
||||
// (last two twice)
|
||||
if (hpos >= HLOAD && hpos < HLOAD+34) begin
|
||||
ram_addr <= row_base + hpos[4:0];
|
||||
row_buffer[hpos[4:0] - 5'd2] <= ram_read;
|
||||
end
|
||||
end
|
||||
// latch character data
|
||||
if (hpos < 256) begin
|
||||
case (hpos[2:0])
|
||||
7: begin
|
||||
cur_cell <= row_buffer[col+1];
|
||||
end
|
||||
endcase
|
||||
end else if (hpos == 308) begin
|
||||
cur_cell <= row_buffer[0];
|
||||
end
|
||||
end
|
||||
|
||||
// extract bit from ROM output
|
||||
assign rgb = rom_data[~xofs] ? cur_attr[3:0] : cur_attr[7:4];
|
||||
|
||||
endmodule
|
||||
|
68
fpga/examples/tiletest.v
Normal file
68
fpga/examples/tiletest.v
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
`include "hvsync_generator.v"
|
||||
`include "font_cp437_8x8.v"
|
||||
`include "ram.v"
|
||||
`include "tile_renderer.v"
|
||||
|
||||
module test_tilerender_top(clk, reset, out);
|
||||
|
||||
input clk, reset;
|
||||
output [1:0] out;
|
||||
wire hsync, vsync;
|
||||
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
|
||||
reg [15:0] ram_addr;
|
||||
wire [15:0] ram_read;
|
||||
reg [15:0] ram_write = 0;
|
||||
reg ram_writeenable = 0;
|
||||
|
||||
wire [10:0] rom_addr;
|
||||
wire [7:0] rom_data;
|
||||
wire ram_busy;
|
||||
|
||||
hvsync_generator #(256,60,40,25) hvsync_gen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.hsync(hsync),
|
||||
.vsync(vsync),
|
||||
.display_on(display_on),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
// RAM
|
||||
RAM_sync #(10,16) ram(
|
||||
.clk(clk),
|
||||
.dout(ram_read),
|
||||
.din(ram_write),
|
||||
.addr(ram_addr),
|
||||
.we(ram_writeenable)
|
||||
);
|
||||
|
||||
wire [3:0] rgb_tile;
|
||||
|
||||
tile_renderer tile_gen(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.hpos(hpos),
|
||||
.vpos(vpos),
|
||||
.ram_addr(ram_addr),
|
||||
.ram_read(ram_read),
|
||||
.ram_busy(ram_busy),
|
||||
.rom_addr(rom_addr),
|
||||
.rom_data(rom_data),
|
||||
.rgb(rgb_tile)
|
||||
);
|
||||
|
||||
assign out = (hsync||vsync) ? 0 : (1+rgb_tile[0]+rgb_tile[1]);
|
||||
|
||||
// tile ROM
|
||||
font_cp437_8x8 tile_rom(
|
||||
.addr(rom_addr),
|
||||
.data(rom_data)
|
||||
);
|
||||
|
||||
endmodule
|
@ -95,7 +95,7 @@ module digits10_array(digit, yofs, bits);
|
||||
input [2:0] yofs; // vertical offset (0-4)
|
||||
output [4:0] bits; // output (5 bits)
|
||||
|
||||
reg [4:0] bitarray[16][5]; // ROM array (16 x 5 x 5 bits)
|
||||
reg [4:0] bitarray[0:15][0:4]; // ROM array (16 x 5 x 5 bits)
|
||||
|
||||
assign bits = bitarray[digit][yofs]; // assign module output
|
||||
|
||||
|
@ -97,7 +97,7 @@ module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
|
||||
.rom_addr(enemy_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(enemy_gfx),
|
||||
.in_progress(player_is_drawing));
|
||||
.in_progress(enemy_is_drawing));
|
||||
|
||||
// signals for enemy bouncing off left/right borders
|
||||
wire enemy_hit_left = (enemy_x == 64);
|
||||
|
@ -9,21 +9,7 @@ A simple racing game with two sprites and a scrolling playfield.
|
||||
This version uses the 8-bit CPU.
|
||||
*/
|
||||
|
||||
// uncomment to see scope view
|
||||
//`define DEBUG
|
||||
|
||||
module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
address_bus, to_cpu, from_cpu, write_enable
|
||||
`ifdef DEBUG
|
||||
, output [7:0] A
|
||||
, output [7:0] B
|
||||
, output [7:0] IP
|
||||
, output carry
|
||||
, output zero
|
||||
`else
|
||||
, rgb
|
||||
`endif
|
||||
);
|
||||
module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle, rgb);
|
||||
|
||||
input clk, reset;
|
||||
input hpaddle, vpaddle;
|
||||
@ -31,16 +17,7 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
wire display_on;
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
`ifdef DEBUG
|
||||
wire [2:0] rgb;
|
||||
assign IP = cpu.IP;
|
||||
assign A = cpu.A;
|
||||
assign B = cpu.B;
|
||||
assign carry = cpu.carry;
|
||||
assign zero = cpu.zero;
|
||||
`else
|
||||
output [3:0] rgb;
|
||||
`endif
|
||||
|
||||
parameter PADDLE_X = 0; // paddle X coordinate
|
||||
parameter PADDLE_Y = 1; // paddle Y coordinate
|
||||
@ -58,13 +35,13 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
// flags: [0, 0, collision, vsync, hsync, vpaddle, hpaddle, display_on]
|
||||
parameter IN_FLAGS = 8'h42;
|
||||
|
||||
reg [7:0] ram[64]; // 64 bytes of RAM
|
||||
reg [7:0] rom[128]; // 128 bytes of ROM
|
||||
reg [7:0] ram[0:15]; // 16 bytes of RAM
|
||||
reg [7:0] rom[0:127]; // 128 bytes of ROM
|
||||
|
||||
output wire [7:0] address_bus; // CPU address bus
|
||||
output reg [7:0] to_cpu; // data bus to CPU
|
||||
output wire [7:0] from_cpu; // data bus from CPU
|
||||
output wire write_enable; // write enable bit from CPU
|
||||
wire [7:0] address_bus; // CPU address bus
|
||||
reg [7:0] to_cpu; // data bus to CPU
|
||||
wire [7:0] from_cpu; // data bus from CPU
|
||||
wire write_enable; // write enable bit from CPU
|
||||
|
||||
// 8-bit CPU module
|
||||
CPU cpu(.clk(clk),
|
||||
@ -77,13 +54,13 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
// RAM write from CPU
|
||||
always @(posedge clk)
|
||||
if (write_enable)
|
||||
ram[address_bus[5:0]] <= from_cpu;
|
||||
ram[address_bus[3:0]] <= from_cpu;
|
||||
|
||||
// RAM read from CPU
|
||||
always @(*)
|
||||
casez (address_bus)
|
||||
// RAM
|
||||
8'b00??????: to_cpu = ram[address_bus[5:0]];
|
||||
8'b00??????: to_cpu = ram[address_bus[3:0]];
|
||||
// special read registers
|
||||
IN_HPOS: to_cpu = hpos[7:0];
|
||||
IN_VPOS: to_cpu = vpos[7:0];
|
||||
@ -150,7 +127,7 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
.rom_addr(enemy_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(enemy_gfx),
|
||||
.in_progress(player_is_drawing));
|
||||
.in_progress(enemy_is_drawing));
|
||||
|
||||
// collision detection logic
|
||||
reg frame_collision;
|
||||
|
@ -24,7 +24,7 @@ module RAM_sync(clk, addr, din, dout, we);
|
||||
output [D-1:0] dout; // data output
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
@ -45,7 +45,7 @@ module RAM_async(clk, addr, din, dout, we);
|
||||
output [D-1:0] dout; // data output
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
@ -66,7 +66,7 @@ module RAM_async_tristate(clk, addr, data, we);
|
||||
inout [D-1:0] data; // data in/out
|
||||
input we; // write enable
|
||||
|
||||
reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
|
||||
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (we) // if write enabled
|
||||
|
@ -73,14 +73,14 @@ module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb,
|
||||
reg line_active[0:M-1]; // slot active?
|
||||
|
||||
// temporary counters
|
||||
reg [NB-1:0] i; // 0..N-1
|
||||
reg [MB-1:0] j; // 0..M-1
|
||||
reg [MB-1:0] k; // 0..M-1
|
||||
reg [7:0] z;
|
||||
reg [8:0] write_ofs;
|
||||
reg [15:0] out_bitmap;
|
||||
reg [7:0] out_attr;
|
||||
reg romload;
|
||||
reg [NB-1:0] i; // read index for main array (0..N-1)
|
||||
reg [MB-1:0] j; // write index for slots (0..M-1)
|
||||
reg [MB-1:0] k; // read index for slots (0..M-1)
|
||||
reg [7:0] z; // relative y offset of sprite
|
||||
reg [8:0] write_ofs; // write index of scanline buffer
|
||||
reg [15:0] out_bitmap; // shift register while writing scanline
|
||||
reg [7:0] out_attr; // attribute while writing scanline
|
||||
reg romload; // 1 when ROM address bus is stable
|
||||
|
||||
// which sprite are we currently reading?
|
||||
wire [NB-1:0] load_index = hpos[NB:1];
|
||||
|
@ -18,7 +18,7 @@ var VERILOG_PRESETS = [
|
||||
{id:'ball_absolute.v', name:'Ball Motion (absolute position)'},
|
||||
{id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'},
|
||||
{id:'ball_paddle.v', name:'Brick Smash Game'},
|
||||
{id:'ram1.v', name:'RAM Text Display'},
|
||||
{id:'chardisplay.v', name:'RAM Text Display'},
|
||||
{id:'switches.v', name:'Switch Inputs'},
|
||||
{id:'paddles.v', name:'Paddle Inputs'},
|
||||
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
|
||||
|
21
src/tools/jsasm.js
Normal file
21
src/tools/jsasm.js
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var asm = require('../../gen/worker/assembler.js');
|
||||
var stdinBuffer = fs.readFileSync(0);
|
||||
var code = stdinBuffer.toString();
|
||||
var asm = new asm.Assembler();
|
||||
asm.loadJSON = function(filename) {
|
||||
return JSON.parse(fs.readFileSync(filename, 'utf8'));
|
||||
};
|
||||
asm.loadInclude = function(filename) {
|
||||
filename = filename.substr(1, filename.length-2); // remove quotes
|
||||
//return fs.readFileSync(filename, 'utf8');
|
||||
};
|
||||
asm.loadModule = function(top_module) {
|
||||
//TODO
|
||||
};
|
||||
var out = asm.assembleFile(code);
|
||||
//console.log(out);
|
||||
out.output.forEach(function(x) {
|
||||
console.log(x.toString(16));
|
||||
});
|
@ -54,7 +54,7 @@ type AssemblerState = {
|
||||
fixups: AssemblerFixup[]
|
||||
}
|
||||
|
||||
var Assembler = function(spec : AssemblerSpec) {
|
||||
export var Assembler = function(spec : AssemblerSpec) {
|
||||
var self = this;
|
||||
var ip = 0;
|
||||
var origin = 0;
|
||||
@ -365,25 +365,3 @@ var Assembler = function(spec : AssemblerSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
// Main
|
||||
/*
|
||||
declare var module, require;
|
||||
if (typeof module !== 'undefined' && require.main === module) {
|
||||
var fs = require('fs');
|
||||
var stdinBuffer = fs.readFileSync(0);
|
||||
var code = stdinBuffer.toString();
|
||||
var asm = new (Assembler as any)();
|
||||
asm.loadJSON = function(filename) {
|
||||
return JSON.parse(fs.readFileSync(filename, 'utf8'));
|
||||
};
|
||||
asm.loadInclude = function(filename) {
|
||||
filename = filename.substr(1, filename.length-2); // remove quotes
|
||||
//return fs.readFileSync(filename, 'utf8');
|
||||
};
|
||||
asm.loadModule = function(top_module) {
|
||||
//TODO
|
||||
};
|
||||
var out = asm.assembleFile(code);
|
||||
console.log(out);
|
||||
}
|
||||
*/
|
||||
|
@ -1225,7 +1225,7 @@ var jsasm_module_key;
|
||||
|
||||
function compileJSASM(asmcode:string, platform, options, is_inline) {
|
||||
loadGen("worker/assembler");
|
||||
var asm = new (Assembler as any)();
|
||||
var asm = new emglobal.exports.Assembler();
|
||||
var includes = [];
|
||||
asm.loadJSON = function(filename) {
|
||||
// TODO: what if it comes from dependencies?
|
||||
@ -1370,7 +1370,7 @@ function compileVerilator(step:BuildStep) {
|
||||
return {
|
||||
output: rtn.output,
|
||||
errors: errors,
|
||||
listings: {},
|
||||
listings: listings,
|
||||
};
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
|
Loading…
Reference in New Issue
Block a user