mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-07 04:30:46 +00:00
moved around ALU ops, 16-bit cpu, reg/wire
This commit is contained in:
parent
f6d320a05b
commit
20ddb8a11f
@ -3,7 +3,7 @@
|
||||
module seven_segment_decoder(digit, segments);
|
||||
|
||||
input [3:0] digit;
|
||||
output [6:0] segments;
|
||||
output reg [6:0] segments;
|
||||
|
||||
always @(*)
|
||||
case(digit)
|
||||
@ -26,7 +26,7 @@ module segments_to_bitmap(segments, line, bits);
|
||||
|
||||
input [6:0] segments;
|
||||
input [2:0] line;
|
||||
output [4:0] bits;
|
||||
output reg [4:0] bits;
|
||||
|
||||
always @(*)
|
||||
case (line)
|
||||
|
@ -5,20 +5,25 @@
|
||||
|
||||
/*
|
||||
00000aaa 0++++bbb operation A+B->A
|
||||
00001ttt ######## conditional branch
|
||||
00100aaa ######## load constant
|
||||
00101aaa ######## load memory
|
||||
00110aaa ######## store memory
|
||||
01000aaa #####bbb load [B+#] -> A
|
||||
00001aaa 0++++bbb operation A+[B]->A
|
||||
00011aaa 0++++000 operation A+imm16 -> A
|
||||
00101aaa ######## load zero page
|
||||
00110aaa ######## store zero page
|
||||
01001aaa #####bbb load [B+#] -> A
|
||||
01010aaa #####bbb store A -> [B+#]
|
||||
10+++aaa 10+++aaa dual unary operation
|
||||
01101aaa 0++++000 operation A+[imm16] -> A
|
||||
01110aaa 00cccbbb store A -> [B+#], C -> IP
|
||||
1000tttt ######## conditional branch
|
||||
11+++aaa ######## immediate binary operation
|
||||
*/
|
||||
|
||||
module CPU16(clk, reset, address, data_in, data_out, write);
|
||||
module CPU16(clk, reset, halt, busy,
|
||||
address, data_in, data_out, write);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input halt;
|
||||
output busy;
|
||||
output [15:0] address;
|
||||
input [15:0] data_in;
|
||||
output [15:0] data_out;
|
||||
@ -37,6 +42,7 @@ module CPU16(clk, reset, address, data_in, data_out, write);
|
||||
reg [3:0] aluop;
|
||||
wire [2:0] rdest = opcode[10:8];
|
||||
wire [2:0] rsrc = opcode[2:0];
|
||||
wire Bconst = opcode[15]; // TODO
|
||||
wire Bload = opcode[11]; // TODO
|
||||
|
||||
localparam S_RESET = 0;
|
||||
@ -44,19 +50,22 @@ module CPU16(clk, reset, address, data_in, data_out, write);
|
||||
localparam S_DECODE = 2;
|
||||
localparam S_COMPUTE = 3;
|
||||
|
||||
localparam SP = 6; // stack ptr = register 6
|
||||
localparam IP = 7; // IP = register 7
|
||||
|
||||
ALU #(16) alu(
|
||||
.A(regs[rdest]),
|
||||
.B(regs[rsrc]),
|
||||
.B(Bconst ? {8'b0, opcode[7:0]}
|
||||
: Bload ? data_in
|
||||
: regs[rsrc]),
|
||||
.Y(Y),
|
||||
.aluop(aluop),
|
||||
.carry(carry));
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset) begin
|
||||
state <= 0;
|
||||
write <= 0;
|
||||
state <= S_RESET;
|
||||
busy <= 1;
|
||||
end else begin
|
||||
case (state)
|
||||
// state 0: reset
|
||||
@ -67,46 +76,102 @@ module CPU16(clk, reset, address, data_in, data_out, write);
|
||||
end
|
||||
// state 1: select opcode address
|
||||
S_SELECT: begin
|
||||
write <= 0;
|
||||
if (halt) begin
|
||||
busy <= 1;
|
||||
state <= S_SELECT;
|
||||
end else begin
|
||||
busy <= 0;
|
||||
address <= regs[IP];
|
||||
regs[IP] <= regs[IP] + 1;
|
||||
write <= 0;
|
||||
state <= S_DECODE;
|
||||
end
|
||||
end
|
||||
// state 2: read/decode opcode
|
||||
S_DECODE: begin
|
||||
opcode <= data_in; // (only use opcode next cycle)
|
||||
casez (data_in)
|
||||
// 00000aaa 0++++bbb operation A+B->A
|
||||
// 00000aaa0++++bbb operation A+B->A
|
||||
16'b00000???0???????: begin
|
||||
aluop <= data_in[6:3];
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// 00100aaa ######## load constant
|
||||
16'b00100???????????: begin
|
||||
regs[rdest] <= {8'b0, data_in[7:0]};
|
||||
state <= S_SELECT;
|
||||
// 00001aaa01+++bbb operation A+[B]->A
|
||||
16'b00001???01??????: begin
|
||||
address <= regs[data_in[2:0]];
|
||||
aluop <= data_in[6:3];
|
||||
state <= S_COMPUTE;
|
||||
if (data_in[2:0] == SP)
|
||||
regs[SP] <= regs[SP] + 1;
|
||||
end
|
||||
// 00101aaa ######## load memory
|
||||
// 00011aaa0++++000 operation A+imm16 -> A
|
||||
16'b00011???0????000: begin
|
||||
address <= regs[IP];
|
||||
regs[IP] <= regs[IP] + 1;
|
||||
aluop <= data_in[6:3];
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// 11+++aaa######## immediate binary operation
|
||||
16'b11??????????????: begin
|
||||
aluop <= data_in[14:11];
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// 00101aaa######## load ZP memory
|
||||
16'b00101???????????: begin
|
||||
address <= {8'b0, data_in[7:0]};
|
||||
aluop <= `OP_LOAD_B;
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// 00110aaa ######## store memory
|
||||
// 00110aaa######## store ZP memory
|
||||
16'b00110???????????: begin
|
||||
address <= {8'b0, data_in[7:0]};
|
||||
data_out <= regs[data_in[10:8]];
|
||||
write <= 1;
|
||||
state <= S_SELECT;
|
||||
end
|
||||
// 00001ttt ######## conditional branch
|
||||
16'b00001???????????: begin
|
||||
// 01001aaa#####bbb [B+#] -> A
|
||||
16'b01001???????????: begin
|
||||
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
||||
aluop <= `OP_LOAD_B;
|
||||
state <= S_COMPUTE;
|
||||
if (data_in[2:0] == SP)
|
||||
regs[SP] <= regs[SP] + 1;
|
||||
end
|
||||
// 01010aaa#####bbb store A -> [B+#]
|
||||
16'b01010???????????: begin
|
||||
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
||||
data_out <= regs[data_in[10:8]];
|
||||
write <= 1;
|
||||
state <= S_SELECT;
|
||||
if (data_in[2:0] == SP)
|
||||
regs[SP] <= regs[SP] - 1;
|
||||
end
|
||||
// 01011aaa0++++000 operation A+[imm16] -> A
|
||||
16'b01011????????000: begin
|
||||
address <= regs[IP];
|
||||
regs[IP] <= regs[IP] + 1;
|
||||
aluop <= data_in[6:3];
|
||||
state <= S_COMPUTE;
|
||||
end
|
||||
// 01110aaa00cccbbb store A -> [B+#], C -> IP
|
||||
16'b01110???00??????: begin
|
||||
address <= regs[data_in[2:0]] + 16'($signed(data_in[7:3]));
|
||||
data_out <= regs[data_in[10:8]];
|
||||
write <= 1;
|
||||
state <= S_SELECT;
|
||||
if (data_in[2:0] == SP)
|
||||
regs[SP] <= regs[SP] - 1;
|
||||
regs[IP] <= regs[data_in[5:3]];
|
||||
end
|
||||
// 1000????######## conditional branch
|
||||
16'b1000????????????: begin
|
||||
if (
|
||||
(data_in[8] && (data_in[10] == carry)) ||
|
||||
(data_in[9] && (data_in[10] == zero)))
|
||||
(data_in[8] && (data_in[11] == carry)) ||
|
||||
(data_in[9] && (data_in[11] == zero)) ||
|
||||
(data_in[10] && (data_in[11] == neg)))
|
||||
begin
|
||||
// relative branch, sign extended
|
||||
regs[IP] <= regs[IP] + { {8{data_in[7]}}, data_in[7:0]};
|
||||
regs[IP] <= regs[IP] + 16'($signed(data_in[7:0]));
|
||||
end
|
||||
state <= S_SELECT;
|
||||
end
|
||||
@ -142,18 +207,22 @@ module test_CPU16_top(
|
||||
output write_enable,
|
||||
output [15:0] IP,
|
||||
output zero,
|
||||
output carry
|
||||
output carry,
|
||||
output busy
|
||||
);
|
||||
|
||||
reg [15:0] ram[0:65535];
|
||||
reg [15:0] rom[0:65535];
|
||||
reg [15:0] rom[0:255];
|
||||
|
||||
assign IP = cpu.regs[7];
|
||||
assign zero = cpu.zero;
|
||||
assign carry = cpu.carry;
|
||||
|
||||
CPU16 cpu(.clk(clk),
|
||||
CPU16 cpu(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.halt(0),
|
||||
.busy(busy),
|
||||
.address(address_bus),
|
||||
.data_in(to_cpu),
|
||||
.data_out(from_cpu),
|
||||
@ -168,7 +237,36 @@ module test_CPU16_top(
|
||||
if (address_bus[15] == 0)
|
||||
to_cpu = ram[address_bus];
|
||||
else
|
||||
to_cpu = rom[address_bus];
|
||||
to_cpu = rom[address_bus[7:0]];
|
||||
|
||||
`ifdef EXT_INLINE_ASM
|
||||
initial begin
|
||||
rom = '{
|
||||
__asm
|
||||
.arch femto16
|
||||
.org 0x8000
|
||||
.len 256
|
||||
mov dx,@Fib
|
||||
jsr dx
|
||||
reset
|
||||
Fib:
|
||||
mov ax,#1
|
||||
mov bx,#0
|
||||
mov sp,@$6fff
|
||||
Loop:
|
||||
mov cx,ax
|
||||
add ax,bx
|
||||
mov bx,cx
|
||||
push ax
|
||||
pop ax
|
||||
mov [42],ax
|
||||
mov ax,[42]
|
||||
bcc Loop
|
||||
rts
|
||||
__endasm
|
||||
};
|
||||
end
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
`define ALU_H
|
||||
|
||||
// ALU operations
|
||||
`define OP_LOAD_A 4'h0
|
||||
`define OP_LOAD_B 4'h1
|
||||
`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
|
||||
@ -13,7 +13,7 @@
|
||||
`define OP_OR 4'h8
|
||||
`define OP_AND 4'h9
|
||||
`define OP_XOR 4'ha
|
||||
`define OP_ZERO 4'hb
|
||||
`define OP_LOAD_B 4'hb
|
||||
`define OP_ADD 4'hc
|
||||
`define OP_SUB 4'hd
|
||||
`define OP_ADC 4'he
|
||||
@ -32,8 +32,8 @@ module ALU(A, B, Y, aluop, carry);
|
||||
always @(*)
|
||||
case (aluop)
|
||||
// unary operations
|
||||
`OP_ZERO: Y = 0;
|
||||
`OP_LOAD_A: Y = {1'b0, A};
|
||||
`OP_LOAD_B: Y = {1'b0, B};
|
||||
`OP_INC: Y = A + 1;
|
||||
`OP_DEC: Y = A - 1;
|
||||
// unary operations that generate and/or use carry
|
||||
@ -45,7 +45,7 @@ module ALU(A, B, Y, aluop, carry);
|
||||
`OP_OR: Y = {1'b0, A | B};
|
||||
`OP_AND: Y = {1'b0, A & B};
|
||||
`OP_XOR: Y = {1'b0, A ^ B};
|
||||
`OP_ZERO: Y = 0;
|
||||
`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;
|
||||
|
@ -7,9 +7,10 @@ module digits10_case(digit, yofs, bits);
|
||||
|
||||
input [3:0] digit;
|
||||
input [2:0] yofs;
|
||||
output [4:0] bits;
|
||||
output reg [4:0] bits;
|
||||
|
||||
wire [6:0] caseexpr = {digit,yofs};
|
||||
|
||||
always @(*)
|
||||
case (caseexpr)/*{w:5,h:5,count:10}*/
|
||||
7'o00: bits = 5'b11111;
|
||||
|
38
presets/verilog/femto16.json
Normal file
38
presets/verilog/femto16.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name":"femto8",
|
||||
"width":16,
|
||||
"vars":{
|
||||
"reg":{"bits":3, "toks":["ax", "bx", "cx", "dx", "ex", "fx", "sp", "ip"]},
|
||||
"unop":{"bits":3, "toks":["zero","loada","inc","dec","asl","lsr","rol","ror"]},
|
||||
"binop":{"bits":3, "toks":["or","and","xor","mov","add","sub","adc","sbb"]},
|
||||
"imm8":{"bits":8},
|
||||
"imm16":{"bits":16},
|
||||
"rel8":{"bits":8, "iprel":true, "ipofs":1}
|
||||
},
|
||||
"rules":[
|
||||
{"fmt":"~binop ~reg,~reg", "bits":["00000",1,"01",0,2]},
|
||||
{"fmt":"~unop ~reg", "bits":["00000",1,"00",0,"000"]},
|
||||
{"fmt":"~binop ~reg,[~reg]", "bits":["00001",1,"01",0,2]},
|
||||
{"fmt":"mov [~reg],~reg", "bits":["01010",1,"00000",0]},
|
||||
|
||||
{"fmt":"mov ~reg,[~imm8]", "bits":["00101",0,1]},
|
||||
{"fmt":"mov [~imm8],~reg", "bits":["00110",1,0]},
|
||||
{"fmt":"~binop ~reg,#~imm8", "bits":["11",0,1,2]},
|
||||
{"fmt":"~binop ~reg,@~imm16","bits":["00011",1,"01",0,"000",2]},
|
||||
{"fmt":"~binop ~reg,[~imm16]", "bits":["01011",1,"01",0,"000",2]},
|
||||
|
||||
{"fmt":"push ~reg", "bits":["01010",0,"00000","110"]},
|
||||
{"fmt":"pop ~reg", "bits":["01001",0,"00001","110"]},
|
||||
{"fmt":"rts", "bits":["01001","111","00001","110"]},
|
||||
{"fmt":"jsr ~reg", "bits":["01110","111","00",0,"110"]},
|
||||
|
||||
{"fmt":"bcc ~rel8", "bits":["10000001",0]},
|
||||
{"fmt":"bcs ~rel8", "bits":["10001001",0]},
|
||||
{"fmt":"bnz ~rel8", "bits":["10000010",0]},
|
||||
{"fmt":"bz ~rel8", "bits":["10001010",0]},
|
||||
{"fmt":"bpl ~rel8", "bits":["10000100",0]},
|
||||
{"fmt":"bmi ~rel8", "bits":["10001100",0]},
|
||||
|
||||
{"fmt":"reset", "bits":["1011100011111111"]}
|
||||
]
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
`undef EXT_INLINE_ASM
|
||||
`include "hvsync_generator.v"
|
||||
`include "sprite_bitmap.v"
|
||||
`include "sprite_renderer.v"
|
||||
`include "cpu8.v"
|
||||
`include "cpu16.v"
|
||||
`define EXT_INLINE_ASM
|
||||
|
||||
// uncomment to see scope view
|
||||
//`define DEBUG
|
||||
@ -9,13 +10,11 @@
|
||||
module frame_buffer_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 [15:0] IP
|
||||
, output carry
|
||||
, output zero
|
||||
`else
|
||||
,rgb
|
||||
, rgb
|
||||
`endif
|
||||
);
|
||||
|
||||
@ -26,9 +25,7 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
wire [8:0] hpos;
|
||||
wire [8:0] vpos;
|
||||
`ifdef DEBUG
|
||||
assign IP = cpu.IP;
|
||||
assign A = cpu.A;
|
||||
assign B = cpu.B;
|
||||
assign IP = cpu.regs[cpu.IP];
|
||||
assign carry = cpu.carry;
|
||||
assign zero = cpu.zero;
|
||||
`else
|
||||
@ -38,18 +35,19 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
parameter IN_HPOS = 8'b01000000;
|
||||
parameter IN_VPOS = 8'b01000001;
|
||||
parameter IN_FLAGS = 8'b01000010;
|
||||
parameter IN_VPU = 8'b01000011;
|
||||
|
||||
reg [7:0] ram[0:63];
|
||||
reg [7:0] rom[0:127];
|
||||
reg [15:0] ram[0:32767];
|
||||
reg [15:0] rom[0:1023];
|
||||
|
||||
output wire [7:0] address_bus;
|
||||
output reg [7:0] to_cpu;
|
||||
output wire [7:0] from_cpu;
|
||||
output wire [15:0] address_bus;
|
||||
output reg [15:0] to_cpu;
|
||||
output wire [15:0] from_cpu;
|
||||
output wire write_enable;
|
||||
|
||||
CPU cpu(.clk(clk),
|
||||
CPU16 cpu(.clk(clk),
|
||||
.reset(reset),
|
||||
.hold(hold),
|
||||
.busy(busy),
|
||||
.address(address_bus),
|
||||
.data_in(to_cpu),
|
||||
.data_out(from_cpu),
|
||||
@ -57,47 +55,23 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
|
||||
always @(posedge clk)
|
||||
if (write_enable) begin
|
||||
casez (address_bus)
|
||||
// VPU lo byte
|
||||
8'b0001000: begin
|
||||
vpu_write[15:8] <= from_cpu;
|
||||
end
|
||||
// VPU hi byte
|
||||
8'b0001001: begin
|
||||
vpu_write[7:0] <= from_cpu;
|
||||
end
|
||||
// VPU write
|
||||
8'b0001010: begin
|
||||
vpu_ram[vpu_write] <= from_cpu[7:4];
|
||||
vpu_ram[vpu_write+1] <= from_cpu[3:0];
|
||||
vpu_write <= vpu_write + 2;
|
||||
end
|
||||
// VPU move
|
||||
8'b0001011: begin
|
||||
// sign extend
|
||||
vpu_write <= {
|
||||
vpu_write[15:8] + { {4{from_cpu[7]}}, from_cpu[7:4] },
|
||||
vpu_write[7:0] + { {4{from_cpu[3]}}, from_cpu[3:0] }
|
||||
};
|
||||
end
|
||||
default: ram[address_bus[5:0]] <= from_cpu;
|
||||
endcase
|
||||
ram[address_bus[14:0]] <= from_cpu;
|
||||
end
|
||||
|
||||
always @(*)
|
||||
casez (address_bus)
|
||||
// RAM
|
||||
8'b00??????: to_cpu = ram[address_bus[5:0]];
|
||||
if (address_bus[15])
|
||||
to_cpu = rom[address_bus[9:0]];
|
||||
else if (&address_bus[14:8]) begin
|
||||
casez (address_bus[7:0])
|
||||
// special read registers
|
||||
IN_HPOS: to_cpu = hpos[7:0];
|
||||
IN_VPOS: to_cpu = vpos[7:0];
|
||||
IN_FLAGS: to_cpu = {3'b0,
|
||||
IN_HPOS: to_cpu = {8'b0, hpos[7:0]};
|
||||
IN_VPOS: to_cpu = {8'b0, vpos[7:0]};
|
||||
IN_FLAGS: to_cpu = {11'b0,
|
||||
vsync, hsync, vpaddle, hpaddle, display_on};
|
||||
IN_VPU: to_cpu = {vpu_ram[vpu_write], vpu_ram[vpu_write+1]};
|
||||
// ROM
|
||||
8'b1???????: to_cpu = rom[address_bus[7:0] + 128];
|
||||
default: ;
|
||||
default: to_cpu = 0;
|
||||
endcase
|
||||
end else
|
||||
to_cpu = ram[address_bus[14:0]];
|
||||
|
||||
hvsync_generator hvsync_gen(
|
||||
.clk(clk),
|
||||
@ -109,38 +83,49 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
reg [3:0] vpu_ram[0:65535];
|
||||
reg [15:0] vpu_read;
|
||||
reg [15:0] vpu_write;
|
||||
reg [3:0] rgb;
|
||||
// video DMA access
|
||||
reg hold;
|
||||
wire busy;
|
||||
reg [15:0] vline[0:31]; // 32x16 bits = 256 4-color pixels
|
||||
reg [4:0] vindex;
|
||||
reg [15:0] vshift;
|
||||
reg [3:0] palette[0:3] = '{0,1,4,7};
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (!hpos[8] && !vpos[8]) begin
|
||||
rgb <= vpu_ram[vpu_read];
|
||||
vpu_read <= vpu_read + 1;
|
||||
end else begin
|
||||
// has CPU released the bus?
|
||||
if (busy) begin
|
||||
// write from main RAM -> scanline RAM
|
||||
vline[vindex] <= ram[{2'b10,vpos[7:0],vindex}];
|
||||
// end of scanline read?
|
||||
if (&vindex)
|
||||
hold <= 0; // release CPU
|
||||
vindex <= vindex + 1; // next address
|
||||
end else if (hpos >= 256 && hpos < 256+4 && vpos < 240) begin
|
||||
hold <= 1; // start DMA mode, hold CPU
|
||||
end else if (!hpos[8] && vpos < 240) begin
|
||||
// load next word from vline buffer
|
||||
if (0 == hpos[2:0]) begin
|
||||
vshift <= vline[vindex];
|
||||
vindex <= vindex + 1;
|
||||
end else
|
||||
vshift <= vshift >> 2;
|
||||
// decode scanline RAM to RGB output
|
||||
rgb <= palette[vshift[1:0]];
|
||||
end else
|
||||
rgb <= 0;
|
||||
if (vpos[8])
|
||||
vpu_read <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef EXT_INLINE_ASM
|
||||
initial begin
|
||||
rom = '{
|
||||
__asm
|
||||
.arch femto8
|
||||
.org 128
|
||||
.len 128
|
||||
.arch femto16
|
||||
.org 32768
|
||||
.len 1024
|
||||
|
||||
.define VPU_LO 8
|
||||
.define VPU_HI 9
|
||||
.define VPU_WRITE 10
|
||||
.define VPU_MOVE 11
|
||||
|
||||
.define IN_HPOS $40
|
||||
.define IN_VPOS $41
|
||||
.define IN_FLAGS $42
|
||||
.define IN_HPOS $7f00
|
||||
.define IN_VPOS $7f01
|
||||
.define IN_FLAGS $7f02
|
||||
|
||||
.define F_DISPLAY 1
|
||||
.define F_HPADDLE 2
|
||||
@ -149,31 +134,15 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
.define F_VSYNC 16
|
||||
|
||||
Start:
|
||||
zero A
|
||||
sta VPU_LO
|
||||
sta VPU_HI
|
||||
sta 0
|
||||
DisplayLoop:
|
||||
zero B
|
||||
mov A,[b]
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
lda #F_VSYNC
|
||||
ldb #IN_FLAGS
|
||||
and none,[B]
|
||||
bz DisplayLoop
|
||||
WaitVsync:
|
||||
and none,[B]
|
||||
bnz WaitVsync
|
||||
zero B
|
||||
mov A,[b]
|
||||
inc A
|
||||
sta 0
|
||||
jmp DisplayLoop
|
||||
mov ax,#0
|
||||
mov bx,ax
|
||||
Loop:
|
||||
xor ax,[bx]
|
||||
mov [bx],ax
|
||||
inc bx
|
||||
inc ax
|
||||
bnz Loop
|
||||
reset
|
||||
__endasm
|
||||
};
|
||||
end
|
||||
|
@ -5,10 +5,10 @@ module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
output hsync, vsync;
|
||||
output reg hsync, vsync;
|
||||
output display_on;
|
||||
output [8:0] hpos;
|
||||
output [8:0] vpos;
|
||||
output reg [8:0] hpos;
|
||||
output reg [8:0] vpos;
|
||||
|
||||
// constant declarations for TV-simulator sync parameters
|
||||
// horizontal
|
||||
|
@ -7,10 +7,10 @@
|
||||
module player_stats(reset, score0, score1, lives, incscore, declives);
|
||||
|
||||
input reset;
|
||||
output [3:0] score0;
|
||||
output [3:0] score1;
|
||||
output reg [3:0] score0;
|
||||
output reg [3:0] score1;
|
||||
input incscore;
|
||||
output [3:0] lives;
|
||||
output reg [3:0] lives;
|
||||
input declives;
|
||||
|
||||
always @(posedge incscore or posedge reset)
|
||||
@ -45,8 +45,8 @@ module scoreboard_generator(score0, score1, lives, vpos, hpos, board_gfx);
|
||||
input [8:0] hpos;
|
||||
output board_gfx;
|
||||
|
||||
wire [3:0] score_digit;
|
||||
wire [4:0] score_bits;
|
||||
reg [3:0] score_digit;
|
||||
reg [4:0] score_bits;
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
|
@ -81,6 +81,7 @@ module sprite_bitmap_top(clk, reset, hsync, vsync, rgb);
|
||||
wire [3:0] car_bit = car_sprite_xofs>=8 ?
|
||||
15-car_sprite_xofs:
|
||||
car_sprite_xofs;
|
||||
|
||||
wire car_gfx = car_sprite_bits[car_bit[2:0]];
|
||||
|
||||
wire r = display_on && car_gfx;
|
||||
|
@ -63,7 +63,7 @@ module sprite_multiple_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
|
||||
sprite_renderer enemy_renderer(
|
||||
.clk(clk),
|
||||
.vstart(enemy_vstart),
|
||||
.load(hpos == 257),
|
||||
.load(hpos == 258),
|
||||
.hstart(enemy_hstart),
|
||||
.rom_addr(car_sprite_yofs),
|
||||
.rom_bits(car_sprite_bits),
|
||||
|
@ -7,20 +7,24 @@
|
||||
module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
|
||||
gfx, in_progress);
|
||||
|
||||
input clk, vstart, load, hstart;
|
||||
output [3:0] rom_addr;
|
||||
input [7:0] rom_bits;
|
||||
output gfx;
|
||||
output in_progress;
|
||||
input clk;
|
||||
input vstart; // start drawing (top border)
|
||||
input load; // ok to load sprite data?
|
||||
input hstart; // start drawing scanline (left border)
|
||||
output [3:0] rom_addr; // select ROM address
|
||||
input [7:0] rom_bits; // input bits from ROM
|
||||
output gfx; // output pixel
|
||||
output in_progress; // 0 if waiting for vstart
|
||||
|
||||
assign in_progress = state != WAIT_FOR_VSTART;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [3:0] ycount;
|
||||
reg [3:0] xcount;
|
||||
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;
|
||||
reg [7:0] outbits; // register to store bits from ROM
|
||||
|
||||
// states
|
||||
localparam WAIT_FOR_VSTART = 0;
|
||||
localparam WAIT_FOR_LOAD = 1;
|
||||
localparam LOAD1_SETUP = 2;
|
||||
@ -32,46 +36,49 @@ module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits,
|
||||
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;
|
||||
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;
|
||||
xcount <= 0; // initialize horiz. count
|
||||
gfx <= 0;
|
||||
if (load) state <= LOAD1_SETUP;
|
||||
// wait for load, then next state
|
||||
if (load)
|
||||
state <= LOAD1_SETUP;
|
||||
end
|
||||
LOAD1_SETUP: begin
|
||||
rom_addr <= ycount;
|
||||
gfx <= 0;
|
||||
rom_addr <= ycount; // load ROM address
|
||||
state <= LOAD1_FETCH;
|
||||
end
|
||||
LOAD1_FETCH: begin
|
||||
outbits[7:0] <= rom_bits;
|
||||
gfx <= 0;
|
||||
outbits <= rom_bits; // latch bits from ROM
|
||||
state <= WAIT_FOR_HSTART;
|
||||
end
|
||||
WAIT_FOR_HSTART: begin
|
||||
if (hstart) state <= DRAW;
|
||||
gfx <= 0;
|
||||
// wait for hstart, then start drawing
|
||||
if (hstart)
|
||||
state <= DRAW;
|
||||
end
|
||||
DRAW: begin
|
||||
// mirror graphics left/right
|
||||
// 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 <= 0; // TODO: reset
|
||||
gfx <= 0;
|
||||
state <= WAIT_FOR_VSTART;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
@ -105,33 +112,33 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
|
||||
.vpos(vpos)
|
||||
);
|
||||
|
||||
wire [3:0] car_sprite_yofs;
|
||||
wire [3:0] car_sprite_addr;
|
||||
wire [7:0] car_sprite_bits;
|
||||
|
||||
car_bitmap car(
|
||||
.yofs(car_sprite_yofs),
|
||||
.yofs(car_sprite_addr),
|
||||
.bits(car_sprite_bits));
|
||||
|
||||
wire vstart = {1'd0,player_y} == vpos;
|
||||
wire hstart = {1'd0,player_x} == hpos;
|
||||
wire car_gfx;
|
||||
wire unused;
|
||||
wire in_progress;
|
||||
|
||||
sprite_renderer renderer(
|
||||
.clk(clk),
|
||||
.vstart(vstart),
|
||||
.load(hsync),
|
||||
.hstart(hstart),
|
||||
.rom_addr(car_sprite_yofs),
|
||||
.rom_addr(car_sprite_addr),
|
||||
.rom_bits(car_sprite_bits),
|
||||
.gfx(car_gfx),
|
||||
.in_progress(unused));
|
||||
.in_progress(in_progress));
|
||||
|
||||
always @(posedge hsync)
|
||||
begin
|
||||
if (!hpaddle) paddle_x <= vpos[7:0];
|
||||
if (!vpaddle) paddle_y <= vpos[7:0];
|
||||
end
|
||||
always @(posedge hpaddle)
|
||||
paddle_x <= vpos[7:0];
|
||||
|
||||
always @(posedge vpaddle)
|
||||
paddle_y <= vpos[7:0];
|
||||
|
||||
always @(posedge vsync)
|
||||
begin
|
||||
@ -141,7 +148,7 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
|
||||
|
||||
wire r = display_on && car_gfx;
|
||||
wire g = display_on && car_gfx;
|
||||
wire b = display_on && car_gfx;
|
||||
wire b = display_on && in_progress;
|
||||
assign rgb = {b,g,r};
|
||||
|
||||
endmodule
|
||||
|
@ -8,7 +8,7 @@ module tank_bitmap(addr, bits);
|
||||
input [7:0] addr;
|
||||
output [7:0] bits;
|
||||
|
||||
reg [15:0] bitarray[256];
|
||||
reg [15:0] bitarray[0:255];
|
||||
|
||||
assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0];
|
||||
|
||||
@ -109,7 +109,9 @@ module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits,
|
||||
output [4:0] rom_addr;
|
||||
input [7:0] rom_bits;
|
||||
output gfx;
|
||||
output busy = state != WAIT_FOR_VSTART;
|
||||
output busy;
|
||||
|
||||
assign busy = state != WAIT_FOR_VSTART;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [3:0] ycount;
|
||||
@ -178,6 +180,7 @@ module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits,
|
||||
endmodule
|
||||
|
||||
module rotation_selector(rotation, bitmap_num, hmirror, vmirror);
|
||||
|
||||
input [3:0] rotation;
|
||||
output [2:0] bitmap_num;
|
||||
output hmirror, vmirror;
|
||||
@ -208,23 +211,6 @@ module rotation_selector(rotation, bitmap_num, hmirror, vmirror);
|
||||
|
||||
endmodule
|
||||
|
||||
function signed [7:0] sin_16x4;
|
||||
input [3:0] in;
|
||||
integer y;
|
||||
case (in[1:0])
|
||||
0: y = 0;
|
||||
1: y = 3;
|
||||
2: y = 5;
|
||||
3: y = 6;
|
||||
endcase
|
||||
case (in[3:2])
|
||||
0: sin_16x4 = 8'(y);
|
||||
1: sin_16x4 = 8'(7-y);
|
||||
2: sin_16x4 = 8'(-y);
|
||||
3: sin_16x4 = 8'(y-7);
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
||||
sprite_addr, sprite_bits, gfx,
|
||||
playfield,
|
||||
@ -305,9 +291,33 @@ module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
||||
end
|
||||
end
|
||||
|
||||
// set if collision; cleared at vsync
|
||||
reg collision_detected;
|
||||
always @(posedge collision_gfx or posedge vsync or posedge reset)
|
||||
|
||||
always @(posedge clk)
|
||||
if (collision_gfx)
|
||||
collision_detected <= collision_gfx;
|
||||
else if (vsync)
|
||||
collision_detected <= 0;
|
||||
|
||||
// sine lookup (4 bits input, 4 signed bits output)
|
||||
|
||||
function signed [3:0] sin_16x4;
|
||||
input [3:0] in;
|
||||
integer y;
|
||||
case (in[1:0])
|
||||
0: y = 0;
|
||||
1: y = 3;
|
||||
2: y = 5;
|
||||
3: y = 6;
|
||||
endcase
|
||||
case (in[3:2])
|
||||
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
|
||||
|
@ -180,13 +180,13 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
var self = this;
|
||||
var video, audio;
|
||||
var useAudio = false;
|
||||
var videoWidth = 256+16;
|
||||
var videoHeight = 240+16;
|
||||
var maxVideoBlankLines = 80;
|
||||
var videoWidth = 256+20;
|
||||
var videoHeight = 240+20;
|
||||
var maxVideoBlankLines = 40; // vertical hold
|
||||
var idata, timer;
|
||||
var gen;
|
||||
var frameRate = 60;
|
||||
var AUDIO_FREQ = (256+23+7+23)*262*60; // 4857480
|
||||
var AUDIO_FREQ = (256+23+7+23)*262*60; // 4857480 Hz
|
||||
var current_output;
|
||||
var paddle_x = 0;
|
||||
var paddle_y = 0;
|
||||
@ -235,6 +235,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
if (inspect_obj && inspect_sym) {
|
||||
var COLOR_BIT_OFF = 0xffff3333;
|
||||
var COLOR_BIT_ON = 0xffffffff;
|
||||
/*
|
||||
for (var y=0; y<videoHeight; y++) {
|
||||
var val = inspect_data[y * videoWidth];
|
||||
var i = y * videoWidth + 16;
|
||||
@ -244,6 +245,14 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
val >>= 1;
|
||||
} while (val != 0);
|
||||
}
|
||||
*/
|
||||
var i = 0;
|
||||
for (var y=0; y<videoHeight; y++) {
|
||||
for (var x=0; x<videoWidth; x++) {
|
||||
var val = inspect_data[i];
|
||||
idata[i++] = (val & 1) ? COLOR_BIT_ON : COLOR_BIT_OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,7 +450,6 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
});
|
||||
audio = new SampleAudio(AUDIO_FREQ);
|
||||
idata = video.getFrameData();
|
||||
// TODO: 15.7 kHz?
|
||||
timer = new AnimationTimer(frameRate, function() {
|
||||
if (!self.isRunning())
|
||||
return;
|
||||
|
@ -534,7 +534,7 @@ function setCompileOutput(data) {
|
||||
editor.setGutterMarker(info.line-1, "gutter-offset", textel);
|
||||
}
|
||||
if (info.insns) {
|
||||
var insnstr = info.insns.length > 8 ? ("...") : info.insns;
|
||||
var insnstr = info.insns.length > 9 ? ("...") : info.insns;
|
||||
var textel = document.createTextNode(insnstr);
|
||||
editor.setGutterMarker(info.line-1, "gutter-bytes", textel);
|
||||
if (info.iscode) {
|
||||
|
@ -70,16 +70,18 @@ var Assembler = function(spec) {
|
||||
}
|
||||
|
||||
function preprocessRules() {
|
||||
if (spec.width)
|
||||
width = spec.width|0;
|
||||
for (var i=0; i<spec.rules.length; i++)
|
||||
rule2regex(spec.rules[i], spec.vars);
|
||||
}
|
||||
if (spec) preprocessRules();
|
||||
|
||||
function warning(msg) {
|
||||
errors.push({msg:msg, line:linenum});
|
||||
function warning(msg, line) {
|
||||
errors.push({msg:msg, line:line?line:linenum});
|
||||
}
|
||||
function fatal(msg) {
|
||||
warning(msg);
|
||||
function fatal(msg, line) {
|
||||
warning(msg, line);
|
||||
aborted = true;
|
||||
}
|
||||
function hex(v, nd) {
|
||||
@ -106,7 +108,8 @@ var Assembler = function(spec) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseConst(s) {
|
||||
function parseConst(s, nbits) {
|
||||
// TODO: check bit length
|
||||
if (!s.length)
|
||||
return s;
|
||||
else if (s.startsWith("$"))
|
||||
@ -128,8 +131,7 @@ var Assembler = function(spec) {
|
||||
var id = m[b+1];
|
||||
var v = spec.vars[rule.varlist[b]];
|
||||
if (!v) {
|
||||
warning("Could not find matching identifier for '" + m[0] + "'");
|
||||
return;
|
||||
return {error:"Could not find matching identifier for '" + m[0] + "'"};
|
||||
}
|
||||
n = v.bits;
|
||||
if (v.toks) {
|
||||
@ -137,7 +139,7 @@ var Assembler = function(spec) {
|
||||
if (x < 0)
|
||||
return null;
|
||||
} else {
|
||||
x = parseConst(id);
|
||||
x = parseConst(id, n);
|
||||
// is it a label? add fixup
|
||||
if (isNaN(x)) {
|
||||
fixups.push({sym:id, ofs:ip, bitlen:n, bitofs:oplen, line:linenum, iprel:!!v.iprel, ipofs:(v.ipofs+0)});
|
||||
@ -147,7 +149,7 @@ var Assembler = function(spec) {
|
||||
}
|
||||
var mask = (1<<n)-1;
|
||||
if ((x&mask) != x)
|
||||
warning("Value " + x + " could not fit in " + n + " bits");
|
||||
return {error:"Value " + x + " does not fit in " + n + " bits"};
|
||||
opcode = (opcode << n) | x;
|
||||
oplen += n;
|
||||
}
|
||||
@ -206,18 +208,21 @@ var Assembler = function(spec) {
|
||||
return; // empty line
|
||||
// look at each rule in order
|
||||
if (!spec) { fatal("Need to load .spec first"); return; }
|
||||
var lastError;
|
||||
for (var i=0; i<spec.rules.length; i++) {
|
||||
var rule = spec.rules[i];
|
||||
var m = rule.re.exec(line);
|
||||
if (m) {
|
||||
var result = self.buildInstruction(rule, m);
|
||||
if (result) {
|
||||
if (result && result.nbits) {
|
||||
addBytes(result);
|
||||
return result;
|
||||
} else if (result && result.error) {
|
||||
lastError = result.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
warning("Could not decode instruction: " + line);
|
||||
warning(lastError ? lastError : ("Could not decode instruction: " + line));
|
||||
}
|
||||
|
||||
self.finish = function() {
|
||||
@ -226,15 +231,18 @@ var Assembler = function(spec) {
|
||||
var fix = fixups[i];
|
||||
var sym = symbols[fix.sym];
|
||||
if (sym) {
|
||||
var ofs = fix.ofs + (fix.bitofs>>3);
|
||||
var shift = fix.bitofs&7;
|
||||
var ofs = fix.ofs + Math.floor(fix.bitofs/width);
|
||||
var shift = fix.bitofs & (width-1);
|
||||
var mask = ((1<<fix.bitlen)-1);
|
||||
var value = parseConst(sym.value);
|
||||
if (fix.iprel) value -= fix.ofs + fix.ipofs;
|
||||
var value = parseConst(sym.value, fix.bitlen);
|
||||
if (fix.iprel)
|
||||
value -= fix.ofs + fix.ipofs;
|
||||
if (value > mask || value < -mask)
|
||||
warning("Symbol " + fix.sym + " (" + value + ") does not fit in " + fix.bitlen + " bits", fix.line);
|
||||
value &= mask;
|
||||
// TODO: check range
|
||||
// TODO: span multiple words?
|
||||
outwords[ofs - origin] ^= value;
|
||||
outwords[ofs - origin] ^= value; // TODO: << shift?
|
||||
} else {
|
||||
warning("Symbol '" + fix.sym + "' not found");
|
||||
}
|
||||
|
@ -23,12 +23,12 @@ describe('Assemble', function() {
|
||||
.define F_VSYNC 16
|
||||
|
||||
Start:
|
||||
zero A
|
||||
zero A ; comment
|
||||
sta VPU_LO
|
||||
sta VPU_HI
|
||||
sta 0
|
||||
DisplayLoop:
|
||||
zero B
|
||||
|
||||
DisplayLoop:zero B
|
||||
mov A,[b]
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
|
Loading…
x
Reference in New Issue
Block a user