diff --git a/presets/verilog/cargame.asm b/presets/verilog/cargame.asm new file mode 100644 index 00000000..590665ca --- /dev/null +++ b/presets/verilog/cargame.asm @@ -0,0 +1,87 @@ +.arch nano8 +.org 128 + +.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 + sta PLAYER_X + sta ENEMY_X + sta ENEMY_Y + lda 180 + sta PLAYER_Y + zero A + sta SPEED +; test hpaddle flag +DisplayLoop: + lda F_HPADDLE + ldb IN_FLAGS + andrb NOP + bz DisplayLoop +; [vpos] -> paddle_x + ldb IN_VPOS + movrb A + sta PLAYER_X +; wait for vsync=1 then vsync=0 + lda F_VSYNC + ldb IN_FLAGS +WaitForVsyncOn: + andrb NOP + bz WaitForVsyncOn +WaitForVsyncOff: + andrb NOP + bnz WaitForVsyncOff +; check collision + lda F_COLLIDE + ldb IN_FLAGS + andrb NOP + bz NoCollision +; load slow speed + lda 16 + sta SPEED +NoCollision: +; update speed + ldb SPEED + movrb A + inc A +; don't store if == 0 + bz MaxSpeed + sta SPEED +MaxSpeed: + movrb A + lsr A + lsr A + lsr A + lsr A +; add to lo byte of track pos + ldb TRACKPOS_LO + addrb B + swapab + sta TRACKPOS_LO + swapab +; update enemy vert pos + ldb ENEMY_Y + addrb A + sta ENEMY_Y + jmp DisplayLoop + reset diff --git a/presets/verilog/music.v b/presets/verilog/music.v new file mode 100644 index 00000000..ff9f05ff --- /dev/null +++ b/presets/verilog/music.v @@ -0,0 +1,163 @@ +`include "hvsync_generator.v" + +module sound_psg(clk, reset, out, reg_sel, reg_data, reg_write); + + input clk, reset; + input [3:0] reg_sel; + input [7:0] reg_data; + input reg_write; + output [7:0] out; + + parameter NVOICES = 4; + + reg outputs[NVOICES]; + reg [17:0] count[NVOICES]; + reg [7:0] register[16]; + + always @(posedge clk) begin + out = 0; + for (int i=0; i dest +mova 2 { 00 (0) 0000 } +movb 2 { 00 (0) 0001 } +inc 2 { 00 (0) 0010 } +dec 2 { 00 (0) 0011 } +or 2 { 00 (0) 0100 } +and 2 { 00 (0) 0101 } +xor 2 { 00 (0) 0110 } +zero 2 { 00 (0) 0111 } +add 2 { 00 (0) 1000 } +sub 2 { 00 (0) 1001 } +asl 2 { 00 (0) 1010 } +lsr 2 { 00 (0) 1011 } +adc 2 { 00 (0) 1100 } +sbb 2 { 00 (0) 1101 } +rol 2 { 00 (0) 1110 } +ror 2 { 00 (0) 1111 } + +; ALU A + immediate -> dest +movi 2 8 { 01 (0) 0001 (1) } +ori 2 8 { 01 (0) 0100 (1) } +andi 2 8 { 01 (0) 0101 (1) } +xori 2 8 { 01 (0) 0110 (1) } +addi 2 8 { 01 (0) 1000 (1) } +subi 2 8 { 01 (0) 1001 (1) } +asli 2 8 { 01 (0) 1010 (1) } +lsri 2 8 { 01 (0) 1011 (1) } +adci 2 8 { 01 (0) 1100 (1) } +sbbi 2 8 { 01 (0) 1101 (1) } +roli 2 8 { 01 (0) 1110 (1) } +rori 2 8 { 01 (0) 1111 (1) } + +; ALU A + read [B] -> dest +movrb 2 { 11 (0) 0001 } +orrb 2 { 11 (0) 0100 } +andrb 2 { 11 (0) 0101 } +xorrb 2 { 11 (0) 0110 } +addrb 2 { 11 (0) 1000 } +subrb 2 { 11 (0) 1001 } +aslrb 2 { 11 (0) 1010 } +lsrrb 2 { 11 (0) 1011 } +adcrb 2 { 11 (0) 1100 } +sbbrb 2 { 11 (0) 1101 } +rolrb 2 { 11 (0) 1110 } +rorrb 2 { 11 (0) 1111 } + +; A -> write [nnnn] +sta 4 { 1001 (0) } + +; other insns +clc { 10001000 } +swapab { 10000001 } +reset { 10001111 } + +lda 8 { 01 00 0001 (0) } +ldb 8 { 01 01 0001 (0) } +jmp 8 { 01 10 0001 (0) } + +; conditional branch +bcc 8 { 1010 0001 (0) } +bcs 8 { 1010 0011 (0) } +bz 8 { 1010 0100 (0) } +bnz 8 { 1010 1100 (0) } + + +; allow raw byte positioning +byte 8 { (0) } ; One byte constant diff --git a/presets/verilog/race_game_cpu.v b/presets/verilog/race_game_cpu.v new file mode 100644 index 00000000..a1aff18f --- /dev/null +++ b/presets/verilog/race_game_cpu.v @@ -0,0 +1,265 @@ +`include "hvsync_generator.v" +`include "sprite_renderer.v" +`include "cpu8.v" + +// uncomment to see scope view +//`define DEBUG + +module sprite_multiple_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 + , output [2:0] rgb +`endif +); + + input clk, reset; + input hpaddle, vpaddle; + output hsync, vsync; + 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; +`endif + + parameter PADDLE_X = 0; + parameter PADDLE_Y = 1; + parameter PLAYER_X = 2; + parameter PLAYER_Y = 3; + parameter ENEMY_X = 4; + parameter ENEMY_Y = 5; + parameter ENEMY_DIR = 6; + parameter SPEED = 7; + parameter TRACKPOS_LO = 8; + parameter TRACKPOS_HI = 9; + + parameter IN_HPOS = 8'b01000000; + parameter IN_VPOS = 8'b01000001; + parameter IN_FLAGS = 8'b01000010; + + reg [7:0] ram[0:63]; + reg [7:0] rom[0:127]; + + output wire [7:0] address_bus; + output reg [7:0] to_cpu; + output wire [7:0] from_cpu; + output wire write_enable; + + CPU cpu(.clk(clk), + .reset(reset), + .address(address_bus), + .data_in(to_cpu), + .data_out(from_cpu), + .write(write_enable)); + + always @(posedge clk) + if (write_enable) + ram[address_bus[5:0]] <= 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: ; + endcase + + hvsync_generator hvsync_gen( + .clk(clk), + .reset(0), + .hsync(hsync), + .vsync(vsync), + .display_on(display_on), + .hpos(hpos), + .vpos(vpos) + ); + + wire player_vstart = {1'0,ram[PLAYER_Y]} == vpos; + wire player_hstart = {1'0,ram[PLAYER_X]} == hpos; + wire player_gfx; + wire player_is_drawing; + + wire enemy_vstart = {1'0,ram[ENEMY_Y]} == vpos; + wire enemy_hstart = {1'0,ram[ENEMY_X]} == hpos; + wire enemy_gfx; + wire enemy_is_drawing; + + wire [3:0] car_sprite_yofs; + wire [7:0] car_sprite_bits; + + car_bitmap car( + .yofs(car_sprite_yofs), + .bits(car_sprite_bits)); + + sprite_renderer player_renderer( + .clk(clk), + .vstart(player_vstart), + .hstart(player_hstart), + .load(hpos == 256), //TODO? + .rom_addr(car_sprite_yofs), + .rom_bits(car_sprite_bits), + .gfx(player_gfx), + .in_progress(player_is_drawing)); + + sprite_renderer enemy_renderer( + .clk(clk), + .vstart(enemy_vstart), + .hstart(enemy_hstart), + .load(hpos == 260), //TODO? + .rom_addr(car_sprite_yofs), + .rom_bits(car_sprite_bits), + .gfx(enemy_gfx), + .in_progress(player_is_drawing)); + + /* + always @(posedge hsync) + begin + if (!hpaddle) ram[PADDLE_X] <= vpos[7:0]; + if (!vpaddle) ram[PADDLE_Y] <= vpos[7:0]; + end + + wire enemy_hit_left = (enemy_x == 64); + wire enemy_hit_right = (enemy_x == 192); + wire enemy_hit_edge = enemy_hit_left || enemy_hit_right; + + always @(posedge vsync) + begin + player_x <= 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 + speed <= speed - 1; + end + */ + + initial begin + rom = '{ + // initialize registers + `I_CONST_IMM_A, + 128, + `I_STORE_A(PLAYER_X), + `I_STORE_A(ENEMY_X), + `I_STORE_A(ENEMY_Y), + `I_CONST_IMM_A, + 180, + `I_STORE_A(PLAYER_Y), + `I_ZERO_A, + `I_STORE_A(SPEED), + // test hpaddle flag + `I_CONST_IMM_A, + 8'b00000010, + `I_CONST_IMM_B, + IN_FLAGS, + `I_COMPUTE_READB(DEST_NOP, OP_AND), + `I_BRANCH_IF_ZERO(1), + 128+10, + // [vpos] -> paddle_x + `I_CONST_IMM_B, + IN_VPOS, + `I_COMPUTE_READB(DEST_A, OP_LOAD_B), + `I_STORE_A(PLAYER_X), + // wait for vsync=1 then vsync=0 + `I_CONST_IMM_A, + 8'b00010000, + `I_CONST_IMM_B, + IN_FLAGS, + `I_COMPUTE_READB(DEST_NOP, OP_AND), + `I_BRANCH_IF_ZERO(1), + 128+25, + `I_COMPUTE_READB(DEST_NOP, OP_AND), + `I_BRANCH_IF_ZERO(0), + 128+28, + // check collision + `I_CONST_IMM_A, + 8'b00100000, + `I_CONST_IMM_B, + IN_FLAGS, + `I_COMPUTE_READB(DEST_NOP, OP_AND), + `I_BRANCH_IF_ZERO(1), + 128+41, + // load slow speed + `I_CONST_IMM_A, + 16, + `I_STORE_A(SPEED), + // update speed + `I_CONST_IMM_B, + SPEED, + `I_COMPUTE_READB(DEST_A, OP_LOAD_B), + `I_COMPUTE(DEST_A, OP_INC), + // don't store if == 0 + `I_BRANCH_IF_ZERO(1), + 128+48, + `I_STORE_A(SPEED), + // branch target + `I_COMPUTE_READB(DEST_A, OP_LOAD_B), + `I_COMPUTE(DEST_A, OP_LSR), + `I_COMPUTE(DEST_A, OP_LSR), + `I_COMPUTE(DEST_A, OP_LSR), + `I_COMPUTE(DEST_A, OP_LSR), + // add to lo byte of track pos + `I_CONST_IMM_B, + TRACKPOS_LO, + `I_COMPUTE_READB(DEST_B, OP_ADD), + `I_SWAP_AB, + `I_STORE_A(TRACKPOS_LO), + `I_SWAP_AB, + // update enemy vert pos + `I_CONST_IMM_B, + ENEMY_Y, + `I_COMPUTE_READB(DEST_A, OP_ADD), + `I_STORE_A(ENEMY_Y), + // repeat main loop + `I_JUMP_IMM, + 128+10, + // leftover elements + 63{0} + }; + end + + reg frame_collision; + + always @(posedge clk) + if (player_gfx && (enemy_gfx || track_gfx)) + frame_collision <= 1; + else if (vpos==0) + frame_collision <= 0; + + 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; + + 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 rgb = {b,g,r}; + +endmodule diff --git a/src/audio.js b/src/audio.js index 3a850dc7..9f2bb60d 100644 --- a/src/audio.js +++ b/src/audio.js @@ -311,11 +311,11 @@ var SampleAudio = function(clockfreq) { var self = this; var sfrac, sinc, accum; var buffer, bufpos, bufferlist; + var idrain, ifill; function mix(ape) { var buflen=ape.outputBuffer.length; var lbuf = ape.outputBuffer.getChannelData(0); - //var rbuf = ape.outputBuffer.getChannelData(1); var m = this.module; if (!m) m = ape.srcElement.module; if (!m) return; @@ -323,10 +323,12 @@ var SampleAudio = function(clockfreq) { m.callback(lbuf); return; } else { - var buf = bufferlist[1]; + var buf = bufferlist[idrain]; for (var i=0; i= buffer.length) { bufpos = 0; - bufferlist[0] = bufferlist[1]; - bufferlist[1] = buffer; + bufferlist[ifill] = buffer; + var inext = (ifill + 1) % bufferlist.length; + if (inext != idrain) ifill = inext; + buffer = bufferlist[ifill]; } } @@ -411,8 +413,9 @@ var SampleAudio = function(clockfreq) { sfrac += sinc; if (sfrac >= 1) { sfrac -= 1; - accum = 0; - this.addSingleSample(accum / sfrac); + value *= sfrac; + this.addSingleSample(accum - value); + accum = value; } } } diff --git a/src/platform/verilog.js b/src/platform/verilog.js index c66e459c..91a58673 100644 --- a/src/platform/verilog.js +++ b/src/platform/verilog.js @@ -181,7 +181,7 @@ var VerilogPlatform = function(mainElement, options) { var idata, timer; var gen; var frameRate = 60; - var AUDIO_FREQ = 15750; + var AUDIO_FREQ = (256+23+7+23)*262*60; // 4857480 var current_output; var paddle_x = 0; var paddle_y = 0; @@ -219,7 +219,7 @@ var VerilogPlatform = function(mainElement, options) { function vidtick() { gen.tick2(); - audio.addSingleSample(0+gen.spkr); // TODO: sync with audio freq + audio.feedSample((gen.spkr&255)*(1.0/255.0), 1); if (debugCond && debugCond()) debugCond = null; }