diff --git a/presets/verilog/cpu8.v b/presets/verilog/cpu8.v index 95954e48..9dea81f2 100644 --- a/presets/verilog/cpu8.v +++ b/presets/verilog/cpu8.v @@ -6,7 +6,7 @@ `define OP_XOR 4'h4 `define OP_INC 4'h5 `define OP_DEC 4'h6 -`define OP_NOP 4'h7 +`define OP_ZERO 4'h7 // operations that generate carry `define OP_ADD 4'h8 `define OP_SUB 4'h9 @@ -24,35 +24,57 @@ module ALU( case (aluop) `OP_LOAD_A: Y = {1'b0, A}; `OP_LOAD_B: Y = {1'b0, B}; - `OP_ADD: Y = A + B; - `OP_SUB: Y = A - B; - `OP_INC: Y = A + 1; - `OP_DEC: Y = A - 1; - `OP_ASL: Y = A + A; - `OP_LSR: Y = {A[0], A >> 1}; `OP_OR: Y = {1'b0, A | B}; `OP_AND: Y = {1'b0, A & B}; `OP_XOR: Y = {1'b0, A ^ B}; + `OP_INC: Y = A + 1; + `OP_DEC: Y = A - 1; + `OP_ZERO: Y = 0; + + `OP_ADD: Y = A + B; + `OP_SUB: Y = A - B; + `OP_ASL: Y = A + A; + `OP_LSR: Y = {A[0], A >> 1}; default: Y = 9'bx; 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, 2'(dest), 4'(op) } `define I_COMPUTE_IMM(dest,op) { 2'b01, 2'(dest), 4'(op) } `define I_COMPUTE_READB(dest,op) { 2'b11, 2'(dest), 4'(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_TO_B { 8'b10000000 } -`define I_CONST_SHORT_A(addr) { 4'b01010, 4'(addr) } -`define I_CONST_SHORT_B(addr) { 4'b01011, 4'(addr) } -`define I_BRANCH_IF_CARRY(carry) { 6'b100100, 1'(carry), 1'b1 } -`define I_RESET { 8'b10000001 } +`define I_STORE_A(addr) { 4'b1001, 4'(addr) } +`define I_BRANCH_IF_CARRY(carry) { 4'b1010, 2'b00, 1'(carry), 1'b1 } +`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) module CPU( input clk, @@ -75,7 +97,7 @@ module CPU( reg [7:0] opcode; wire [3:0] aluop = opcode[3:0]; wire [1:0] opdest = opcode[5:4]; - wire memalu = opcode[6]; + wire B_or_data = opcode[6]; localparam S_RESET = 0; localparam S_SELECT = 1; @@ -83,7 +105,7 @@ module CPU( localparam S_COMPUTE = 3; localparam S_READ_IP = 4; - ALU alu(.A(A), .B(memalu?data_in:B), .Y(Y), .aluop(aluop)); + ALU alu(.A(A), .B(B_or_data?data_in:B), .Y(Y), .aluop(aluop)); always @(posedge clk) if (reset) begin @@ -118,20 +140,26 @@ module CPU( IP <= IP + 1; state <= S_COMPUTE; end - // ALU A + [B] -> dest + // ALU A + read [B] -> dest 8'b11??????: begin address <= B; state <= S_COMPUTE; end - // A -> write [B] - 8'b10000000: begin - address <= B; + // A -> write [aluop] + 8'b1001????: begin + address <= {4'b0, aluop}; 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'b1001????: begin + 8'b1010????: begin if ( (data_in[0] && (data_in[1] == carry)) || (data_in[2] && (data_in[3] == zero))) @@ -143,16 +171,6 @@ module CPU( end IP <= IP + 1; // skip immediate end - // aluop -> A - 8'b1010????: begin - A <= {4'b0, data_in[3:0]}; - state <= S_SELECT; - end - // aluop -> B - 8'b1011????: begin - B <= {4'b0, data_in[3:0]}; - state <= S_SELECT; - end // fall-through RESET default: begin state <= S_RESET; // reset @@ -197,8 +215,8 @@ module test_CPU_top( output [7:0] B ); - reg [7:0] ram[127:0]; - reg [7:0] rom[127:0]; + reg [7:0] ram[0:127]; + reg [7:0] rom[0:127]; assign IP = cpu.IP; assign A = cpu.A; @@ -221,17 +239,18 @@ module test_CPU_top( to_cpu = rom[address_bus[6:0]]; initial begin - // ROM starts at address 0x80 - rom['h00] = `I_CONST_IMM_A; - rom['h01] = 1; - rom['h02] = `I_CONST_SHORT_B(0); - rom['h03] = `I_COMPUTE(`DEST_A, `OP_ADD); - rom['h04] = `I_COMPUTE(`DEST_B, `OP_ADD); - rom['h05] = `I_STORE_A_TO_B; - //rom['h06] = `I_JUMP_IMM; - rom['h06] = `I_BRANCH_IF_CARRY(0); - rom['h07] = 3 + 'h80; // correct for ROM offset - rom['h08] = `I_RESET; + rom = '{ + `I_ZERO_A, + `I_CONST_IMM_B, + 1, + `I_COMPUTE(`DEST_A, `OP_ADD), // addr 4 + `I_SWAP_AB, + `I_BRANCH_IF_CARRY(0), + 3 + 'h80, // correct for ROM offset + `I_RESET, + // leftover elements + 120{0} + }; end endmodule diff --git a/presets/verilog/simplecpu.v b/presets/verilog/simplecpu.v index e8c6118f..2d2a9469 100644 --- a/presets/verilog/simplecpu.v +++ b/presets/verilog/simplecpu.v @@ -1,15 +1,17 @@ `define OP_LOAD_A 4'h0 `define OP_LOAD_B 4'h1 -`define OP_ADD 4'h2 -`define OP_SUB 4'h3 -`define OP_INC 4'h4 -`define OP_DEC 4'h5 -`define OP_ASL 4'h6 -`define OP_LSR 4'h7 -`define OP_OR 4'h8 -`define OP_AND 4'h9 -`define OP_XOR 4'ha +`define OP_OR 4'h2 +`define OP_AND 4'h3 +`define OP_XOR 4'h4 +`define OP_INC 4'h5 +`define OP_DEC 4'h6 +`define OP_NOP 4'h7 +// operations that generate carry +`define OP_ADD 4'h8 +`define OP_SUB 4'h9 +`define OP_ASL 4'ha +`define OP_LSR 4'hb module ALU( input [7:0] A, @@ -22,27 +24,51 @@ module ALU( case (aluop) `OP_LOAD_A: Y = {1'b0, A}; `OP_LOAD_B: Y = {1'b0, B}; - `OP_ADD: Y = A + B; - `OP_SUB: Y = A - B; - `OP_INC: Y = A + 1; - `OP_DEC: Y = A - 1; - `OP_ASL: Y = {A[7], A + A}; - `OP_LSR: Y = {A[0], A >> 1}; `OP_OR: Y = {1'b0, A | B}; `OP_AND: Y = {1'b0, A & B}; `OP_XOR: Y = {1'b0, A ^ B}; + `OP_INC: Y = A + 1; + `OP_DEC: Y = A - 1; + + `OP_ADD: Y = A + B; + `OP_SUB: Y = A - B; + `OP_ASL: Y = A + A; + `OP_LSR: Y = {A[0], A >> 1}; default: Y = 9'bx; endcase endmodule -`define REG_A 1'b0 -`define REG_B 1'b1 -`define I_CONST(r,x) { 2'b00, r, x } -`define I_LOAD_ADDR(r,addr) { 3'b010, r, addr } -`define I_STORE_ADDR(r,addr) { 3'b011, r, addr } -`define I_COMPUTE(r,op) { 3'b100, r, op } -`define I_RESET 8'hff +/* +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 (replaces + operator) +nnnn = 4-bit constant +tttt = flags test for conditional branch +*/ + +`define DEST_A 2'b00 +`define DEST_B 2'b01 +`define DEST_IP 2'b10 +`define DEST_NOP 2'b11 +`define I_COMPUTE(dest,op) { 2'b00, 2'(dest), 4'(op) } +`define I_COMPUTE_IMM(dest,op) { 2'b01, 2'(dest), 4'(op) } +`define I_COMPUTE_READB(dest,op) { 2'b11, 2'(dest), 4'(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, 4'(addr) } +`define I_BRANCH_IF_CARRY(carry) { 4'b1010, 2'b00, 1'(carry), 1'b1 } +`define I_SWAP_AB { 8'b10000001 } +`define I_RESET { 8'b10111111 } module CPU( input clk, @@ -53,9 +79,7 @@ module CPU( output write ); - reg [7:0] ip; - reg [7:0] opcode; - reg [3:0] aluop; + reg [7:0] IP; reg [7:0] A, B; reg [8:0] Y; reg [2:0] state; @@ -63,17 +87,19 @@ module CPU( 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 memalu = opcode[6]; + localparam S_RESET = 0; localparam S_SELECT = 1; localparam S_DECODE = 2; - localparam S_LOAD_ADDR = 3; - localparam S_STORE_ADDR = 4; - localparam S_COMPUTE = 5; + localparam S_COMPUTE = 3; + localparam S_READ_IP = 4; - wire load_const = opcode[3:1] == 3'b111; - - ALU alu(.A(A), .B(B), .Y(Y), .aluop(aluop)); + ALU alu(.A(A), .B(memalu?data_in:B), .Y(Y), .aluop(aluop)); always @(posedge clk) if (reset) begin @@ -83,75 +109,87 @@ module CPU( case (state) // state 0: reset S_RESET: begin - ip <= 8'h80; + IP <= 8'h80; write <= 0; state <= S_SELECT; end // state 1: select opcode address S_SELECT: begin - address <= ip; - ip <= ip + 1; + address <= IP; + IP <= IP + 1; write <= 0; state <= S_DECODE; end // state 2: read/decode opcode S_DECODE: begin + opcode <= data_in; casez (data_in) - 8'b00??????: begin // load constant - if (data_in[5]) - B <= {3'b0, data_in[4:0]}; - else - A <= {3'b0, data_in[4:0]}; - state <= S_SELECT; - end - 8'b010?????: begin // read memory - address <= {4'b0, data_in[3:0]}; - state <= S_LOAD_ADDR; - end - 8'b011?????: begin // write memory - address <= {4'b0, data_in[3:0]}; - state <= S_STORE_ADDR; - end - 8'b100?????: begin // compute w/ ALU - aluop <= data_in[3:0]; + // ALU A + B -> dest + 8'b00??????: begin state <= S_COMPUTE; end - 8'b101000??: begin // branch if flag set/clear - if ((data_in[0] ? carry : zero) ^ data_in[1]) - ip <= A; + // 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 [aluop] + 8'b1001????: begin + address <= {4'b0, aluop}; + 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 - opcode <= data_in; end - // state 3: load address - S_LOAD_ADDR: begin - if (opcode[4]) - B <= data_in; - else - A <= data_in; - state <= S_SELECT; - end - // state 4: store address - S_STORE_ADDR: begin - if (opcode[4]) - data_out <= B; - else - data_out <= A; - write <= 1; - state <= S_SELECT; - end - // state 5: compute ALU op and flags + // state 3: compute ALU op and flags S_COMPUTE: begin - if (opcode[4]) - B <= Y[7:0]; - else - A <= Y[7:0]; - carry <= Y[8]; + // 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 (code >= 8) + if (aluop[3]) carry <= Y[8]; + // set zero flag zero <= ~|Y; + // 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 @@ -165,11 +203,18 @@ module test_CPU_top( output [7:0] address_bus, output reg [7:0] to_cpu, output [7:0] from_cpu, - output write_enable + output write_enable, + output [7:0] IP, + output [7:0] A, + output [7:0] B ); - reg [7:0] ram[127:0]; - reg [7:0] rom[127:0]; + reg [7:0] ram[0:127]; + reg [7:0] rom[0:127]; + + assign IP = cpu.IP; + assign A = cpu.A; + assign B = cpu.B; CPU cpu(.clk(clk), .reset(reset), @@ -188,16 +233,19 @@ module test_CPU_top( to_cpu = rom[address_bus[6:0]]; initial begin - // address 0x80 - rom['h00] = `I_CONST(`REG_A, 5'h1f); - rom['h01] = `I_COMPUTE(`REG_A, `OP_ASL); - rom['h02] = `I_COMPUTE(`REG_A, `OP_ASL); - rom['h03] = `I_COMPUTE(`REG_A, `OP_ASL); - rom['h04] = `I_COMPUTE(`REG_A, `OP_ASL); - rom['h05] = `I_COMPUTE(`REG_B, `OP_LOAD_A); - rom['h06] = `I_STORE_ADDR(`REG_B, 4'd1); - rom['h07] = `I_LOAD_ADDR(`REG_B, 4'd1); - rom['h08] = `I_RESET; + rom = '{ + `I_CONST_IMM_A, + 0, + `I_CONST_IMM_B, + 1, + `I_COMPUTE(`DEST_A, `OP_ADD), // addr 4 + `I_SWAP_AB, + `I_BRANCH_IF_CARRY(0), + 4 + 'h80, // correct for ROM offset + `I_RESET, + // leftover elements + 119{0} + }; end endmodule diff --git a/presets/verilog/sprite_renderer.v b/presets/verilog/sprite_renderer.v index 3c20bd97..fabf9978 100644 --- a/presets/verilog/sprite_renderer.v +++ b/presets/verilog/sprite_renderer.v @@ -10,80 +10,99 @@ module car_bitmap(yofs, bits); assign bits = bitarray[yofs]; initial begin/*{w:8,h:16}*/ - bitarray[0] = 8'b110000; - bitarray[1] = 8'b1110111; - bitarray[2] = 8'b11100110; - bitarray[3] = 8'b11111111; - bitarray[4] = 8'b11100110; - bitarray[5] = 8'b1100111; - bitarray[6] = 8'b110000; + bitarray[0] = 8'b1100; + bitarray[1] = 8'b11001100; + bitarray[2] = 8'b11111100; + bitarray[3] = 8'b11101100; + bitarray[4] = 8'b11100000; + bitarray[5] = 8'b1100000; + bitarray[6] = 8'b1110000; bitarray[7] = 8'b110000; bitarray[8] = 8'b110000; - bitarray[9] = 8'b1110000; - bitarray[10] = 8'b1100000; - bitarray[11] = 8'b11100000; - bitarray[12] = 8'b11101100; - bitarray[13] = 8'b11111100; - bitarray[14] = 8'b11001100; - bitarray[15] = 8'b1100; + bitarray[9] = 8'b110000; + bitarray[10] = 8'b1100111; + bitarray[11] = 8'b11100110; + bitarray[12] = 8'b11111111; + bitarray[13] = 8'b11100110; + bitarray[14] = 8'b1110111; + bitarray[15] = 8'b110000; end + endmodule -module sprite_renderer(clk, vstart, load, hstart, rom_yofs, rom_bits, +module sprite_renderer(clk, vstart, load, hstart, rom_addr, rom_bits, gfx, in_progress); input clk, vstart, load, hstart; - output [3:0] rom_yofs; + output [3:0] rom_addr; input [7:0] rom_bits; output gfx; - output in_progress = yactive; - + output in_progress = state != WAIT_FOR_VSTART; + + reg [2:0] state; reg [3:0] ycount; reg [3:0] xcount; - reg yactive; - reg xactive; - reg loading; reg [7:0] outbits; + 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; + always @(posedge clk) begin - // set a default value (blank) for pixel output - // note: multiple non-blocking assignments are vendor-specific - gfx <= 0; - // load next line? set ROM address - if (yactive && load) begin - rom_yofs <= ~ycount; - loading <= 1; - // ROM address was set, now latch bits from bus - end else if (loading) begin - outbits <= rom_bits; - loading <= 0; - ycount <= ycount + 1; - // start sprite at this vertical scanline - end else if (vstart) begin - yactive <= 1; - //ycount <= 0; - // start sprite at this horizontal clock - end else if (hstart && yactive) begin - xactive <= 1; - //xcount <= 0; - // both X & Y active, set pixel output - end else if (xactive && yactive) - 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 <= ycount; + gfx <= 0; + state <= LOAD1_FETCH; + end + LOAD1_FETCH: begin + outbits[7:0] <= rom_bits; + gfx <= 0; + state <= WAIT_FOR_HSTART; + end + WAIT_FOR_HSTART: begin + if (hstart) state <= DRAW; + gfx <= 0; + end + DRAW: begin // mirror graphics left/right - gfx <= outbits[xcount[3]?~xcount[2:0]:xcount[2:0]]; + gfx <= outbits[xcount<8 ? xcount[2:0] : ~xcount[2:0]]; xcount <= xcount + 1; if (xcount == 15) begin // pre-increment value - xactive <= 0; // done drawing this scanline - if (ycount == 0) // post-increment value - yactive <= 0; // done drawing sprite + 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 + default: begin + state <= 0; // TODO: reset + gfx <= 0; + end + endcase end endmodule + module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle); input clk; @@ -127,7 +146,7 @@ module test_top(clk, hsync, vsync, rgb, hpaddle, vpaddle); .vstart(vstart), .load(hsync), .hstart(hstart), - .rom_yofs(car_sprite_yofs), + .rom_addr(car_sprite_yofs), .rom_bits(car_sprite_bits), .gfx(car_gfx), .in_progress(unused)); diff --git a/src/pixed/pixeleditor.js b/src/pixed/pixeleditor.js index 81d6954a..4aa8999b 100644 --- a/src/pixed/pixeleditor.js +++ b/src/pixed/pixeleditor.js @@ -159,6 +159,15 @@ function PixelEditor(parentDiv, fmt, palette, initialData, thumbnails) { }); } + function setPixels(p) { + var i = 0; + for (var y=0; y 0) { ctx.textAlign = 'right'; var value = arr.length * scope_time_x + i + jstart; - ctx.fillText(""+trace_buffer[value], videoWidth-1, yposlist[i]); + shadowText(ctx, ""+trace_buffer[value], videoWidth-1, yposlist[i]); } } // draw scope line & label if (scope_time_x > 0) { ctx.fillStyle = "cyan"; - ctx.fillText(""+(scope_time_x+scope_x_offset), + shadowText(ctx, ""+(scope_time_x+scope_x_offset), (scope_time_x>10)?(scope_time_x-2):(scope_time_x+20), videoHeight-2); ctx.fillRect(scope_time_x, 0, 1, 4000); }