Update sys. Re-organize the sources.

This commit is contained in:
sorgelig
2020-05-11 23:17:53 +08:00
parent 0c1cb37dba
commit 5a66d5ec1c
102 changed files with 1996 additions and 1364 deletions

280
rtl/video/video_cram.mif Normal file
View File

@ -0,0 +1,280 @@
-- Copyright (C) 1991-2007 Altera Corporation
-- Your use of Altera Corporation's design tools, logic functions
-- and other software and tools, and its AMPP partner logic
-- functions, and any output files from any of the foregoing
-- (including device programming or simulation files), and any
-- associated documentation or information are expressly subject
-- to the terms and conditions of the Altera Program License
-- Subscription Agreement, Altera MegaCore Function License
-- Agreement, or other applicable license agreement, including,
-- without limitation, that your use is for the sole purpose of
-- programming logic devices manufactured by Altera and sold by
-- Altera or its authorized distributors. Please refer to the
-- applicable agreement for further details.
-- Quartus II generated Memory Initialization File (.mif)
WIDTH=16;
DEPTH=256;
ADDRESS_RADIX=HEX;
DATA_RADIX=HEX;
CONTENT BEGIN
000 : 0000;
001 : 0008;
002 : 0010;
003 : 0018;
004 : 2000;
005 : 2008;
006 : 2010;
007 : 2018;
008 : 4000;
009 : 4008;
00A : 4010;
00B : 4018;
00C : 6000;
00D : 6008;
00E : 6010;
00F : 6018;
010 : 0100;
011 : 0108;
012 : 0110;
013 : 0118;
014 : 2100;
015 : 2108;
016 : 2110;
017 : 2118;
018 : 4100;
019 : 4108;
01A : 4110;
01B : 4118;
01C : 6100;
01D : 6108;
01E : 6110;
01F : 6118;
020 : 0200;
021 : 0208;
022 : 0210;
023 : 0218;
024 : 2200;
025 : 2208;
026 : 2210;
027 : 2218;
028 : 4200;
029 : 4208;
02A : 4210;
02B : 4218;
02C : 6200;
02D : 6208;
02E : 6210;
02F : 6218;
030 : 0300;
031 : 0308;
032 : 0310;
033 : 0318;
034 : 2300;
035 : 2308;
036 : 2310;
037 : 2318;
038 : 4300;
039 : 4308;
03A : 4310;
03B : 4318;
03C : 6300;
03D : 6308;
03E : 6310;
03F : 6318;
040 : 0000;
041 : 0010;
042 : 4000;
043 : 4010;
044 : 0200;
045 : 0210;
046 : 4200;
047 : 4210;
048 : 2108;
049 : 0018;
04A : 6000;
04B : 6018;
04C : 0300;
04D : 0318;
04E : 6300;
04F : 6318;
050 : 0000;
051 : 0010;
052 : 4000;
053 : 4010;
054 : 0200;
055 : 0210;
056 : 4200;
057 : 4210;
058 : 0000;
059 : 0018;
05A : 6000;
05B : 6018;
05C : 0300;
05D : 0318;
05E : 6300;
05F : 6318;
060 : 0000;
061 : 0010;
062 : 4000;
063 : 4010;
064 : 0200;
065 : 0210;
066 : 4200;
067 : 4210;
068 : 2108;
069 : 0018;
06A : 6000;
06B : 6018;
06C : 0300;
06D : 0318;
06E : 6300;
06F : 6318;
070 : 0000;
071 : 0010;
072 : 4000;
073 : 4010;
074 : 0200;
075 : 0210;
076 : 4200;
077 : 4210;
078 : 0000;
079 : 0018;
07A : 6000;
07B : 6018;
07C : 0300;
07D : 0318;
07E : 6300;
07F : 6318;
080 : 0000;
081 : 0010;
082 : 4000;
083 : 4010;
084 : 0200;
085 : 0210;
086 : 4200;
087 : 4210;
088 : 2108;
089 : 0018;
08A : 6000;
08B : 6018;
08C : 0300;
08D : 0318;
08E : 6300;
08F : 6318;
090 : 0000;
091 : 0010;
092 : 4000;
093 : 4010;
094 : 0200;
095 : 0210;
096 : 4200;
097 : 4210;
098 : 0000;
099 : 0018;
09A : 6000;
09B : 6018;
09C : 0300;
09D : 0318;
09E : 6300;
09F : 6318;
0A0 : 0000;
0A1 : 0010;
0A2 : 4000;
0A3 : 4010;
0A4 : 0200;
0A5 : 0210;
0A6 : 4200;
0A7 : 4210;
0A8 : 2108;
0A9 : 0018;
0AA : 6000;
0AB : 6018;
0AC : 0300;
0AD : 0318;
0AE : 6300;
0AF : 6318;
0B0 : 0000;
0B1 : 0010;
0B2 : 4000;
0B3 : 4010;
0B4 : 0200;
0B5 : 0210;
0B6 : 4200;
0B7 : 4210;
0B8 : 0000;
0B9 : 0018;
0BA : 6000;
0BB : 6018;
0BC : 0300;
0BD : 0318;
0BE : 6300;
0BF : 6318;
0C0 : 0000;
0C1 : 0010;
0C2 : 4000;
0C3 : 4010;
0C4 : 0200;
0C5 : 0210;
0C6 : 4200;
0C7 : 4210;
0C8 : 2108;
0C9 : 0018;
0CA : 6000;
0CB : 6018;
0CC : 0300;
0CD : 0318;
0CE : 6300;
0CF : 6318;
0D0 : 0000;
0D1 : 0010;
0D2 : 4000;
0D3 : 4010;
0D4 : 0200;
0D5 : 0210;
0D6 : 4200;
0D7 : 4210;
0D8 : 0000;
0D9 : 0018;
0DA : 6000;
0DB : 6018;
0DC : 0300;
0DD : 0318;
0DE : 6300;
0DF : 6318;
0E0 : 0000;
0E1 : 0010;
0E2 : 4000;
0E3 : 4010;
0E4 : 0200;
0E5 : 0210;
0E6 : 4200;
0E7 : 4210;
0E8 : 2108;
0E9 : 0018;
0EA : 6000;
0EB : 6018;
0EC : 0300;
0ED : 0318;
0EE : 6300;
0EF : 6318;
0F0 : 0000;
0F1 : 0010;
0F2 : 4000;
0F3 : 4010;
0F4 : 0200;
0F5 : 0210;
0F6 : 4200;
0F7 : 4210;
0F8 : 0000;
0F9 : 0018;
0FA : 6000;
0FB : 6018;
0FC : 0300;
0FD : 0318;
0FE : 6300;
0FF : 6318;
END;

33
rtl/video/video_fetch.v Normal file
View File

@ -0,0 +1,33 @@
// This module fetches video data from DRAM
module video_fetch
(
// clocks
input wire clk,
// control
input wire [3:0] f_sel,
input wire [1:0] b_sel,
input wire fetch_stb,
// video data
output reg [31:0] fetch_data,
output reg [31:0] fetch_temp,
// DRAM interface
input wire video_strobe,
input wire [15:0] video_data
);
// fetching data
always @(posedge clk) if (video_strobe)
begin
if (f_sel[0]) fetch_temp[ 7: 0] <= b_sel[0] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[1]) fetch_temp[15: 8] <= b_sel[1] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[2]) fetch_temp[23:16] <= video_data[ 7:0];
if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8];
end
always @(posedge clk) if (fetch_stb) fetch_data <= fetch_temp;
endmodule

