diff --git a/ZX-Spectrum.sv b/ZX-Spectrum.sv index 227b895..8d8c621 100644 --- a/ZX-Spectrum.sv +++ b/ZX-Spectrum.sv @@ -28,7 +28,7 @@ module emu input RESET, //Must be passed to hps_io module - inout [45:0] HPS_BUS, + inout [48:0] HPS_BUS, //Base video clock. Usually equals to CLK_SYS. output CLK_VIDEO, @@ -927,7 +927,7 @@ always @(posedge CLK_VIDEO) if (ce_pix) begin vbl <= VBlank; casex({ulap_ena, ulap_mono}) - 'b0X: {Gx,Rx,Bx} <= {G, {3{I & G}}, G, {3{I & G}}, R, {3{I & R}}, R, {3{I & R}}, B, {3{I & B}}, B, {3{I & B}}}; + 'b0X: {Gx,Rx,Bx} <= {G, G, {6{I & G}}, R, R, {6{I & R}}, B, B, {6{I & B}}}; 'b10: {Gx,Rx,Bx} <= {{2{ulap_color[7:5]}}, ulap_color[7:6], {2{ulap_color[4:2]}}, ulap_color[4:3], {4{ulap_color[1:0]}}}; 'b11: {Gx,Rx,Bx} <= {3{ulap_color}}; endcase diff --git a/sys/arcade_video.v b/sys/arcade_video.v index ff554a5..e0d0789 100644 --- a/sys/arcade_video.v +++ b/sys/arcade_video.v @@ -174,15 +174,16 @@ module screen_rotate input rotate_ccw, input no_rotate, + input flip, - output FB_EN, - output [4:0] FB_FORMAT, - output [11:0] FB_WIDTH, - output [11:0] FB_HEIGHT, - output [31:0] FB_BASE, - output [13:0] FB_STRIDE, - input FB_VBL, - input FB_LL, + output FB_EN, + output [4:0] FB_FORMAT, + output reg [11:0] FB_WIDTH, + output reg [11:0] FB_HEIGHT, + output [31:0] FB_BASE, + output [13:0] FB_STRIDE, + input FB_VBL, + input FB_LL, output DDRAM_CLK, input DDRAM_BUSY, @@ -196,6 +197,8 @@ module screen_rotate parameter MEM_BASE = 7'b0010010; // buffer at 0x24000000, 3x8MB +reg do_flip; + assign DDRAM_CLK = CLK_VIDEO; assign DDRAM_BURSTCNT = 1; assign DDRAM_ADDR = {MEM_BASE, i_fb, ram_addr[22:3]}; @@ -207,8 +210,6 @@ assign DDRAM_RD = 0; assign FB_EN = fb_en[2]; assign FB_FORMAT = 5'b00110; assign FB_BASE = {MEM_BASE,o_fb,23'd0}; -assign FB_WIDTH = vsz; -assign FB_HEIGHT = hsz; assign FB_STRIDE = stride; function [1:0] buf_next; @@ -220,6 +221,17 @@ function [1:0] buf_next; end endfunction +always @(posedge CLK_VIDEO) begin + do_flip <= no_rotate && flip; + if( do_flip ) begin + FB_WIDTH <= hsz; + FB_HEIGHT <= vsz; + end else begin + FB_WIDTH <= vsz; + FB_HEIGHT <= hsz; + end +end + reg [1:0] i_fb,o_fb; always @(posedge CLK_VIDEO) begin reg old_vbl,old_vs; @@ -251,20 +263,23 @@ always @(posedge CLK_VIDEO) begin if(CE_PIXEL) begin old_vs <= VGA_VS; old_de <= VGA_DE; - + hcnt <= hcnt + 1'd1; if(~old_de & VGA_DE) begin hcnt <= 1; vcnt <= vcnt + 1'd1; end - if(old_de & ~VGA_DE) hsz <= hcnt; + if(old_de & ~VGA_DE) begin + hsz <= hcnt; + if( do_flip ) bwidth <= hcnt + 2'd3; + end if(~old_vs & VGA_VS) begin vsz <= vcnt; - bwidth <= vcnt + 2'd3; + if( !do_flip ) bwidth <= vcnt + 2'd3; vcnt <= 0; - fb_en <= {fb_en[1:0], ~no_rotate}; + fb_en <= {fb_en[1:0], ~no_rotate | flip}; end - if(old_vs & ~VGA_VS) bufsize <= hsz * stride; + if(old_vs & ~VGA_VS) bufsize <= (do_flip ? vsz : hsz ) * stride; end end @@ -278,21 +293,25 @@ always @(posedge CLK_VIDEO) begin reg old_vs, old_de; ram_wr <= 0; - if(CE_PIXEL) begin + if(CE_PIXEL && FB_EN) begin old_vs <= VGA_VS; old_de <= VGA_DE; if(~old_vs & VGA_VS) begin - next_addr <= rotate_ccw ? (bufsize - stride) : {vsz-1'd1, 2'b00}; + next_addr <= + do_flip ? bufsize-3'd4 : + rotate_ccw ? (bufsize - stride) : {vsz-1'd1, 2'b00}; hcnt <= rotate_ccw ? 3'd4 : {vsz-2'd2, 2'b00}; end if(VGA_DE) begin ram_wr <= 1; - ram_data <= {VGA_B,VGA_G,VGA_R}; + ram_data <= {8'd0,VGA_B,VGA_G,VGA_R}; ram_addr <= next_addr; - next_addr <= rotate_ccw ? (next_addr - stride) : (next_addr + stride); + next_addr <= + do_flip ? next_addr-3'd4 : + rotate_ccw ? (next_addr - stride) : (next_addr + stride); end - if(old_de & ~VGA_DE) begin + if(old_de & ~VGA_DE & ~do_flip) begin next_addr <= rotate_ccw ? (bufsize - stride + hcnt) : hcnt; hcnt <= rotate_ccw ? (hcnt + 3'd4) : (hcnt - 3'd4); end diff --git a/sys/ascal.vhd b/sys/ascal.vhd index f157e9f..bd4e238 100644 --- a/sys/ascal.vhd +++ b/sys/ascal.vhd @@ -151,6 +151,7 @@ ENTITY ascal IS o_vs : OUT std_logic; -- V sync o_de : OUT std_logic; -- Display Enable o_vbl : OUT std_logic; -- V blank + o_brd : OUT std_logic; -- border enable o_ce : IN std_logic; -- Clock Enable o_clk : IN std_logic; -- Output clock @@ -458,6 +459,7 @@ ARCHITECTURE rtl OF ascal IS SIGNAL o_hacc,o_hacc_ini,o_hacc_next,o_vacc,o_vacc_next,o_vacc_ini : natural RANGE 0 TO 4*OHRES-1; SIGNAL o_hsv,o_vsv,o_dev,o_pev,o_end : unsigned(0 TO 5); SIGNAL o_hsp,o_vss : std_logic; + SIGNAL o_vcarrym,o_prim : boolean; SIGNAL o_read,o_read_pre : std_logic; SIGNAL o_readlev,o_copylev : natural RANGE 0 TO 2; SIGNAL o_hburst,o_hbcpt : natural RANGE 0 TO 31; @@ -485,6 +487,7 @@ ARCHITECTURE rtl OF ascal IS SIGNAL o_divstart : std_logic; SIGNAL o_divrun : std_logic; SIGNAL o_hacpt,o_vacpt : unsigned(11 DOWNTO 0); + SIGNAL o_vacptl : unsigned(1 DOWNTO 0); ----------------------------------------------------------------------------- FUNCTION shift_ishift(shift : unsigned(0 TO 119); @@ -705,8 +708,10 @@ ARCHITECTURE rtl OF ascal IS RETURN x; END FUNCTION; - SIGNAL o_h_frac2,o_v_frac : unsigned(FRAC-1 DOWNTO 0); + SIGNAL o_h_near_frac,o_v_near_frac : unsigned(FRAC-1 DOWNTO 0); + SIGNAL o_h_bil_frac,o_v_bil_frac : unsigned(FRAC-1 DOWNTO 0); SIGNAL o_h_bil_pix,o_v_bil_pix : type_pix; + SIGNAL o_h_near_pix,o_v_near_pix : type_pix; ----------------------------------------------------------------------------- -- Nearest + Bilinear + Sharp Bilinear @@ -718,6 +723,7 @@ ARCHITECTURE rtl OF ascal IS TYPE type_bil_t IS RECORD r,g,b : unsigned(8+FRAC DOWNTO 0); END RECORD; + FUNCTION bil_calc(f : unsigned(FRAC-1 DOWNTO 0); p : arr_pix(0 TO 3)) RETURN type_bil_t IS VARIABLE fp,fn : unsigned(FRAC DOWNTO 0); @@ -725,7 +731,7 @@ ARCHITECTURE rtl OF ascal IS VARIABLE x : type_bil_t; CONSTANT Z : unsigned(FRAC-1 DOWNTO 0):=(OTHERS =>'0'); BEGIN - fp:='0' & f; + fp:=('0' & f) + (Z & f(FRAC-1)); fn:=('1' & Z) - fp; u:=p(2).r * fp + p(1).r * fn; x.r:=u; @@ -735,7 +741,27 @@ ARCHITECTURE rtl OF ascal IS x.b:=u; RETURN x; END FUNCTION; + + FUNCTION near_calc(f : unsigned(FRAC-1 DOWNTO 0); + p : arr_pix(0 TO 3)) RETURN type_bil_t IS + VARIABLE fp,fn : unsigned(FRAC DOWNTO 0); + VARIABLE u : unsigned(8+FRAC DOWNTO 0); + VARIABLE x : type_bil_t; + CONSTANT Z : unsigned(FRAC-1 DOWNTO 0):=(OTHERS =>'0'); + BEGIN + IF f(FRAC-1)='0' THEN + x.r := '0' & p(1).r & Z; + x.g := '0' & p(1).g & Z; + x.b := '0' & p(1).b & Z; + ELSE + x.r := '0' & p(2).r & Z; + x.g := '0' & p(2).g & Z; + x.b := '0' & p(2).b & Z; + END IF; + RETURN x; + END FUNCTION; SIGNAL o_h_bil_t,o_v_bil_t : type_bil_t; + SIGNAL o_h_near_t,o_v_near_t : type_bil_t; SIGNAL i_h_bil_t : type_bil_t; ----------------------------------------------------------------------------- @@ -1864,35 +1890,51 @@ BEGIN o_state<=sHSYNC; o_hsp<='0'; END IF; + o_prim<=true; + o_vcarrym<=false; -------------------------------------------------- WHEN sHSYNC => - dif_v:=(o_vacc_next - 2*o_vsize + 16384) MOD 16384; + dif_v :=(o_vacc_next - 2*o_vsize + 16384) MOD 16384; + IF o_prim THEN + IF dif_v>=8192 THEN + o_vacc <=o_vacc_next; + ELSE + o_vacc <=dif_v; + END IF; + END IF; IF dif_v>=8192 THEN - o_vacc <=o_vacc_next; o_vacc_next<=(o_vacc_next + 2*o_ivsize) MOD 8192; vcarry_v:=false; ELSE - o_vacc <=dif_v; - o_vacc_next<=(dif_v + 2*o_ivsize + 8192) MOD 8192; + o_vacc_next<=dif_v; vcarry_v:=true; END IF; - o_divstart<='1'; + IF o_vcpt_pre2=o_vmin THEN o_vacc <=o_vacc_ini; o_vacc_next<=o_vacc_ini + 2*o_ivsize; - o_vacpt<=x"001"; + o_vacpt <=x"001"; + o_vacptl<="01"; vcarry_v:=false; END IF; IF vcarry_v THEN o_vacpt<=o_vacpt+1; END IF; + IF vcarry_v AND o_prim THEN + o_vacptl<=o_vacptl+1; + END IF; + o_vcarrym <= o_vcarrym OR vcarry_v; + o_prim <= false; o_hbcpt<=0; -- Clear burst counter on line - IF (o_vpe='1' AND vcarry_v) OR o_fload>0 THEN - o_state<=sREAD; - ELSE - o_state<=sDISP; + o_divstart<=to_std_logic(NOT vcarry_v); + IF NOT vcarry_v OR o_fload>0 THEN + IF (o_vpe='1' AND o_vcarrym) OR o_fload>0 THEN + o_state<=sREAD; + ELSE + o_state<=sDISP; + END IF; END IF; WHEN sREAD => @@ -1942,7 +1984,7 @@ BEGIN o_alt<="0100"; ELSE o_adrs<=to_unsigned(o_adrs_pre + (o_hbcpt * N_BURST),32); - o_alt<=altx(o_vacpt(1 DOWNTO 0) + 1); + o_alt<=altx(o_vacptl + 1); END IF; END IF; @@ -2276,33 +2318,38 @@ BEGIN o_hpixq<=(o_hpix3,o_hpix2,o_hpix1,o_hpix0); - -- NEAREST / BILINEAR / SHARP BILINEAR --------------- + -- NEAREST ------------------------------------------- + -- C2 + o_h_near_frac<=near_frac(o_hfrac2); + + -- C3 + o_h_near_t<=near_calc(o_h_near_frac,o_hpixq); + + -- C4 : Nearest + o_h_near_pix.r<=o_h_near_t.r(7+FRAC DOWNTO FRAC); + o_h_near_pix.g<=o_h_near_t.g(7+FRAC DOWNTO FRAC); + o_h_near_pix.b<=o_h_near_t.b(7+FRAC DOWNTO FRAC); + + -- BILINEAR / SHARP BILINEAR --------------- -- C1 : Pre-calc Sharp Bilinear o_h_sbil_t<=sbil_frac1(o_hfrac1); -- C2 : Select - o_h_frac2<=(OTHERS =>'0'); - CASE o_hmode(1 DOWNTO 0) IS - WHEN "00" => -- Nearest - IF MASK(MASK_NEAREST)='1' THEN - o_h_frac2<=near_frac(o_hfrac2); - END IF; - WHEN "01" => -- Bilinear - IF MASK(MASK_BILINEAR)='1' THEN - o_h_frac2<=bil_frac(o_hfrac2); - END IF; - WHEN "10" => -- Sharp Bilinear - IF MASK(MASK_SHARP_BILINEAR)='1' THEN - o_h_frac2<=sbil_frac2(o_hfrac2,o_h_sbil_t); - END IF; - WHEN OTHERS => - NULL; - END CASE; - + o_h_bil_frac<=(OTHERS =>'0'); + IF o_hmode(0)='1' THEN -- Bilinear + IF MASK(MASK_BILINEAR)='1' THEN + o_h_bil_frac<=bil_frac(o_hfrac2); + END IF; + ELSE -- Sharp Bilinear + IF MASK(MASK_SHARP_BILINEAR)='1' THEN + o_h_bil_frac<=sbil_frac2(o_hfrac2,o_h_sbil_t); + END IF; + END IF; + -- C3 : Opposite frac - o_h_bil_t<=bil_calc(o_h_frac2,o_hpixq); + o_h_bil_t<=bil_calc(o_h_bil_frac,o_hpixq); - -- C4 : Nearest / Bilinear / Sharp Bilinear + -- C4 : Bilinear / Sharp Bilinear o_h_bil_pix.r<=bound(o_h_bil_t.r,8+FRAC); o_h_bil_pix.g<=bound(o_h_bil_t.g,8+FRAC); o_h_bil_pix.b<=bound(o_h_bil_t.b,8+FRAC); @@ -2340,9 +2387,12 @@ BEGIN o_ldw<=(x"00",x"00",x"00"); CASE o_hmode(2 DOWNTO 0) IS - WHEN "000" | "001" | "010" => -- Nearest | Bilinear | Sharp Bilinear - IF MASK(MASK_NEAREST)='1' OR - MASK(MASK_BILINEAR)='1' OR + WHEN "000" => -- Nearest + IF MASK(MASK_NEAREST)='1' THEN + o_ldw<=o_h_near_pix; + END IF; + WHEN "001" | "010" => -- Bilinear | Sharp Bilinear + IF MASK(MASK_BILINEAR)='1' OR MASK(MASK_SHARP_BILINEAR)='1' THEN o_ldw<=o_h_bil_pix; END IF; @@ -2441,7 +2491,7 @@ BEGIN -- CYCLE 2 ----------------------------------------- -- Lines reordering - CASE o_vacpt(1 DOWNTO 0) IS + CASE o_vacptl IS WHEN "10" => pixq_v:=(o_ldr0,o_ldr1,o_ldr2,o_ldr3); WHEN "11" => pixq_v:=(o_ldr1,o_ldr2,o_ldr3,o_ldr0); WHEN "00" => pixq_v:=(o_ldr2,o_ldr3,o_ldr0,o_ldr1); @@ -2461,31 +2511,37 @@ BEGIN o_vpixq1<=o_vpixq; - -- NEAREST / BILINEAR / SHARP BILINEAR ------------- + -- NEAREST ----------------------------------------- + -- C4 + o_v_near_frac<=near_frac(o_vfrac); + + -- C5 + o_v_near_t<=near_calc(o_v_near_frac,o_vpixq1); + + -- C6 : Nearest + o_v_near_pix.r<=o_v_near_t.r(7+FRAC DOWNTO FRAC); + o_v_near_pix.g<=o_v_near_t.g(7+FRAC DOWNTO FRAC); + o_v_near_pix.b<=o_v_near_t.b(7+FRAC DOWNTO FRAC); + + -- BILINEAR / SHARP BILINEAR ----------------------- -- C3 : Pre-calc Sharp Bilinear o_v_sbil_t<=sbil_frac1(o_vfrac); -- C4 : Select - o_v_frac<=(OTHERS =>'0'); - CASE o_vmode(1 DOWNTO 0) IS - WHEN "00" => -- Nearest - IF MASK(MASK_NEAREST)='1' THEN - o_v_frac<=near_frac(o_vfrac); - END IF; - WHEN "01" => -- Bilinear - IF MASK(MASK_BILINEAR)='1' THEN - o_v_frac<=bil_frac(o_vfrac); - END IF; - WHEN "10" => -- Sharp Bilinear - IF MASK(MASK_SHARP_BILINEAR)='1' THEN - o_v_frac<=sbil_frac2(o_vfrac,o_v_sbil_t); - END IF; - WHEN OTHERS => NULL; - END CASE; + o_v_bil_frac<=(OTHERS =>'0'); + IF o_vmode(0)='1' THEN -- Bilinear + IF MASK(MASK_BILINEAR)='1' THEN + o_v_bil_frac<=bil_frac(o_vfrac); + END IF; + ELSE -- Sharp Bilinear + IF MASK(MASK_SHARP_BILINEAR)='1' THEN + o_v_bil_frac<=sbil_frac2(o_vfrac,o_v_sbil_t); + END IF; + END IF; - o_v_bil_t<=bil_calc(o_v_frac,o_vpixq1); + o_v_bil_t<=bil_calc(o_v_bil_frac,o_vpixq1); - -- C6 : Nearest / Bilinear / Sharp Bilinear + -- C6 : Bilinear / Sharp Bilinear o_v_bil_pix.r<=bound(o_v_bil_t.r,8+FRAC); o_v_bil_pix.g<=bound(o_v_bil_t.g,8+FRAC); o_v_bil_pix.b<=bound(o_v_bil_t.b,8+FRAC); @@ -2524,11 +2580,17 @@ BEGIN o_r<=x"00"; o_g<=x"00"; o_b<=x"00"; + o_brd<= not o_pev(5); CASE o_vmode(2 DOWNTO 0) IS - WHEN "000" | "001" | "010" => -- Nearest | Bilinear | Sharp Bilinear - IF MASK(MASK_NEAREST)='1' OR - MASK(MASK_BILINEAR)='1' OR + WHEN "000" => -- Nearest + IF MASK(MASK_NEAREST)='1' THEN + o_r<=o_v_near_pix.r; + o_g<=o_v_near_pix.g; + o_b<=o_v_near_pix.b; + END IF; + WHEN "001" | "010" => -- Bilinear | Sharp Bilinear + IF MASK(MASK_BILINEAR)='1' OR MASK(MASK_SHARP_BILINEAR)='1' THEN o_r<=o_v_bil_pix.r; o_g<=o_v_bil_pix.g; diff --git a/sys/hps_io.sv b/sys/hps_io.sv index 9ab1a3a..67ee7c6 100644 --- a/sys/hps_io.sv +++ b/sys/hps_io.sv @@ -24,13 +24,13 @@ // Use buffer to access SD card. It's time-critical part. // // WIDE=1 for 16 bit file I/O -// VDNUM 1..4 +// VDNUM 1..10 // BLKSZ 0..7: 0 = 128, 1 = 256, 2 = 512(default), .. 7 = 16384 // module hps_io #(parameter CONF_STR, CONF_STR_BRAM=1, PS2DIV=0, WIDE=0, VDNUM=1, BLKSZ=2, PS2WE=0) ( input clk_sys, - inout [45:0] HPS_BUS, + inout [48:0] HPS_BUS, // buttons up to 32 output reg [31:0] joystick_0, @@ -41,12 +41,19 @@ module hps_io #(parameter CONF_STR, CONF_STR_BRAM=1, PS2DIV=0, WIDE=0, VDNUM=1, output reg [31:0] joystick_5, // analog -127..+127, Y: [15:8], X: [7:0] - output reg [15:0] joystick_analog_0, - output reg [15:0] joystick_analog_1, - output reg [15:0] joystick_analog_2, - output reg [15:0] joystick_analog_3, - output reg [15:0] joystick_analog_4, - output reg [15:0] joystick_analog_5, + output reg [15:0] joystick_l_analog_0, + output reg [15:0] joystick_l_analog_1, + output reg [15:0] joystick_l_analog_2, + output reg [15:0] joystick_l_analog_3, + output reg [15:0] joystick_l_analog_4, + output reg [15:0] joystick_l_analog_5, + + output reg [15:0] joystick_r_analog_0, + output reg [15:0] joystick_r_analog_1, + output reg [15:0] joystick_r_analog_2, + output reg [15:0] joystick_r_analog_3, + output reg [15:0] joystick_r_analog_4, + output reg [15:0] joystick_r_analog_5, // paddle 0..255 output reg [7:0] paddle_0, @@ -104,6 +111,7 @@ module hps_io #(parameter CONF_STR, CONF_STR_BRAM=1, PS2DIV=0, WIDE=0, VDNUM=1, output reg [26:0] ioctl_addr, // in WIDE mode address will be incremented by 2 output reg [DW:0] ioctl_dout, output reg ioctl_upload = 0, // signal indicating an active upload + input ioctl_upload_req, input [DW:0] ioctl_din, output reg ioctl_rd, output reg [31:0] ioctl_file_ext, @@ -250,6 +258,8 @@ always@(posedge clk_sys) begin : uio_block reg [3:0] stflg = 0; reg [63:0] status_req; reg old_status_set = 0; + reg old_upload_req = 0; + reg upload_req = 0; reg old_info = 0; reg [7:0] info_n = 0; reg [15:0] tmp1; @@ -261,6 +271,9 @@ always@(posedge clk_sys) begin : uio_block stflg <= stflg + 1'd1; status_req <= status_in; end + + old_upload_req <= ioctl_upload_req; + if(~old_upload_req & ioctl_upload_req) upload_req <= 1; old_info <= info_req; if(~old_info & info_req) info_n <= info; @@ -303,11 +316,13 @@ always@(posedge clk_sys) begin : uio_block 'h0X17, 'h0X18: begin sd_ack <= disk[VD:0]; sdn_ack <= io_din[11:8]; end 'h29: io_dout <= {4'hA, stflg}; - 'h2B: io_dout <= 1; + 'h2B: io_dout <= {HPS_BUS[48:46],4'b0010}; 'h2F: io_dout <= 1; 'h32: io_dout <= gamma_bus[21]; 'h36: begin io_dout <= info_n; info_n <= 0; end 'h39: io_dout <= 1; + 'h3C: if(upload_req) begin io_dout <= 1; upload_req <= 0; end + 'h3E: io_dout <= 1; // shadow mask endcase sd_buff_addr <= 0; @@ -380,17 +395,17 @@ always@(posedge clk_sys) begin : uio_block io_dout <= sd_buff_din[sdn_ack]; end - // joystick analog + // joystick left analog 'h1a: if(!byte_cnt[MAX_W:2]) begin case(byte_cnt[1:0]) 1: {pdsp_idx,stick_idx} <= io_din[7:0]; // first byte is joystick index 2: case(stick_idx) - 0: joystick_analog_0 <= io_din; - 1: joystick_analog_1 <= io_din; - 2: joystick_analog_2 <= io_din; - 3: joystick_analog_3 <= io_din; - 4: joystick_analog_4 <= io_din; - 5: joystick_analog_5 <= io_din; + 0: joystick_l_analog_0 <= io_din; + 1: joystick_l_analog_1 <= io_din; + 2: joystick_l_analog_2 <= io_din; + 3: joystick_l_analog_3 <= io_din; + 4: joystick_l_analog_4 <= io_din; + 5: joystick_l_analog_5 <= io_din; 15: case(pdsp_idx) 0: paddle_0 <= io_din[7:0]; 1: paddle_1 <= io_din[7:0]; @@ -409,6 +424,21 @@ always@(posedge clk_sys) begin : uio_block endcase end + // joystick right analog + 'h3d: if(!byte_cnt[MAX_W:2]) begin + case(byte_cnt[1:0]) + 1: stick_idx <= io_din[3:0]; // first byte is joystick index + 2: case(stick_idx) + 0: joystick_r_analog_0 <= io_din; + 1: joystick_r_analog_1 <= io_din; + 2: joystick_r_analog_2 <= io_din; + 3: joystick_r_analog_3 <= io_din; + 4: joystick_r_analog_4 <= io_din; + 5: joystick_r_analog_5 <= io_din; + endcase + endcase + end + // notify image selection 'h1c: begin img_mounted <= io_din[VD:0] ? io_din[VD:0] : 1'b1; diff --git a/sys/shadowmask.sv b/sys/shadowmask.sv new file mode 100644 index 0000000..572679c --- /dev/null +++ b/sys/shadowmask.sv @@ -0,0 +1,136 @@ +module shadowmask +( + input clk, + input clk_sys, + + input cmd_wr, + input [15:0] cmd_in, + + input [23:0] din, + input hs_in,vs_in, + input de_in, + input brd_in, + input enable, + + output reg [23:0] dout, + output reg hs_out,vs_out, + output reg de_out +); + + +reg [4:0] hmax; +reg [4:0] vmax; +reg [7:0] mask_idx; +reg mask_2x; +reg mask_rotate; +reg mask_enable; +reg [10:0] mask_lut[256]; + +always @(posedge clk) begin + reg [4:0] hcount; + reg [4:0] vcount; + reg [3:0] hindex; + reg [3:0] vindex; + reg [4:0] hmax2; + reg [4:0] vmax2; + reg [11:0] pcnt,pde; + reg old_hs, old_vs, old_brd; + reg next_v; + + old_hs <= hs_in; + old_vs <= vs_in; + old_brd<= brd_in; + + // hcount and vcount counts pixel rows and columns + // hindex and vindex half the value of the counters for double size patterns + // hindex2, vindex2 swap the h and v counters for drawing rotated masks + hindex <= mask_2x ? hcount[4:1] : hcount[3:0]; + vindex <= mask_2x ? vcount[4:1] : vcount[3:0]; + mask_idx <= mask_rotate ? {hindex,vindex} : {vindex,hindex}; + + // hmax and vmax store these sizes + // hmax2 and vmax2 swap the values to handle rotation + hmax2 <= ((mask_rotate ? vmax : hmax) << mask_2x) | mask_2x; + vmax2 <= ((mask_rotate ? hmax : vmax) << mask_2x) | mask_2x; + + pcnt <= pcnt+1'd1; + if(old_brd && ~brd_in) pde <= pcnt-4'd3; + + hcount <= hcount+1'b1; + if(hcount == hmax2 || pde == pcnt) hcount <= 0; + + if(~old_brd && brd_in) next_v <= 1; + if(old_vs && ~vs_in) vcount <= 0; + if(old_hs && ~hs_in) begin + vcount <= vcount + next_v; + next_v <= 0; + pcnt <= 0; + if (vcount == vmax2) vcount <= 0; + end +end + +reg [4:0] r_mul, g_mul, b_mul; // 1.4 fixed point multipliers +always @(posedge clk) begin + reg [10:0] lut; + + lut <= mask_lut[mask_idx]; + + r_mul <= 5'b10000; g_mul <= 5'b10000; b_mul <= 5'b10000; // default 100% to all channels + if (mask_enable) begin + r_mul <= lut[10] ? {1'b1,lut[7:4]} : {1'b0,lut[3:0]}; + g_mul <= lut[9] ? {1'b1,lut[7:4]} : {1'b0,lut[3:0]}; + b_mul <= lut[8] ? {1'b1,lut[7:4]} : {1'b0,lut[3:0]}; + end +end + +always @(posedge clk) begin + reg [11:0] vid; + reg [7:0] r1, g1, b1; + reg [7:0] r2, g2, b2; + reg [7:0] r3_x, g3_x, b3_x; // 6.25% + 12.5% + reg [8:0] r3_y, g3_y, b3_y; // 25% + 50% + 100% + reg [8:0] r4, g4, b4; + + // C1 - data input + {r1,g1,b1} <= din; + vid <= {vid[8:0],vs_in, hs_in, de_in}; + + // C2 - relax timings + {r2,g2,b2} <= {r1,g1,b1}; + + // C3 - perform multiplications + r3_x <= ({4{r_mul[0]}} & r2[7:4]) + ({8{r_mul[1]}} & r2[7:3]); + r3_y <= ({6{r_mul[2]}} & r2[7:2]) + ({7{r_mul[3]}} & r2[7:1]) + ({9{r_mul[4]}} & r2[7:0]); + g3_x <= ({4{g_mul[0]}} & g2[7:4]) + ({8{g_mul[1]}} & g2[7:3]); + g3_y <= ({6{g_mul[2]}} & g2[7:2]) + ({7{g_mul[3]}} & g2[7:1]) + ({9{g_mul[4]}} & g2[7:0]); + b3_x <= ({4{b_mul[0]}} & b2[7:4]) + ({8{b_mul[1]}} & b2[7:3]); + b3_y <= ({6{b_mul[2]}} & b2[7:2]) + ({7{b_mul[3]}} & b2[7:1]) + ({9{b_mul[4]}} & b2[7:0]); + + // C4 - combine results + r4 <= r3_x + r3_y; + g4 <= g3_x + g3_y; + b4 <= b3_x + b3_y; + + // C5 - clamp and output + dout <= {{8{r4[8]}} | r4[7:0], {8{g4[8]}} | g4[7:0], {8{b4[8]}} | b4[7:0]}; + {vs_out,hs_out,de_out} <= vid[11:9]; +end + +// clock in mask commands +always @(posedge clk_sys) begin + reg m_enable; + reg [7:0] idx; + + if (cmd_wr) begin + case(cmd_in[15:13]) + 3'b000: begin {m_enable, mask_rotate, mask_2x} <= cmd_in[3:1]; idx <= 0; end + 3'b001: vmax <= cmd_in[3:0]; + 3'b010: hmax <= cmd_in[3:0]; + 3'b011: begin mask_lut[idx] <= cmd_in[10:0]; idx <= idx + 1'd1; end + endcase + end + + mask_enable <= m_enable & enable; +end + +endmodule diff --git a/sys/sys.qip b/sys/sys.qip index 2854eaf..094dab7 100644 --- a/sys/sys.qip +++ b/sys/sys.qip @@ -7,6 +7,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) m set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) hq2x.sv ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scanlines.v ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) shadowmask.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_cleaner.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) gamma_corr.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_mixer.sv ] diff --git a/sys/sys_top.v b/sys/sys_top.v index dbf8a8a..4bbf5e0 100644 --- a/sys/sys_top.v +++ b/sys/sys_top.v @@ -295,7 +295,7 @@ reg [31:0] cfg_custom_p2; reg [4:0] vol_att; initial vol_att = 5'b11111; -reg [6:0] coef_addr; +reg [8:0] coef_addr; reg [8:0] coef_data; reg coef_wr = 0; @@ -336,6 +336,10 @@ always@(posedge clk_sys) begin old_strobe <= io_strobe; coef_wr <= 0; +`ifndef MISTER_DEBUG_NOHDMI + shadowmask_wr <= 0; +`endif + if(~io_uio) begin has_cmd <= 0; cmd <= 0; @@ -363,6 +367,7 @@ always@(posedge clk_sys) begin end end else begin + cnt <= cnt + 1'd1; if(cmd == 1) begin cfg <= io_din; cfg_set <= 1; @@ -370,7 +375,6 @@ always@(posedge clk_sys) begin end if(cmd == 'h20) begin cfg_set <= 0; - cnt <= cnt + 1'd1; if(cnt<8) begin case(cnt[2:0]) 0: if(WIDTH != io_din[11:0]) WIDTH <= io_din[11:0]; @@ -402,7 +406,6 @@ always@(posedge clk_sys) begin end end if(cmd == 'h2F) begin - cnt <= cnt + 1'd1; case(cnt[3:0]) 0: {LFB_EN,LFB_FLT,LFB_FMT} <= {io_din[15], io_din[14], io_din[5:0]}; 1: LFB_BASE[15:0] <= io_din[15:0]; @@ -419,12 +422,14 @@ always@(posedge clk_sys) begin if(cmd == 'h25) {led_overtake, led_state} <= io_din; if(cmd == 'h26) vol_att <= io_din[4:0]; if(cmd == 'h27) VSET <= io_din[11:0]; - if(cmd == 'h2A) {coef_wr,coef_addr,coef_data} <= {1'b1,io_din}; + if(cmd == 'h2A) begin + if(cnt[0]) {coef_wr,coef_data} <= {1'b1,io_din[8:0]}; + else coef_addr <= io_din[8:0]; + end if(cmd == 'h2B) scaler_flt <= io_din[2:0]; if(cmd == 'h37) {FREESCALE,HSET} <= {io_din[15],io_din[11:0]}; if(cmd == 'h38) vs_line <= io_din[11:0]; if(cmd == 'h39) begin - cnt <= cnt + 1'd1; case(cnt[3:0]) 0: acx_att <= io_din[4:0]; 1: aflt_rate[15:0] <= io_din; @@ -444,7 +449,6 @@ always@(posedge clk_sys) begin endcase end if(cmd == 'h3A) begin - cnt <= cnt + 1'd1; case(cnt[3:0]) 0: arc1x <= io_din[12:0]; 1: arc1y <= io_din[12:0]; @@ -452,6 +456,9 @@ always@(posedge clk_sys) begin 3: arc2y <= io_din[12:0]; endcase end +`ifndef MISTER_DEBUG_NOHDMI + if(cmd == 'h3E) {shadowmask_wr,shadowmask_data} <= {1'b1, io_din}; +`endif end end @@ -618,7 +625,7 @@ wire [15:0] vbuf_byteenable; wire vbuf_write; wire [23:0] hdmi_data; -wire hdmi_vs, hdmi_hs, hdmi_de, hdmi_vbl; +wire hdmi_vs, hdmi_hs, hdmi_de, hdmi_vbl, hdmi_brd; wire freeze; `ifndef MISTER_DEBUG_NOHDMI @@ -634,6 +641,7 @@ ascal .PALETTE2("false"), `endif `endif + .FRAC(6), .N_DW(128), .N_AW(28) ) @@ -667,6 +675,7 @@ ascal .o_vs (hdmi_vs), .o_de (hdmi_de), .o_vbl (hdmi_vbl), + .o_brd (hdmi_brd), .o_lltune (lltune), .htotal (WIDTH + HFP + HBP + HS), .hsstart (WIDTH + HFP), @@ -1049,34 +1058,43 @@ cyclonev_hps_interface_peripheral_i2c hdmi_i2c ); `ifndef MISTER_DEBUG_NOHDMI -wire [23:0] hdmi_data_sl; -wire hdmi_de_sl, hdmi_vs_sl, hdmi_hs_sl; `ifdef MISTER_FB reg dis_output; always @(posedge clk_hdmi) begin reg dis; - dis <= fb_force_blank; + dis <= fb_force_blank & ~LFB_EN; dis_output <= dis; end `else wire dis_output = 0; `endif -scanlines #(1) HDMI_scanlines +wire [23:0] hdmi_data_mask; +wire hdmi_de_mask, hdmi_vs_mask, hdmi_hs_mask; + +reg [15:0] shadowmask_data; +reg shadowmask_wr = 0; + +shadowmask HDMI_shadowmask ( .clk(clk_hdmi), + .clk_sys(clk_sys), + + .cmd_wr(shadowmask_wr), + .cmd_in(shadowmask_data), - .scanlines(scanlines), .din(dis_output ? 24'd0 : hdmi_data), .hs_in(hdmi_hs), .vs_in(hdmi_vs), .de_in(hdmi_de), - - .dout(hdmi_data_sl), - .hs_out(hdmi_hs_sl), - .vs_out(hdmi_vs_sl), - .de_out(hdmi_de_sl) + .brd_in(hdmi_brd), + .enable(~LFB_EN), + + .dout(hdmi_data_mask), + .hs_out(hdmi_hs_mask), + .vs_out(hdmi_vs_mask), + .de_out(hdmi_de_mask) ); wire [23:0] hdmi_data_osd; @@ -1091,10 +1109,10 @@ osd hdmi_osd .io_din(io_din), .clk_video(clk_hdmi), - .din(hdmi_data_sl), - .hs_in(hdmi_hs_sl), - .vs_in(hdmi_vs_sl), - .de_in(hdmi_de_sl), + .din(hdmi_data_mask), + .hs_in(hdmi_hs_mask), + .vs_in(hdmi_vs_mask), + .de_in(hdmi_de_mask), .dout(hdmi_data_osd), .hs_out(hdmi_hs_osd), @@ -1462,12 +1480,12 @@ wire [6:0] user_out, user_in; assign clk_ihdmi= clk_vid; assign ce_hpix = ce_pix; -assign hr_out = r_out; -assign hg_out = g_out; -assign hb_out = b_out; -assign hhs_fix = hs_fix; -assign hvs_fix = vs_fix; -assign hde_emu = de_emu; +assign hr_out = vga_data_sl[23:16]; +assign hg_out = vga_data_sl[15:8]; +assign hb_out = vga_data_sl[7:0]; +assign hhs_fix = vga_hs_sl; +assign hvs_fix = vga_vs_sl; +assign hde_emu = vga_de_sl; wire uart_dtr; wire uart_dsr; @@ -1504,11 +1522,15 @@ wire [13:0] fb_stride; assign fb_stride = 0; `endif +reg [1:0] sl_r; +wire [1:0] sl = sl_r; +always @(posedge clk_sys) sl_r <= FB_EN ? 2'b00 : scanlines; + emu emu ( .CLK_50M(FPGA_CLK2_50), .RESET(reset), - .HPS_BUS({f1, HDMI_TX_VS, + .HPS_BUS({fb_en, sl, f1, HDMI_TX_VS, clk_100m, clk_ihdmi, ce_hpix, hde_emu, hhs_fix, hvs_fix, io_wait, clk_sys, io_fpga, io_uio, io_strobe, io_wide, io_din, io_dout}),