235
rtl/video/video_mode.v Normal file
View File

@ -0,0 +1,235 @@
// This module decodes video modes
module video_mode
(
// clocks
input wire clk, f1, c3,
// video config
input wire [7:0] vpage,
input wire [7:0] vconf,
input wire ts_rres_ext,
// video parameters & mode controls
input wire [8:0] gx_offs,
output wire [9:0] x_offs_mode,
output wire [8:0] hpix_beg,
output wire [8:0] hpix_end,
output wire [8:0] vpix_beg,
output wire [8:0] vpix_end,
output wire [8:0] hpix_beg_ts,
output wire [8:0] hpix_end_ts,
output wire [8:0] vpix_beg_ts,
output wire [8:0] vpix_end_ts,
output wire [5:0] x_tiles,
output wire [4:0] go_offs,
output wire [3:0] fetch_sel,
output wire [1:0] fetch_bsl,
input wire [3:0] fetch_cnt,
input wire pix_start,
input wire line_start_s,
output wire tv_hires,
output wire [1:0] render_mode,
output wire pix_stb,
output wire fetch_stb,
// video data
input wire [15:0] txt_char,
// video counters
input wire [7:0] cnt_col,
input wire [8:0] cnt_row,
input wire cptr,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
// clocking strobe for pixels (TV)
assign pix_stb = tv_hires ? f1 : c3;
// Modes
localparam M_ZX = 2'h0; // ZX
localparam M_HC = 2'h1; // 16c
localparam M_XC = 2'h2; // 256c
localparam M_TX = 2'h3; // Text
// Render modes (affects 'video_render.v')
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// fetch strobes
wire ftch[0:3];
assign fetch_stb = (pix_start | ftch[render_mode]) && c3;
assign ftch[R_ZX] = &fetch_cnt[3:0];
assign ftch[R_HC] = &fetch_cnt[1:0];
assign ftch[R_XC] = fetch_cnt[0];
assign ftch[R_TX] = &fetch_cnt[3:0];
// fetch window
wire [4:0] g_offs[0:3];
// these values are from a thin air!!! recheck them occasionally!
assign g_offs[M_ZX] = 5'd18;
assign g_offs[M_HC] = 5'd6;
assign g_offs[M_XC] = 5'd4;
assign g_offs[M_TX] = 5'd10;
assign go_offs = g_offs[vmod];
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
// wire m_c = (vmod == M_HC) | (vmod == M_XC);
// assign fetch_sel = vmod == M_TX ? f_txt_sel[cnt_col[1:0]] : {~cptr, ~cptr, cptr | m_c, cptr | m_c};
// wire [3:0] f_sel[0:7];
wire [3:0] f_sel[0:3];
assign f_sel[M_ZX] = {~cptr, ~cptr, cptr, cptr};
assign f_sel[M_HC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_XC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_TX] = f_txt_sel[cnt_col[1:0]];
assign fetch_sel = f_sel[vmod];
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
// wire [1:0] f_bsl[0:7];
// assign f_bsl[M_ZX] = 2'b10;
// assign f_bsl[M_HC] = 2'b10;
// assign f_bsl[M_XC] = 2'b10;
// assign f_bsl[M_TX] = f_txt_bsl[cnt_col[1:0]];
// assign fetch_bsl = f_bsl[vmod];
wire [3:0] f_txt_sel[0:3];
assign f_txt_sel[1] = 4'b0011; // char
assign f_txt_sel[2] = 4'b1100; // attr
assign f_txt_sel[3] = 4'b0001; // gfx0
assign f_txt_sel[0] = 4'b0010; // gfx1
wire [1:0] f_txt_bsl[0:3];
assign f_txt_bsl[1] = 2'b10; // char
assign f_txt_bsl[2] = 2'b10; // attr
assign f_txt_bsl[3] = {2{cnt_row[0]}}; // gfx0
assign f_txt_bsl[0] = {2{cnt_row[0]}}; // gfx1
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
wire [4:0] bw[0:3];
assign bw[M_ZX] = {BW8, BU1}; // '1 of 8' (ZX)
assign bw[M_HC] = {BW4, BU1}; // '1 of 4' (16c)
assign bw[M_XC] = {BW2, BU1}; // '1 of 2' (256c)
assign bw[M_TX] = {BW8, BU4}; // '4 of 8' (text)
assign video_bw = bw[vmod];
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
// render mode
// wire [1:0] r_mode[0:7];
wire [1:0] r_mode[0:3];
assign r_mode[M_ZX] = R_ZX;
assign r_mode[M_HC] = R_HC;
assign r_mode[M_XC] = R_XC;
assign r_mode[M_TX] = R_TX;
assign render_mode = r_mode[vmod];
// raster resolution
wire [8:0] hp_beg[0:3];
wire [8:0] hp_end[0:3];
wire [8:0] vp_beg[0:3];
wire [8:0] vp_end[0:3];
wire [5:0] x_tile[0:3];
assign hp_beg[0] = 9'd136; // 256 (88-52-256-52)
assign hp_beg[1] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[2] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[3] = 9'd88; // 360 (88-0-360-0)
assign hp_end[0] = 9'd392; // 256
assign hp_end[1] = 9'd428; // 320
assign hp_end[2] = 9'd428; // 320
assign hp_end[3] = 9'd448; // 360
assign vp_beg[0] = 9'd080; // 192 (22-24-192-24)/(32-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = 9'd076; // 200 (22-20-200-20)/(32-44-200-44)
assign vp_beg[2] = 9'd056; // 240 (22-0-240-0)/(32-24-240-24)
assign vp_beg[3] = 9'd032; // 288 (22-0-240-0)/(32-0-288-0)
assign vp_end[0] = 9'd272; // 192
assign vp_end[1] = 9'd276; // 200
assign vp_end[2] = 9'd296; // 240
assign vp_end[3] = 9'd320; // 240/288
assign x_tile[0] = 6'd34; // 256
assign x_tile[1] = 6'd42; // 320
assign x_tile[2] = 6'd42; // 320
assign x_tile[3] = 6'd47; // 360
assign hpix_beg = hp_beg[rres];
assign hpix_end = hp_end[rres];
assign vpix_beg = vp_beg[rres];
assign vpix_end = vp_end[rres];
assign hpix_beg_ts = ts_rres_ext ? hp_beg[3] : hp_beg[rres];
assign hpix_end_ts = ts_rres_ext ? hp_end[3] : hp_end[rres];
assign vpix_beg_ts = ts_rres_ext ? vp_beg[3] : vp_beg[rres];
assign vpix_end_ts = ts_rres_ext ? vp_end[3] : vp_end[rres];
assign x_tiles = ts_rres_ext ? x_tile[3] : x_tile[rres];
// videomode addresses
wire [20:0] v_addr[0:3];
assign v_addr[M_ZX] = addr_zx;
assign v_addr[M_HC] = addr_16c;
assign v_addr[M_XC] = addr_256c;
assign v_addr[M_TX] = addr_text;
assign video_addr = v_addr[vmod];
// ZX
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr};
wire [11:0] addr_zx_gfx = {cnt_row[7:6], cnt_row[2:0], cnt_row[5:3], cnt_col[4:1]};
wire [11:0] addr_zx_atr = {3'b110, cnt_row[7:3], cnt_col[4:1]};
// 16c
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]};
// 256c
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7:0]};
// Textmode
wire [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]};
wire [13:0] addr_tx[0:3];
assign addr_tx[0] = {vpage[0], cnt_row[8:3], 1'b0, cnt_col[7:2]}; // char codes, data[15:0]
assign addr_tx[1] = {vpage[0], cnt_row[8:3], 1'b1, cnt_col[7:2]}; // char attributes, data[31:16]
assign addr_tx[2] = {~vpage[0], 3'b000, (txt_char[7:0]), cnt_row[2:1]}; // char0 graphics, data[7:0]
assign addr_tx[3] = {~vpage[0], 3'b000, (txt_char[15:8]), cnt_row[2:1]}; // char1 graphics, data[15:8]
endmodule

60
rtl/video/video_out.v Normal file
View File

@ -0,0 +1,60 @@
// This module generates video for DAC
// (c)2015 TSL
module video_out
(
// clocks
input wire clk, c3,
// video controls
input wire tv_blank,
input wire [1:0] plex_sel_in,
// mode controls
input wire tv_hires,
input wire [3:0] palsel,
// Z80 pins
input wire [15:0] cram_data_in,
input wire [7:0] cram_addr_in,
input wire cram_we,
// video data
input wire [7:0] vplex_in,
output wire [7:0] vred,
output wire [7:0] vgrn,
output wire [7:0] vblu,
output wire vdac_mode
);
reg [7:0] vplex;
always @(posedge clk) if (c3) vplex <= vplex_in;
wire [7:0] vdata = tv_hires ? {palsel, plex_sel_in[1] ? vplex[3:0] : vplex[7:4]} : vplex;
// CRAM
wire [15:0] vpixel;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/video/video_cram.mif")) video_cram
(
.clock (clk),
.address_a(cram_addr_in),
.data_a (cram_data_in),
.wren_a (cram_we),
.address_b(vdata),
.q_b (vpixel)
);
reg blank;
always @(posedge clk) blank <= tv_blank;
wire [14:0] vpix = blank ? 15'b0 : vpixel[14:0];
assign vred = {vpix[14:10], vpix[14:12]};
assign vgrn = {vpix[ 9: 5], vpix[ 9: 7]};
assign vblu = {vpix[ 4: 0], vpix[ 4: 2]};
assign vdac_mode = vpixel[15];
endmodule

149
rtl/video/video_ports.v Normal file
View File

@ -0,0 +1,149 @@
// This module latches all port parameters for video from Z80
module video_ports
(
// clocks
input wire clk,
input wire [ 7:0] d,
input wire res,
input wire int_start,
input wire line_start_s,
// port write strobes
input wire zborder_wr,
input wire border_wr,
input wire zvpage_wr,
input wire vpage_wr,
input wire vconf_wr,
input wire gx_offsl_wr,
input wire gx_offsh_wr,
input wire gy_offsl_wr,
input wire gy_offsh_wr,
input wire t0x_offsl_wr,
input wire t0x_offsh_wr,
input wire t0y_offsl_wr,
input wire t0y_offsh_wr,
input wire t1x_offsl_wr,
input wire t1x_offsh_wr,
input wire t1y_offsl_wr,
input wire t1y_offsh_wr,
input wire tsconf_wr,
input wire palsel_wr,
input wire tmpage_wr,
input wire t0gpage_wr,
input wire t1gpage_wr,
input wire sgpage_wr,
input wire hint_beg_wr ,
input wire vint_begl_wr,
input wire vint_begh_wr,
// video parameters
output reg [7:0] border,
output reg [7:0] vpage,
output reg [7:0] vconf,
output reg [8:0] gx_offs,
output reg [8:0] gy_offs,
output reg [8:0] t0x_offs,
output reg [8:0] t0y_offs,
output reg [8:0] t1x_offs,
output reg [8:0] t1y_offs,
output reg [7:0] palsel,
output reg [7:0] hint_beg,
output reg [8:0] vint_beg,
output reg [7:0] tsconf,
output reg [7:0] tmpage,
output reg [7:0] t0gpage,
output reg [7:0] t1gpage,
output reg [7:0] sgpage
);
reg [7:0] vpage_r;
reg [7:0] vconf_r;
reg [7:0] t0gpage_r;
reg [7:0] t1gpage_r;
reg [8:0] gx_offs_r;
reg [8:0] t0x_offs_r;
reg [8:0] t1x_offs_r;
reg [7:0] palsel_r;
wire [8:0] vint_beg_inc = vint_beg + vint_inc;
wire [8:0] vint_beg_next = {(vint_beg_inc[8:6] == 3'b101) ? 3'b0 : vint_beg_inc[8:6], vint_beg_inc[5:0]}; // if over 319 lines, decrement 320
reg [3:0] vint_inc;
always @(posedge clk) begin
if (res) begin
vint_beg <= 9'd0;
vint_inc <= 4'b0;
end
else if (vint_begl_wr) vint_beg[7:0] <= d;
else if (vint_begh_wr) begin
vint_beg[8] <= d[0];
vint_inc <= d[7:4];
end
else if (int_start) vint_beg <= vint_beg_next;
end
always @(posedge clk) begin
if (res) begin
vpage_r <= 8'h05;
vconf_r <= 8'h00;
gx_offs_r <= 9'b0;
palsel_r <= 8'h0F;
gy_offs <= 9'b0;
tsconf <= 8'b0;
hint_beg <= 8'd1;
end
else begin
if (zborder_wr ) border <= {palsel[3:0], 1'b0, d[2:0]};
if (border_wr ) border <= d;
if (gy_offsl_wr ) gy_offs[7:0] <= d;
if (gy_offsh_wr ) gy_offs[8] <= d[0];
if (t0y_offsl_wr) t0y_offs[7:0] <= d;
if (t0y_offsh_wr) t0y_offs[8] <= d[0];
if (t1y_offsl_wr) t1y_offs[7:0] <= d;
if (t1y_offsh_wr) t1y_offs[8] <= d[0];
if (tsconf_wr ) tsconf <= d;
if (tmpage_wr ) tmpage <= d;
if (sgpage_wr ) sgpage <= d;
if (hint_beg_wr ) hint_beg <= d;
if (zvpage_wr ) vpage_r <= {6'b000001, d[3], 1'b1};
if (vpage_wr ) vpage_r <= d;
if (vconf_wr ) vconf_r <= d;
if (gx_offsl_wr ) gx_offs_r[7:0] <= d;
if (gx_offsh_wr ) gx_offs_r[8] <= d[0];
if (palsel_wr ) palsel_r <= d;
if (t0x_offsl_wr) t0x_offs_r[7:0] <= d;
if (t0x_offsh_wr) t0x_offs_r[8] <= d[0];
if (t1x_offsl_wr) t1x_offs_r[7:0] <= d;
if (t1x_offsh_wr) t1x_offs_r[8] <= d[0];
if (t0gpage_wr ) t0gpage_r <= d;
if (t1gpage_wr ) t1gpage_r <= d;
end
end
// latching regs at line start, delaying hires for 1 line
always @(posedge clk) begin
if (res)
begin
vpage <= 8'h05;
vconf <= 8'h00;
gx_offs <= 9'b0;
palsel <= 8'h0F;
end
else if (zvpage_wr) vpage <= {6'b000001, d[3], 1'b1};
else if (line_start_s) begin
vpage <= vpage_r;
vconf <= vconf_r;
gx_offs <= gx_offs_r;
palsel <= palsel_r;
t0x_offs <= t0x_offs_r;
t1x_offs <= t1x_offs_r;
t0gpage <= t0gpage_r;
t1gpage <= t1gpage_r;
end
end
endmodule

84
rtl/video/video_render.v Normal file
View File

@ -0,0 +1,84 @@
// This module renders video data for output
module video_render
(
// clocks
input wire clk, c1,
// video controls
input wire hvpix,
input wire hvtspix,
input wire nogfx,
input wire notsu,
input wire gfxovr,
input wire flash,
input wire hires,
input wire [3:0] psel,
input wire [3:0] palsel,
// mode controls
input wire [1:0] render_mode,
// video data
input wire [31:0] data,
input wire [ 7:0] border_in,
input wire [ 7:0] tsdata_in,
output wire [ 7:0] vplex_out
);
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// ZX graphics
wire [15:0] zx_gfx = data[15: 0];
wire [15:0] zx_atr = data[31:16];
wire zx_dot = zx_gfx[{psel[3], ~psel[2:0]}];
wire [7:0] zx_attr = ~psel[3] ? zx_atr[7:0] : zx_atr[15:8];
wire [7:0] zx_pix = {palsel, zx_attr[6], zx_dot ^ (flash & zx_attr[7]) ? zx_attr[2:0] : zx_attr[5:3]};
// text graphics
// (it uses common renderer with ZX, but different attributes)
wire [7:0] tx_pix = {palsel, zx_dot ? zx_attr[3:0] : zx_attr[7:4]};
// 16c graphics
wire [3:0] hc_dot[0:3];
assign hc_dot[0] = data[ 7: 4];
assign hc_dot[1] = data[ 3: 0];
assign hc_dot[2] = data[15:12];
assign hc_dot[3] = data[11: 8];
wire [7:0] hc_pix = {palsel, hc_dot[psel[1:0]]};
// 256c graphics
wire [7:0] xc_dot[0:1];
assign xc_dot[0] = data[ 7: 0];
assign xc_dot[1] = data[15: 8];
wire [7:0] xc_pix = xc_dot[psel[0]];
// mode selects
wire [7:0] pix[0:3];
assign pix[R_ZX] = zx_pix; // ZX
assign pix[R_HC] = hc_pix; // 16c
assign pix[R_XC] = xc_pix; // 256c
assign pix[R_TX] = tx_pix; // text
wire pixv[0:3];
assign pixv[R_ZX] = zx_dot ^ (flash & zx_attr[7]);
assign pixv[R_HC] = |hc_dot[psel[1:0]];
assign pixv[R_XC] = |xc_dot[psel[0]];
assign pixv[R_TX] = zx_dot;
// video plex muxer
wire tsu_visible = (|tsdata_in[3:0] && !notsu);
wire gfx_visible = (pixv[render_mode] && !nogfx);
wire [7:0] video1 = tsu_visible ? tsdata_in : (nogfx ? border_in : pix[render_mode]);
wire [7:0] video2 = gfx_visible ? pix[render_mode] : (tsu_visible ? tsdata_in : border_in);
wire [7:0] video = hvpix ? (gfxovr ? video2 : video1) : ((hvtspix && tsu_visible) ? tsdata_in : border_in);
assign vplex_out = hires ? {temp, video[3:0]} : video; // in hi-res plex contains two pixels 4 bits each
reg [3:0] temp;
always @(posedge clk) if (c1) temp <= video[3:0];
endmodule

169
rtl/video/video_sync.v Normal file
View File

@ -0,0 +1,169 @@
// This module generates all video raster signals
module video_sync
(
// clocks
input wire clk, f1, c0, c1, c3, pix_stb,
// video parameters
input wire [8:0] hpix_beg,
input wire [8:0] hpix_end,
input wire [8:0] vpix_beg,
input wire [8:0] vpix_end,
input wire [8:0] hpix_beg_ts,
input wire [8:0] hpix_end_ts,
input wire [8:0] vpix_beg_ts,
input wire [8:0] vpix_end_ts,
input wire [4:0] go_offs,
input wire [1:0] x_offs,
input wire [7:0] hint_beg,
input wire [8:0] vint_beg,
input wire [7:0] cstart,
input wire [8:0] rstart,
// video syncs
output reg hsync,
output reg vsync,
// video controls
input wire nogfx,
output wire v_pf,
output wire hpix,
output wire vpix,
output wire v_ts,
output wire hvpix,
output wire hvtspix,
output wire tv_hblank,
output wire tv_vblank,
output wire frame_start,
output wire line_start_s,
output wire pix_start,
output wire ts_start,
output wire frame,
output wire flash,
// video counters
output wire [8:0] ts_raddr,
output reg [8:0] lcount,
output reg [7:0] cnt_col,
output reg [8:0] cnt_row,
output reg cptr,
output reg [3:0] scnt,
// DRAM
input wire video_pre_next,
output reg video_go,
// ZX controls
input wire y_offs_wr,
output wire int_start
);
localparam HSYNC_BEG = 9'd11;
localparam HSYNC_END = 9'd43;
localparam HBLNK_BEG = 9'd00;
localparam HBLNK_END = 9'd88;
localparam HSYNCV_BEG = 9'd5;
localparam HSYNCV_END = 9'd31;
localparam HBLNKV_END = 9'd42;
localparam HPERIOD = 9'd448;
localparam VSYNC_BEG = 9'd08;
localparam VSYNC_END = 9'd11;
localparam VBLNK_BEG = 9'd00;
localparam VBLNK_END = 9'd32;
localparam VPERIOD = 9'd320;
// counters
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3) hcount <= line_start ? 9'b0 : hcount + 9'b1;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s) vcount <= (vcount == (VPERIOD - 1)) ? 9'b0 : vcount + 9'b1;
// column address for DRAM
always @(posedge clk) begin
if (line_start2) begin
cnt_col <= cstart;
cptr <= 1'b0;
end
else if (video_pre_next) begin
cnt_col <= cnt_col + 8'b1;
cptr <= ~cptr;
end
end
// row address for DRAM
always @(posedge clk) begin if (c3)
if (vis_start || (line_start && y_offs_wr_r)) cnt_row <= rstart;
else if (line_start && vpix) cnt_row <= cnt_row + 9'b1;
end
// pixel counter
always @(posedge clk) if (pix_stb) scnt <= pix_start ? 4'b0 : scnt + 4'b1; // f1 or c3
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
always @(posedge clk) if (ts_start_coarse) lcount <= vcount - vpix_beg_ts + 9'b1;
// Y offset re-latch trigger
reg y_offs_wr_r;
always @(posedge clk) begin
if (y_offs_wr) y_offs_wr_r <= 1'b1;
else if (line_start_s) y_offs_wr_r <= 1'b0;
end
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
always @(posedge clk) begin
if (frame_start && c3) begin
flash_ctr <= flash_ctr + 5'b1;
end
end
// sync strobes
wire hs = (hcount >= HSYNC_BEG) && (hcount < HSYNC_END);
wire vs = (vcount >= VSYNC_BEG) && (vcount < VSYNC_END);
assign tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
assign tv_vblank = (vcount >= VBLNK_BEG) && (vcount < VBLNK_END);
assign hvpix = hpix && vpix;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
assign hvtspix = htspix && vtspix;
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts);
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts);
assign v_ts = (vcount >= (vpix_beg_ts - 1)) && (vcount < (vpix_end_ts - 1)); // vertical TS window
assign v_pf = (vcount >= (vpix_beg_ts - 17)) && (vcount < (vpix_end_ts - 9)); // vertical tilemap prefetch window
always @(posedge clk) video_go <= (hcount >= (hpix_beg - go_offs - x_offs)) && (hcount < (hpix_end - go_offs - x_offs + 4)) && vpix && !nogfx;
wire line_start = hcount == (HPERIOD - 1);
assign line_start_s = line_start && c3;
wire line_start2 = hcount == (HSYNC_END - 1);
assign frame_start = line_start && (vcount == (VPERIOD - 1));
wire vis_start = line_start && (vcount == (VBLNK_END - 1));
assign pix_start = hcount == (hpix_beg - x_offs - 1);
wire ts_start_coarse = hcount == (hpix_beg_ts - 1);
assign ts_start = c3 && ts_start_coarse;
assign int_start = (hcount == {hint_beg, 1'b0}) && (vcount == vint_beg) && c0;
always @(posedge clk) begin
hsync <= hs;
vsync <= vs;
end
endmodule

467
rtl/video/video_top.v Normal file
View File

@ -0,0 +1,467 @@
// This module is a video top-level
module video_top
(
// clocks
input wire clk,
input wire f0, f1,
input wire h0, h1,
input wire c0, c1, c2, c3,
// input wire t0, // debug!!!
// video DAC
output wire [7:0] vred,
output wire [7:0] vgrn,
output wire [7:0] vblu,
output wire vdac_mode,
// video syncs
output wire hsync,
output wire vsync,
output wire hblank,
output wire vblank,
output wire pix_stb,
// Z80 controls
input wire [ 7:0] d,
input wire [15:0] zmd,
input wire [ 7:0] zma,
input wire cram_we,
input wire sfile_we,
// port write strobes
input wire zborder_wr,
input wire border_wr,
input wire zvpage_wr,
input wire vpage_wr,
input wire vconf_wr,
input wire gx_offsl_wr,
input wire gx_offsh_wr,
input wire gy_offsl_wr,
input wire gy_offsh_wr,
input wire t0x_offsl_wr,
input wire t0x_offsh_wr,
input wire t0y_offsl_wr,
input wire t0y_offsh_wr,
input wire t1x_offsl_wr,
input wire t1x_offsh_wr,
input wire t1y_offsl_wr,
input wire t1y_offsh_wr,
input wire tsconf_wr,
input wire palsel_wr,
input wire tmpage_wr,
input wire t0gpage_wr,
input wire t1gpage_wr,
input wire sgpage_wr,
input wire hint_beg_wr ,
input wire vint_begl_wr,
input wire vint_begh_wr,
// ZX controls
input wire res,
output wire int_start,
output wire line_start_s,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw,
output wire video_go,
input wire [15:0] dram_rdata, // raw, should be latched by c2 (video_next)
input wire video_next,
input wire video_pre_next,
input wire next_video,
input wire video_strobe,
output wire [20:0] ts_addr,
output wire ts_req,
input wire ts_pre_next,
input wire ts_next,
output wire [20:0] tm_addr,
output wire tm_req,
input wire tm_next
);
// video config
wire [7:0] vpage; // re-latched at line_start
wire [7:0] vconf; //
wire [8:0] gx_offs; //
wire [8:0] gy_offs; //
wire [7:0] palsel; //
wire [8:0] t0x_offs; //
wire [8:0] t1x_offs; //
wire [7:0] t0gpage; //
wire [7:0] t1gpage; //
wire [7:0] sgpage; // * not yet !!!
wire [8:0] t0y_offs;
wire [8:0] t1y_offs;
wire [7:0] tsconf;
wire [7:0] tmpage;
wire [7:0] hint_beg;
wire [8:0] vint_beg;
wire [8:0] hpix_beg;
wire [8:0] hpix_end;
wire [8:0] vpix_beg;
wire [8:0] vpix_end;
wire [8:0] hpix_beg_ts;
wire [8:0] hpix_end_ts;
wire [8:0] vpix_beg_ts;
wire [8:0] vpix_end_ts;
wire [5:0] x_tiles;
wire [9:0] x_offs_mode;
wire [4:0] go_offs;
wire [1:0] render_mode;
wire tv_hires;
wire nogfx = vconf[5];
wire notsu = vconf[4];
wire gfxovr = vconf[3];
wire tv_hblank;
wire tv_vblank;
// counters
wire [7:0] cnt_col;
wire [8:0] cnt_row;
wire cptr;
wire [3:0] scnt;
wire [8:0] lcount;
// synchro
wire frame_start;
wire pix_start;
wire tv_pix_start;
wire ts_start;
wire v_ts;
wire v_pf;
wire hpix;
wire vpix;
wire hvpix;
wire hvtspix;
wire flash;
// fetcher
wire [31:0] fetch_data;
wire [31:0] fetch_temp;
wire [3:0] fetch_sel;
wire [1:0] fetch_bsl;
wire fetch_stb;
// video data
wire [7:0] border;
wire [7:0] vplex;
// TS
wire tsr_go;
wire [5:0] tsr_addr;
wire [8:0] tsr_line;
wire [7:0] tsr_page;
wire [8:0] tsr_x;
wire [2:0] tsr_xs;
wire tsr_xf;
wire [3:0] tsr_pal;
wire tsr_rdy;
// TS-line
wire [8:0] ts_waddr;
wire [7:0] ts_wdata;
wire ts_we;
wire [8:0] ts_raddr;
video_ports video_ports
(
.clk (clk),
.d (d),
.res (res),
.line_start_s (line_start_s),
.border_wr (border_wr),
.zborder_wr (zborder_wr),
.zvpage_wr (zvpage_wr),
.vpage_wr (vpage_wr),
.vconf_wr (vconf_wr),
.gx_offsl_wr (gx_offsl_wr),
.gx_offsh_wr (gx_offsh_wr),
.gy_offsl_wr (gy_offsl_wr),
.gy_offsh_wr (gy_offsh_wr),
.t0x_offsl_wr (t0x_offsl_wr),
.t0x_offsh_wr (t0x_offsh_wr),
.t0y_offsl_wr (t0y_offsl_wr),
.t0y_offsh_wr (t0y_offsh_wr),
.t1x_offsl_wr (t1x_offsl_wr),
.t1x_offsh_wr (t1x_offsh_wr),
.t1y_offsl_wr (t1y_offsl_wr),
.t1y_offsh_wr (t1y_offsh_wr),
.palsel_wr (palsel_wr),
.hint_beg_wr (hint_beg_wr),
.vint_begl_wr (vint_begl_wr),
.vint_begh_wr (vint_begh_wr),
.tsconf_wr (tsconf_wr),
.tmpage_wr (tmpage_wr),
.t0gpage_wr (t0gpage_wr),
.t1gpage_wr (t1gpage_wr),
.sgpage_wr (sgpage_wr),
.border (border),
.vpage (vpage),
.vconf (vconf),
.gx_offs (gx_offs),
.gy_offs (gy_offs),
.t0x_offs (t0x_offs),
.t1x_offs (t1x_offs),
.t0y_offs (t0y_offs),
.t1y_offs (t1y_offs),
.palsel (palsel),
.hint_beg (hint_beg),
.vint_beg (vint_beg),
.int_start (0),
.tsconf (tsconf),
.tmpage (tmpage),
.t0gpage (t0gpage),
.t1gpage (t1gpage),
.sgpage (sgpage)
);
video_mode video_mode
(
.clk (clk),
.f1 (f1),
.c3 (c3),
.vpage (vpage),
.vconf (vconf),
.fetch_sel (fetch_sel),
.fetch_bsl (fetch_bsl),
.fetch_cnt (scnt),
.fetch_stb (fetch_stb),
.txt_char (fetch_temp[15:0]),
.gx_offs (gx_offs),
.x_offs_mode (x_offs_mode),
.ts_rres_ext (tsconf[0]),
.hpix_beg (hpix_beg),
.hpix_end (hpix_end),
.vpix_beg (vpix_beg),
.vpix_end (vpix_end),
.hpix_beg_ts (hpix_beg_ts),
.hpix_end_ts (hpix_end_ts),
.vpix_beg_ts (vpix_beg_ts),
.vpix_end_ts (vpix_end_ts),
.x_tiles (x_tiles),
.go_offs (go_offs),
.cnt_col (cnt_col),
.cnt_row (cnt_row),
.cptr (cptr),
.line_start_s (line_start_s),
.pix_start (pix_start),
.tv_hires (tv_hires),
.pix_stb (pix_stb),
.render_mode (render_mode),
.video_addr (video_addr),
.video_bw (video_bw)
);
video_sync video_sync
(
.clk (clk),
.f1 (f1),
.c0 (c0),
.c1 (c1),
.c3 (c3),
.hpix_beg (hpix_beg),
.hpix_end (hpix_end),
.vpix_beg (vpix_beg),
.vpix_end (vpix_end),
.hpix_beg_ts (hpix_beg_ts),
.hpix_end_ts (hpix_end_ts),
.vpix_beg_ts (vpix_beg_ts),
.vpix_end_ts (vpix_end_ts),
.go_offs (go_offs),
.x_offs (x_offs_mode[1:0]),
.y_offs_wr (gy_offsl_wr || gy_offsh_wr),
.line_start_s (line_start_s),
.hint_beg (hint_beg),
.vint_beg (vint_beg),
.hsync (hsync),
.vsync (vsync),
.tv_hblank (tv_hblank),
.tv_vblank (tv_vblank),
.ts_raddr (ts_raddr),
.lcount (lcount),
.cnt_col (cnt_col),
.cnt_row (cnt_row),
.cptr (cptr),
.scnt (scnt),
.flash (flash),
.pix_stb (pix_stb),
.pix_start (pix_start),
.ts_start (ts_start),
.cstart (x_offs_mode[9:2]),
.rstart (gy_offs),
.frame_start (frame_start),
.int_start (int_start),
.v_pf (v_pf),
.hpix (hpix),
.v_ts (v_ts),
.vpix (vpix),
.hvpix (hvpix),
.hvtspix (hvtspix),
.nogfx (nogfx),
.video_go (video_go),
.video_pre_next(video_pre_next)
);
video_fetch video_fetch
(
.clk (clk),
.f_sel (fetch_sel),
.b_sel (fetch_bsl),
.fetch_stb (fetch_stb),
.fetch_data (fetch_data),
.fetch_temp (fetch_temp),
.video_strobe (video_strobe),
.video_data (dram_rdata)
);
video_ts video_ts
(
.clk (clk),
.start (ts_start),
.line (lcount),
.v_ts (v_ts),
.tsconf (tsconf),
.t0gpage (t0gpage),
.t1gpage (t1gpage),
.sgpage (sgpage),
.tmpage (tmpage),
.num_tiles (x_tiles),
.v_pf (v_pf),
.t0x_offs (t0x_offs),
.t1x_offs (t1x_offs),
.t0y_offs (t0y_offs),
.t1y_offs (t1y_offs),
.t0_palsel (palsel[5:4]),
.t1_palsel (palsel[7:6]),
.dram_addr (tm_addr),
.dram_req (tm_req),
.dram_next (tm_next),
.dram_rdata (dram_rdata),
.tsr_go (tsr_go),
.tsr_addr (tsr_addr),
.tsr_line (tsr_line),
.tsr_page (tsr_page),
.tsr_pal (tsr_pal),
.tsr_x (tsr_x),
.tsr_xs (tsr_xs),
.tsr_xf (tsr_xf),
.tsr_rdy (tsr_rdy),
.sfile_addr_in (zma),
.sfile_data_in (zmd),
.sfile_we (sfile_we)
);
video_ts_render video_ts_render
(
.clk (clk),
.reset (ts_start),
.tsr_go (tsr_go),
.addr (tsr_addr),
.line (tsr_line),
.page (tsr_page),
.pal (tsr_pal),
.x_coord (tsr_x),
.x_size (tsr_xs),
.flip (tsr_xf),
.mem_rdy (tsr_rdy),
.ts_waddr (ts_waddr),
.ts_wdata (ts_wdata),
.ts_we (ts_we),
.dram_addr (ts_addr),
.dram_req (ts_req),
.dram_pre_next (ts_pre_next),
.dram_next (ts_next),
.dram_rdata (dram_rdata)
);
video_render video_render
(
.clk (clk),
.c1 (c1),
.hvpix (hvpix),
.hvtspix (hvtspix),
.nogfx (nogfx),
.notsu (notsu),
.gfxovr (gfxovr),
.flash (flash),
.hires (tv_hires),
.psel (scnt),
.palsel (palsel[3:0]),
.render_mode (render_mode),
.data (fetch_data),
.border_in (border),
.tsdata_in (ts_rdata),
.vplex_out (vplex)
);
video_out video_out
(
.clk (clk),
.c3 (c3),
.tv_blank (tv_hblank|tv_vblank),
.palsel (palsel[3:0]),
.plex_sel_in ({h1, f1}),
.tv_hires (tv_hires),
.cram_addr_in (zma),
.cram_data_in (zmd[15:0]),
.cram_we (cram_we),
.vplex_in (vplex),
.vred (vred),
.vgrn (vgrn),
.vblu (vblu),
.vdac_mode (vdac_mode)
);
assign hblank = tv_hblank;
assign vblank = tv_vblank;
// 2 buffers: 512 pixels * 8 bits (9x8) - used as bitmap buffer for TS overlay over graphics
// (2 altdprams)
wire tl_act0 = lcount[0];
wire tl_act1 = ~lcount[0];
wire [8:0] ts_waddr0 = tl_act0 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata0 = tl_act0 ? 8'd0 : ts_wdata;
wire ts_we0 = tl_act0 ? c3 : ts_we;
wire [8:0] ts_waddr1 = tl_act1 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata1 = tl_act1 ? 8'd0 : ts_wdata;
wire ts_we1 = tl_act1 ? c3 : ts_we;
wire [7:0] ts_rdata = tl_act0 ? ts_rdata0 : ts_rdata1;
wire [7:0] ts_rdata0, ts_rdata1;
dpram #(.ADDRWIDTH(9)) video_tsline0
(
.clock (clk),
.address_a (ts_waddr0),
.data_a (ts_wdata0),
.wren_a (ts_we0),
.address_b (ts_raddr),
.q_b (ts_rdata0)
);
dpram #(.ADDRWIDTH(9)) video_tsline1
(
.clock (clk),
.address_a (ts_waddr1),
.data_a (ts_wdata1),
.wren_a (ts_we1),
.address_b (ts_raddr),
.q_b (ts_rdata1)
);
endmodule

379
rtl/video/video_ts.v Normal file
View File

@ -0,0 +1,379 @@
// This is the Tile Sprite Processing Unit
// Tiles map address:
// bits desc
// 20:13 tmpage
// 12:
// Graphics address:
// bits desc
// 20:13 Xgpage
// 15:7 line (bits 15:13 are added) - 512 lines
// 6:0 word within line - 128 words = 512 pixels
module video_ts
(
// clocks
input wire clk,
// video controls
input wire start,
input wire [8:0] line, // = vcount - vpix_beg + 9'b1;
input wire v_ts,
input wire v_pf, // vertical tilemap prefetch window
// video config
input wire [7:0] tsconf,
input wire [7:0] t0gpage,
input wire [7:0] t1gpage,
input wire [7:0] sgpage,
input wire [7:0] tmpage,
input wire [5:0] num_tiles,
input wire [8:0] t0x_offs,
input wire [8:0] t1x_offs,
input wire [8:0] t0y_offs,
input wire [8:0] t1y_offs,
input wire [1:0] t0_palsel,
input wire [1:0] t1_palsel,
// SFYS interface
input wire [7:0] sfile_addr_in,
input wire [15:0] sfile_data_in,
input wire sfile_we,
// renderer interface
output wire tsr_go,
output wire [5:0] tsr_addr, // graphics address within the line
output wire [8:0] tsr_line, // bitmap line
output wire [7:0] tsr_page, // bitmap 1st page
output wire [8:0] tsr_x, // addr in buffer (0-359 visibles)
output wire [2:0] tsr_xs, // size (8-64 pix)
output wire tsr_xf, // X flip
output wire [3:0] tsr_pal, // palette
input wire tsr_rdy, // renderer is done and ready to receive a new task
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire dram_next,
input wire [15:0] dram_rdata
);
// config
wire s_en = tsconf[7];
wire t1_en = tsconf[6];
wire t0_en = tsconf[5];
wire t1z_en = tsconf[3];
wire t0z_en = tsconf[2];
// TS renderer interface
assign tsr_go = sprite_go || tile_go;
assign tsr_x = sprites ? sprites_x : tile_x;
assign tsr_xs = sprites ? sprites_xs : 3'd0;
assign tsr_xf = sprites ? sprites_xf : t_xflp;
assign tsr_page = sprites ? sgpage : tile_page;
assign tsr_line = sprites ? sprites_line : tile_line;
assign tsr_addr = sprites ? sprites_addr : tile_addr;
assign tsr_pal = sprites ? s_pal : tile_pal;
// Layer selectors control
localparam LAYERS = 6; // Total number of layers to process
localparam TM = 0; // Individual layers
localparam S0 = 1;
localparam T0 = 2;
localparam S1 = 3;
localparam T1 = 4;
localparam S2 = 5;
wire tmap = layer_active[TM];
wire sprites = layer_active[S0] || layer_active[S1] || layer_active[S2];
wire tiles = layer_active[T0] || layer_active[T1];
reg [LAYERS-1:0] layer;
always @(posedge clk)
if (start)
layer <= 1;
else if (|(layer & layer_skip))
layer <= {layer[LAYERS-2:0], 1'b0};
reg [LAYERS-1:0] layer_active;
always @(posedge clk)
if (start)
layer_active <= 0;
else
layer_active <= layer & ~layer_skip;
wire [LAYERS-1:0] layer_enabled = {s_en, t1_en, s_en, t0_en, s_en, t1_en || t0_en};
wire [LAYERS-1:0] layer_allowed = {{5{v_ts}}, v_pf};
wire [LAYERS-1:0] layer_end = {spr_end[2], tile_end[1], spr_end[1], tile_end[0], spr_end[0], tm_end};
reg [LAYERS-1:0] layer_skip;
always @(posedge clk)
if (start)
layer_skip <= ~(layer_enabled & layer_allowed);
else
layer_skip <= layer_skip | layer_end;
// --- Tile map prefetch ---
// DRAM controls
assign dram_addr = {tmpage, tm_b_row, tm_layer, tm_b_line, tm_num}; // 20:13 - page, 12:7 - row, 6 - layer, 5:0 - column (burst number : number in burst)
assign dram_req = tmap;
// TMB control
wire [8:0] tmb_waddr = {tm_line[4:3], tm_b_line, tm_num, tm_layer}; // 8:7 - buffer #, 6:4 - burst number (of 8 bursts), 3:1 - number in burst (8 tiles per burst), 0 - layer
wire [8:0] tm_line = line + 9'd16;
wire [2:0] tm_b_line = tm_line[2:0];
wire [5:0] tm_b_row = tm_line[8:3] + (tm_layer ? t1y_offs[8:3] : t0y_offs[8:3]);
wire [2:0] tm_num = tm_x[2:0];
wire tm_layer = tm_x[3];
// internal layers control
wire tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
reg [1:0] m_layer;
always @(posedge clk)
if (start)
m_layer <= 2'b1;
else if (tm_end)
m_layer <= {m_layer[0], 1'b0};
// tilemap X coordinate
reg [4:0] tm_x;
always @(posedge clk)
if (start)
tm_x <= t0_en ? 5'd0 : 5'd8;
else if (tm_next)
tm_x <= tm_x + 5'd1;
// --- Tiles ---
// tile descriptor fields
wire [11:0] t_tnum = tmb_rdata[11:0];
wire [1:0] t_pal = tmb_rdata[13:12];
wire t_xflp = tmb_rdata[14];
wire t_yflp = tmb_rdata[15];
// TSR control
wire [7:0] tile_page = t_sel ? t0gpage : t1gpage;
wire [8:0] tile_line = {t_tnum[11:6], (t_line[2:0] ^ {3{t_yflp}})};
wire [5:0] tile_addr = t_tnum[5:0];
wire [8:0] tile_x = {(tx - 6'd1), 3'd0} - tx_offs[2:0];
wire [3:0] tile_pal = {t_sel ? t0_palsel : t1_palsel, t_pal};
// TMB control
wire [8:0] tmb_raddr = {t_line[4:3], tx + tx_offs[8:3], ~t_sel};
// layer parameter selectors
wire [8:0] tx_offs = t_sel ? t0x_offs : t1x_offs;
wire [3:0] ty_offs = t_sel ? t0y_offs[2:0] : t1y_offs[2:0];
wire t_sel = t_layer[0];
// internal layers control
wire [1:0] tile_end = {2{t_layer_end}} & t_layer[1:0];
wire t_layer_end = tx == num_tiles;
wire t_layer_start = start || t_layer_end;
reg [1:0] t_layer;
always @(posedge clk)
if (start)
t_layer <= t0_en ? 2'b01 : 2'b10;
else if (t_layer_end)
t_layer <= {t_layer[0], 1'b0};
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
// tile_skip tx+1 -
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
wire tile_go = tile_good && tsr_allowed;
wire tile_wait = tile_good && !tsr_allowed;
wire tile_good = tm_valid && tile_valid;
wire tile_skip = tm_valid && !tile_valid;
wire tsr_allowed = tiles && tsr_rdy;
wire tile_valid = |t_tnum || (t_sel ? t0z_en : t1z_en);
wire tm_pre_valid = tm_valid_r[0];
wire tm_valid = tm_valid_r[1];
reg [1:0] tm_valid_r;
always @(posedge clk)
if (t_layer_start || tile_wait)
tm_valid_r <= TM_PRE_VALID;
else if (tm_pre_valid || tile_go)
tm_valid_r <= TM_VALID;
reg [5:0] tx;
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
tx <= tx + 6'd1;
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
wire [4:0] t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [8:0] s_ycrd = sfile_rdata[8:0];
wire [2:0] s_ysz = sfile_rdata[11:9];
wire s_act = sfile_rdata[13];
wire s_leap = sfile_rdata[14];
wire s_yflp = sfile_rdata[15];
// R1
wire [8:0] s_xcrd = sfile_rdata[8:0];
wire [2:0] s_xsz = sfile_rdata[11:9];
wire s_xflp = sfile_rdata[15];
// R2
wire [11:0] s_tnum = sfile_rdata[11:0];
wire [3:0] s_pal = sfile_rdata[15:12];
// TSR control
reg [8:0] sprites_x;
reg [2:0] sprites_xs;
reg sprites_xf;
wire [5:0] sprites_addr = s_tnum[5:0];
// internal layers control
wire [2:0] spr_end = ({3{s_layer_end}} & s_layer[2:0]) | {3{sprites_last}};
wire s_layer_end = (sr0_valid && !spr_valid && s_leap) || (sprite_go && s_leap_r);
wire sprites_last = sreg == 8'd255;
reg [2:0] s_layer;
always @(posedge clk)
if (start)
s_layer <= 3'b1;
else if (s_layer_end)
s_layer <= {s_layer[1:0], 1'b0};
// SFile registers control
// condition write to sreg write to sr_valid action
// start 0 SR0_PRE_VALID Start
// sr0_pre_valid sreg+3 SR0_VALID SR0 pre-read
// sr0_valid && !spr_valid sreg+3 - Skip sprite
// sr0_valid && spr_valid sreg-2 SR1_PRE_VALID SR1 pre-read
// sr1_pre_valid sreg+1 SR1_VALID SR1 read
// sr1_valid sreg+1 SR2_VALID SR2 pre-read
// sr2_valid && !tsr_rdy - - Wait for TSR ready
// sr2_valid && tsr_rdy sreg+1 SR0_PRE_VALID Next sprite
// sprites_last - NONE_VALID End
localparam NONE_VALID = 5'b00000;
localparam SR0_PRE_VALID = 5'b00001;
localparam SR0_VALID = 5'b00010;
localparam SR1_PRE_VALID = 5'b00100;
localparam SR1_VALID = 5'b01000;
localparam SR2_VALID = 5'b10000;
wire sprite_go = sr2_valid && sprites && tsr_rdy; // kick to renderer
wire spr_valid = s_visible && s_act;
wire sr0_pre_valid = sr_valid[0];
wire sr0_valid = sr_valid[1];
wire sr1_pre_valid = sr_valid[2];
wire sr1_valid = sr_valid[3];
wire sr2_valid = sr_valid[4];
reg [4:0] sr_valid;
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
sr_valid <= NONE_VALID;
else if (sr0_pre_valid)
sr_valid <= SR0_VALID;
else if (sr0_valid && spr_valid)
sr_valid <= SR1_PRE_VALID;
else if (sr1_pre_valid)
sr_valid <= SR1_VALID;
else if (sr1_valid)
sr_valid <= SR2_VALID;
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
reg [7:0] sreg;
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
sreg <= sreg + 8'd3;
else if (sr0_valid)
sreg <= spr_valid ? (sreg - 8'd2) : (sreg + 8'd3);
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// SFile control
reg [5:0] s_bmline_offset_r;
reg s_leap_r;
always @(posedge clk) begin
if (sr0_valid)
begin
s_leap_r <= s_leap;
s_bmline_offset_r <= s_bmline_offset;
end
if (sr1_valid)
begin
sprites_x <= s_xcrd;
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
end
// sprite Y geometry
wire [8:0] s_line = line - s_ycrd; // visible line of sprite in current video line
wire s_visible = (s_line <= s_ymax); // check if sprite line is within Y size
wire [5:0] s_ymax = {s_ysz, 3'b111};
wire [8:0] sprites_line = {s_tnum[11:6], 3'b0} + s_bmline_offset_r;
wire [5:0] s_bmline_offset = s_yflp ? (s_ymax - s_line[5:0]) : s_line[5:0];
// SFile
wire [15:0] sfile_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) video_sfile
(
.clock (clk),
.address_a (sfile_addr_in),
.data_a (sfile_data_in),
.wren_a (sfile_we),
.address_b (sreg),
.q_b (sfile_rdata)
);
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams)
wire [15:0] tmb_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf
(
.clock (clk),
.address_a (tmb_waddr),
.data_a (dram_rdata),
.wren_a (tm_next),
.address_b (tmb_raddr),
.q_b (tmb_rdata)
);
endmodule

150
rtl/video/video_ts_render.v Normal file
View File

@ -0,0 +1,150 @@
// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
// Inputs (only valid by 'tsr_go'):
// - DRAM address of graphics data (including page, line, word)
// - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max)
// - X coordinate
// - X flip bit
// - palette selector
// Address in TS-line is calculated from X coordinate respecting the X flip.
// Inc/dec direction is set automatically.
// At the 'c2' of last DRAM cycle 'mem_rdy' is asserted.
// It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation.
// First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only
// after TS request recognized and processed by DRAM controller.
// It is recommended to assert 'tsr_go' at 'c2'.
module video_ts_render
(
// clocks
input wire clk,
// controls
input wire reset, // line start strobe, inits the machine
input wire [ 8:0] x_coord, // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size'
input wire [ 2:0] x_size, // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle)
input wire flip, // indicates that sprite is X-flipped
input wire tsr_go, // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process
input wire [ 5:0] addr, // address of dword within the line (dword = 8 pix)
input wire [ 8:0] line, // line of bitmap
input wire [ 7:0] page, // 1st page of bitmap (TxGpage or SGpage)
input wire [ 3:0] pal, // palette selector, bits[7:4] of CRAM address
output wire mem_rdy, // ready to receive new task
// TS-Line interface
output reg [ 8:0] ts_waddr,
output wire [ 7:0] ts_wdata,
output wire ts_we,
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire [15:0] dram_rdata,
input wire dram_pre_next,
input wire dram_next
);
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
// DRAM addressing
assign dram_addr = tsr_go ? addr_in : addr_next;
wire [20:0] addr_in = {addr_offset, addr, 1'b0};
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
// as renderer can't move outside the single bitmap line, only 7 bits are processed
reg [20:0] addr_reg;
always @(posedge clk) addr_reg <= dram_addr;
// DRAM cycles counter
assign mem_rdy = cyc[4];
reg [4:0] cyc;
always @(posedge clk)
if (reset)
cyc <= 5'b10000;
else if (tsr_go)
cyc <= {1'b0, x_size, 1'b1};
else if (dram_pre_next)
cyc <= cyc - 5'd1;
// DRAM data fetching
reg [15:0] data;
always @(posedge clk) if (dram_next) data <= dram_rdata;
// pixel render counter
assign ts_we = render_on && |pix; // write signal for TS-line
wire render_on = !cnt[2];
reg [2:0] cnt;
always @(posedge clk)
if (reset)
cnt <= 3'b100;
else if (dram_next)
cnt <= 3'b000;
else if (render_on)
cnt <= cnt + 3'd1;
// renderer reload
reg tsr_rld;
always @(posedge clk)
if (reset)
tsr_rld <= 1'b0;
else if (tsr_go)
tsr_rld <= 1'b1;
else if (dram_next)
tsr_rld <= 1'b0;
// delayed values
reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
always @(posedge clk)
if (tsr_go)
begin
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
pal_d <= pal;
flip_d <= flip;
end
// TS-line address
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
wire tsr_rld_stb = tsr_rld && dram_next;
always @(posedge clk) ts_waddr <= ts_waddr_mx;
reg [3:0] pal_r;
reg flip_r;
always @(posedge clk)
if (tsr_rld_stb)
begin
pal_r <= pal_d;
flip_r <= flip_d;
end
// renderer mux
assign ts_wdata = {pal_r, pix};
wire [3:0] pix = pix_m[cnt[1:0]];
wire [3:0] pix_m[0:3];
assign pix_m[0] = data[7:4];
assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
assign pix_m[3] = data[11:8];
endmodule