URL
https://opencores.org/ocsvn/rf6809/rf6809/trunk
Subversion Repositories rf6809
[/] [rf6809/] [trunk/] [rtl/] [noc/] [video/] [rfTextController_x12.sv] - Rev 19
Compare with Previous | Blame | View Log
// ============================================================================
// __
// \\__/ o\ (C) 2006-2022 Robert Finch, Waterloo
// \ __ / All rights reserved.
// \/_// robfinch<remove>@finitron.ca
// ||
//
// rfTextController.sv
// text controller
//
// BSD 3-Clause License
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
// Text Controller
//
// FEATURES
//
// This core requires an external timing generator to provide horizontal
// and vertical sync signals, but otherwise can be used as a display
// controller on it's own. However, this core may also be embedded within
// another core such as a VGA controller.
//
// Window positions are referenced to the rising edge of the vertical and
// horizontal sync pulses.
//
// The core includes an embedded dual port RAM to hold the screen
// characters.
//
// The controller expects a 64kB memory region to be reserved.
//
// Memory Map:
// 0000-3FFF display ram
// DF00-DFFF controller registers
// E000-FFFF character bitmap ram
//
//--------------------------------------------------------------------
// Registers
//
// 00h
// 7 - 0 cccccccc number of columns (horizontal displayed number of characters)
// 15- 8 rrrrrrrr number of rows (vertical displayed number of characters)
// 19-16 dddd character output delay
// 43-32 nnnn nnnnnnnn window left (horizontal sync position - reference for left edge of displayed)
// 59-48 nnnn nnnnnnnn window top (vertical sync position - reference for the top edge of displayed)
// 01h
// 4- 0 nnnnn maximum scan line (char ROM max value is 7)
// 11- 8 wwww pixel size - width
// 15-12 hhhh pixel size - height
// 24 r reset state bit
// 32 e controller enable
// 40 m multi-color mode
// 48-52 nnnnn yscroll
// 56-60 nnnnn xscroll
// 02h
// 23- 0 cccccccc cccccccc color code for transparent background RGB 8,8,8 (only RGB 7,7,7 used)
// 63-32 cccc...cccc border color ZRGB 8,8,8,8
// 03h
// 23- 0 cccccccc cccccccc tile color code 1
// 55-32 cccccccc cccccccc tile color code 2
// 04h
// 4- 0 eeeee cursor end
// 7- 5 bbb blink control
// BP: 00=no blink
// BP: 01=no display
// BP: 10=1/16 field rate blink
// BP: 11=1/32 field rate blink
// 12- 8 sssss cursor start
// 15-14 tt cursor image type (box, underline, sidebar, asterisk
// 47-32 aaaaaaaa aaaaaaaa cursor position
// 05h
// 15- 0 aaaaaaaa aaaaaaaa start address (index into display memory)
// 07h
// 150 - - aaaaaaaa aaaaaaaa light pen position
//--------------------------------------------------------------------
//
// ============================================================================
//`define USE_CLOCK_GATE
`define INTERNAL_RAMS 1'b1
module rfTextController_x12(
rst_i, clk_i, cs_i,
cti_i, cyc_i, stb_i, ack_o, wr_i, adr_i, dat_i, dat_o,
txt_clk_o, txt_cyc_o, txt_stb_o, txt_ack_i, txt_we_o, txt_sel_o, txt_adr_o, txt_dat_o, txt_dat_i,
cbm_clk_o, cbm_cyc_o, cbm_stb_o, cbm_ack_i, cbm_we_o, cbm_sel_o, cbm_adr_o, cbm_dat_o, cbm_dat_i,
lp_i,
dot_clk_i, hsync_i, vsync_i, blank_i, border_i, zrgb_i, zrgb_o, xonoff_i
);
parameter num = 4'd1;
parameter COLS = 8'd64;
parameter ROWS = 8'd32;
// Syscon
input rst_i; // reset
input clk_i; // clock
// Slave signals
input cs_i; // circuit select
input [2:0] cti_i;
input cyc_i; // valid bus cycle
input stb_i; // data strobe
output ack_o; // data acknowledge
input wr_i; // write
input [15:0] adr_i; // address
input [11:0] dat_i; // data input
output reg [11:0] dat_o; // data output
// Master Signals
output txt_clk_o;
output reg txt_cyc_o;
output reg txt_stb_o;
input txt_ack_i;
output txt_we_o;
output [7:0] txt_sel_o;
output reg [16:0] txt_adr_o;
output [63:0] txt_dat_o;
input [63:0] txt_dat_i;
output cbm_clk_o; // character bitmap data fetch clock
output reg cbm_cyc_o;
output reg cbm_stb_o;
input cbm_ack_i;
output cbm_we_o;
output [7:0] cbm_sel_o;
output reg [15:0] cbm_adr_o;
output [63:0] cbm_dat_o;
input [63:0] cbm_dat_i;
input lp_i; // light pen
// Video signals
input dot_clk_i; // video dot clock
input hsync_i; // end of scan line
input vsync_i; // end of frame
input blank_i; // blanking signal
input border_i; // border area
input [31:0] zrgb_i; // input pixel stream
output reg [31:0] zrgb_o; // output pixel stream
input xonoff_i;
reg controller_enable;
reg [31:0] bkColor32, bkColor32d; // background color
reg [31:0] fgColor32, fgColor32d; // foreground color
wire [1:0] pix; // pixel value from character generator 1=on,0=off
reg por;
wire vclk;
assign txt_clk_o = vclk;
assign txt_we_o = por;
assign txt_sel_o = 8'hFF;
assign cbm_clk_o = vclk;
assign cbm_we_o = 1'b0;
assign cbm_sel_o = 8'hFF;
reg [63:0] rego;
reg [4:0] yscroll;
reg [5:0] xscroll;
reg [11:0] windowTop;
reg [11:0] windowLeft;
reg [ 7:0] numCols;
reg [ 7:0] numRows;
reg [ 7:0] charOutDelay;
reg [ 1:0] mode;
reg [ 4:0] maxRowScan;
reg [ 5:0] maxScanpix;
reg [1:0] tileWidth; // width of tile in bytes (0=1,1=2,2=4,3=8)
reg [ 4:0] cursorStart, cursorEnd;
reg [15:0] cursorPos;
reg [1:0] cursorType;
reg [15:0] startAddress;
reg [ 2:0] rBlink;
reg [3:0] bdrCode;
wire [23:0] bdrColor; // Border color
reg [ 3:0] pixelWidth; // horizontal pixel width in clock cycles
reg [ 3:0] pixelHeight; // vertical pixel height in scan lines
reg mcm; // multi-color mode
wire [11:0] hctr; // horizontal reference counter (counts clocks since hSync)
wire [11:0] scanline; // scan line
reg [ 7:0] row; // vertical reference counter (counts rows since vSync)
reg [ 7:0] col; // horizontal column
reg [ 4:0] rowscan; // scan line within row
reg [ 5:0] colscan; // pixel column number within cell
wire nxt_row; // when to increment the row counter
wire nxt_col; // when to increment the column counter
reg [ 5:0] bcnt; // blink timing counter
wire blink;
reg iblank;
reg [4:0] maxScanlinePlusOne;
wire nhp; // next horizontal pixel
wire ld_shft = nxt_col & nhp;
// display and timing signals
reg [15:0] txtAddr; // index into memory
reg [15:0] penAddr;
wire [23:0] screen_ram_out; // character code
wire [23:0] txtBkColor; // background color code
wire [23:0] txtFgColor; // foreground color code
wire [5:0] txtZorder;
reg [3:0] txtTcCode; // transparent color code
wire [23:0] txtTcColor;
reg [3:0] tileCode1;
reg [3:0] tileCode2;
wire [23:0] tileColor1;
wire [23:0] tileColor2;
reg bgt, bgtd;
wire [11:0] tdat_o;
wire [8:0] chdat_o;
wire [2:0] scanindex = rowscan[2:0];
//--------------------------------------------------------------------
// bus interfacing
// Address Decoding
// I/O range Dx
//--------------------------------------------------------------------
// Register the inputs
reg cs_rom, cs_reg, cs_text, cs_any;
reg [15:0] radr_i;
reg [11:0] rdat_i;
reg rwr_i;
always_ff @(posedge clk_i)
cs_rom <= cs_i && cyc_i && stb_i && (adr_i[15:8] > 8'hDF);
always_ff @(posedge clk_i)
cs_reg <= cs_i && cyc_i && stb_i && (adr_i[15:8] == 8'hDF);
always_ff @(posedge clk_i)
cs_text <= cs_i && cyc_i && stb_i && (adr_i[15:8] < 8'hDF);
always_ff @(posedge clk_i)
cs_any <= cs_i && cyc_i && stb_i;
always_ff @(posedge clk_i)
rwr_i <= wr_i;
always_ff @(posedge clk_i)
radr_i <= adr_i;
always_ff @(posedge clk_i)
rdat_i <= dat_i;
// Register outputs
always @(posedge clk_i)
if (cs_i)
casez({cs_rom,cs_reg,cs_text})
3'b1??: dat_o <= {3'b0,chdat_o};
3'b01?: dat_o <= rego;
3'b001: dat_o <= tdat_o;
default: dat_o <= 12'h0;
endcase
else
dat_o <= 12'h0;
//always @(posedge clk_i)
// if (cs_text) begin
// $display("TC WRite: %h %h", adr_i, dat_i);
// $stop;
// end
// - there is a four cycle latency for reads, an ack is generated
// after the synchronous RAM read
// - writes can be acknowledged right away.
ack_gen #(
.READ_STAGES(5),
.WRITE_STAGES(1),
.REGISTER_OUTPUT(1)
)
uag1 (
.rst_i(rst_i),
.clk_i(clk_i),
.ce_i(1'b1),
.i(cs_any),
.we_i(cs_any & rwr_i),
.o(ack_o),
.rid_i(0),
.wid_i(0),
.rid_o(),
.wid_o()
);
//--------------------------------------------------------------------
//--------------------------------------------------------------------
`ifdef USE_CLOCK_GATE
BUFHCE ucb1 (.I(dot_clk_i), .CE(controller_enable), .O(vclk));
`else
assign vclk = dot_clk_i;
`endif
//--------------------------------------------------------------------
// Video Memory
//--------------------------------------------------------------------
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Address Calculation:
// - Simple: the row times the number of cols plus the col plus the
// base screen address
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
reg [15:0] rowcol;
always_ff @(posedge vclk)
txtAddr <= startAddress + rowcol + col;
// Register read-back memory
// Allows reading back of register values by shadowing them with ram
wire [5:0] rrm_adr = radr_i[5:0];
wire [11:0] rrm_o;
regReadbackMem #(.WID(12)) rrm0L
(
.wclk(clk_i),
.adr(rrm_adr),
.wce(cs_reg),
.we(rwr_i),
.i(rdat_i[11:0]),
.o(rrm_o[11:0])
);
wire [23:0] lfsr_o;
lfsr #(24) ulfsr1(rst_i, dot_clk_i, 1'b1, 1'b0, lfsr_o);
assign m_dat_o = lfsr_o;
/*
wire pe_cs;
edge_det u1(.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(cs_text), .pe(pe_cs), .ne(), .ee() );
reg [14:0] ctr;
always @(posedge clk_i)
if (pe_cs) begin
if (cti_i==3'b000)
ctr <= adr_i[16:3];
else
ctr <= adr_i[16:3] + 12'd1;
cnt <= 3'b000;
end
else if (cs_text && cnt[2:0]!=3'b100 && cti_i!=3'b000) begin
ctr <= ctr + 2'd1;
cnt <= cnt + 3'd1;
end
reg [13:0] radr;
always @(posedge clk_i)
radr <= pe_cs ? adr_i[16:3] : ctr;
*/
// text screen RAM
wire [13:0] bram_adr = radr_i[13:0];
`ifdef INTERNAL_RAMS
rfTextControllerRam_x12 screen_ram1
(
.clka(clk_i),
.ena(cs_text),
.wea(rwr_i),
.addra({radr_i[12:0],radr_i[13]}),
.dina(rdat_i),
.douta(tdat_o),
.clkb(vclk),
.enb(ld_shft|por),
.web(por),
.addrb(txtAddr[12:0]),
.dinb({12'hE6,lfsr_o[11:0]}),
.doutb(screen_ram_out)
);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Character bitmap ROM
// - room for 512 8x8 characters
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
wire [63:0] char_bmp; // character ROM output
char_ram charRam0
(
.clk_i(clk_i),
.cs_i(cs_rom),
.we_i(1'b0),
.adr_i(radr_i[14:0]),
.dat_i(rdat_i),
.dat_o(chdat_o),
.dot_clk_i(vclk),
.ce_i(ld_shft),
.char_code_i(screen_ram_out[8:0]),
.maxscanline_i(maxScanlinePlusOne),
.scanline_i(rowscan[4:0]),
.bmp_o(char_bmp)
);
//assign char_bmp[63:9] = 55'h0;
`else
reg [63:0] char_bmp; // character ROM output
`endif
reg [63:0] txt_dati;
reg [31:0] bmp_ndx;
`ifdef INTERNAL_RAMS
`else
always_ff @(posedge vclk)
bmp_ndx <= (maxScanlinePlusOne * txt_dati[15:0] + rowscan[4:0]) << tileWidth;
always_ff @(posedge vclk)
begin
if (ld_shft) begin
txt_cyc_o <= 1'b1;
txt_stb_o <= 1'b1;
txt_adr_o <= {txtAddr[13:0],3'b0};
cbm_cyc_o <= 1'b1;
cbm_stb_o <= 1'b1;
cbm_adr_o <= {bmp_ndx[31:3],3'b0};
end
if (txt_ack_i) begin
txt_cyc_o <= 1'b0;
txt_stb_o <= 1'b0;
txt_dati <= txt_dat_i;
end
if (cbm_ack_i) begin
cbm_cyc_o <= 1'b0;
cbm_stb_o <= 1'b0;
case(tileWidth)
2'd0: char_bmp <= cbm_dat_i >> {bmp_ndx[2:0],3'b0};
2'd1: char_bmp <= cbm_dat_i >> {bmp_ndx[2:1],4'b0};
2'd2: char_bmp <= cbm_dat_i >> {bmp_ndx[2],5'b0};
2'd3: char_bmp <= cbm_dat_i;
endcase
end
end
`endif
/*
syncRam4kx9 charRam0
(
.clka(clk_i), // input wire clka
.ena(cs_rom), // input wire ena
.wea(1'b0),//rwr_i), // input wire [0 : 0] wea
.addra(bram_adr), // input wire [11 : 0] addra
.dina(rdat_i[8:0]), // input wire [8 : 0] dina
.douta(chdat_o), // output wire [8 : 0] douta
.clkb(vclk), // input wire clkb
.enb(ld_shft), // input wire enb
.web(1'b0), // input wire [0 : 0] web
.addrb({screen_ram_out[8:0],scanline[2:0]}), // input wire [11 : 0] addrb
.dinb(9'h0), // input wire [8 : 0] dinb
.doutb(char_bmp) // output wire [8 : 0] doutb
);
*/
// pipeline delay - sync color with character bitmap output
reg [5:0] txtZorder1;
wire [3:0] txtBkCode1 = screen_ram_out[15:12];
wire [3:0] txtFgCode1 = screen_ram_out[19:16];
rfColorROM ucr1(vclk, ld_shft, {1'b0,txtBkCode1}, txtBkColor);
rfColorROM ucr2(vclk, ld_shft, {1'b0,txtFgCode1}, txtFgColor);
rfColorROM ucr3(vclk, 1'b1, {1'b0,txtTcCode}, txtTcColor);
rfColorROM ucr4(vclk, 1'b1, {1'b0,bdrCode}, bdrColor);
rfColorROM ucr5(vclk, ld_shft, {1'b0,tileCode1}, tileColor1);
rfColorROM ucr6(vclk, ld_shft, {1'b0,tileCode2}, tileColor2);
always @(posedge vclk)
if (ld_shft) txtZorder1 <= 6'h3F;
//--------------------------------------------------------------------
// Light Pen
//--------------------------------------------------------------------
wire lpe;
edge_det u1 (.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(lp_i), .pe(lpe), .ne(), .ee() );
always @(posedge clk_i)
if (rst_i)
penAddr <= 32'h0000_0000;
else begin
if (lpe)
penAddr <= txtAddr;
end
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register read port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always @*
if (cs_reg) begin
case(radr_i[5:0])
6'd56: rego <= {8'h00,penAddr[15:12]};
6'd57: rego <= penAddr[11:0];
default: rego <= rrm_o;
endcase
end
else
rego <= 12'h000;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Register write port
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
always @(posedge clk_i)
if (rst_i) begin
por <= 1'b1;
mcm <= 1'b0;
controller_enable <= 1'b1;
xscroll <= 6'd0;
yscroll <= 5'd0;
txtTcCode <= 4'hE;
bdrCode <= 4'hE;
startAddress <= 16'h0000;
cursorStart <= 5'd00;
cursorEnd <= 5'd31;
cursorPos <= 16'h0003;
cursorType <= 2'b00;
// 104x63
/*
windowTop <= 12'd26;
windowLeft <= 12'd260;
pixelWidth <= 4'd0;
pixelHeight <= 4'd1; // 525 pixels (408 with border)
*/
// 52x31
/*
// 84x47
windowTop <= 12'd16;
windowLeft <= 12'd90;
pixelWidth <= 4'd1; // 681 pixels
pixelHeight <= 4'd1; // 384 pixels
*/
// 48x29
if (num==4'd1) begin
windowTop <= 12'd4058;//12'd16;
windowLeft <= 12'd3956;//12'd3930;//12'd86;
pixelWidth <= 4'd0; // 800 pixels
pixelHeight <= 4'd0; // 600 pixels
numCols <= COLS;
numRows <= ROWS;
maxRowScan <= 5'd17;
maxScanpix <= 6'd11;
rBlink <= 3'b111; // 01 = non display
charOutDelay <= 8'd5;
end
else if (num==4'd2) begin
windowTop <= 12'd4032;//12'd16;
windowLeft <= 12'd3720;//12'd86;
pixelWidth <= 4'd0; // 800 pixels
pixelHeight <= 4'd0; // 600 pixels
numCols <= 40;
numRows <= 25;
maxRowScan <= 5'd7;
maxScanpix <= 6'd7;
rBlink <= 3'b111; // 01 = non display
charOutDelay <= 8'd6;
end
end
else begin
if (bcnt > 6'd10)
por <= 1'b0;
if (cs_reg & rwr_i) begin // register write ?
$display("TC Write: r%d=%h", rrm_adr, rdat_i);
case(radr_i[5:0])
6'd0: numCols <= rdat_i[7:0];
6'd1: numRows <= rdat_i[7:0];
6'd2: charOutDelay <= rdat_i;
6'd4: ;
6'd5: windowLeft[11:0] <= rdat_i;
6'd6: ;
6'd7: windowTop[11:0] <= rdat_i;
6'd8: maxRowScan <= rdat_i[4:0];
6'd9:
begin
pixelHeight <= rdat_i[7:4];
pixelWidth <= rdat_i[3:0]; // horizontal pixel width
end
6'd11: por <= rdat_i[0];
6'd12: controller_enable <= rdat_i[0];
6'd13: mcm <= rdat_i[0];
6'd14: yscroll <= rdat_i[4:0];
6'd15: xscroll <= rdat_i[5:0];
6'd16: txtTcCode <= rdat_i[7:0];
6'd20: bdrCode <= rdat_i[7:0];
6'd24: tileCode1 <= rdat_i[7:0];
6'd28: tileCode2 <= rdat_i[7:0];
6'd31: maxScanpix <= rdat_i[5:0];
6'd32:
begin
cursorEnd <= rdat_i[4:0]; // scan line sursor starts on
rBlink <= rdat_i[7:5];
end
6'd33:
begin
cursorStart <= rdat_i[4:0]; // scan line cursor ends on
cursorType <= rdat_i[7:6];
end
6'd34: cursorPos[15:12] <= rdat_i[3:0];
6'd35: cursorPos[11: 0] <= rdat_i;
6'd40: startAddress[15:12] <= rdat_i[3:0];
6'd41: startAddress[11: 0] <= rdat_i;
default: ;
endcase
end
end
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// "Box" cursor bitmap
reg [7:0] curout;
always @*
case({cursorType,scanindex})
// Box cursor
5'b00_000: curout = 8'b11111110;
5'b00_001: curout = 8'b10000010;
5'b00_010: curout = 8'b10000010;
5'b00_011: curout = 8'b10000010;
5'b00_100: curout = 8'b10000010;
5'b00_101: curout = 8'b10000010;
5'b00_110: curout = 8'b10010010;
5'b00_111: curout = 8'b11111110;
// vertical bar cursor
5'b01_000: curout = 8'b11000000;
5'b01_001: curout = 8'b10000000;
5'b01_010: curout = 8'b10000000;
5'b01_011: curout = 8'b10000000;
5'b01_100: curout = 8'b10000000;
5'b01_101: curout = 8'b10000000;
5'b01_110: curout = 8'b10000000;
5'b01_111: curout = 8'b11000000;
// underline cursor
5'b10_000: curout = 8'b00000000;
5'b10_001: curout = 8'b00000000;
5'b10_010: curout = 8'b00000000;
5'b10_011: curout = 8'b00000000;
5'b10_100: curout = 8'b00000000;
5'b10_101: curout = 8'b00000000;
5'b10_110: curout = 8'b00000000;
5'b10_111: curout = 8'b11111111;
// Asterisk
5'b11_000: curout = 8'b00000000;
5'b11_001: curout = 8'b00000000;
5'b11_010: curout = 8'b00100100;
5'b11_011: curout = 8'b00011000;
5'b11_100: curout = 8'b01111110;
5'b11_101: curout = 8'b00011000;
5'b11_110: curout = 8'b00100100;
5'b11_111: curout = 8'b00000000;
endcase
//-------------------------------------------------------------
// Video Stuff
//-------------------------------------------------------------
wire pe_hsync;
wire pe_vsync;
edge_det edh1
(
.rst(rst_i),
.clk(vclk),
.ce(1'b1),
.i(hsync_i),
.pe(pe_hsync),
.ne(),
.ee()
);
edge_det edv1
(
.rst(rst_i),
.clk(vclk),
.ce(1'b1),
.i(vsync_i),
.pe(pe_vsync),
.ne(),
.ee()
);
// Horizontal counter:
//
/*
HVCounter uhv1
(
.rst(rst_i),
.vclk(vclk),
.pixcce(1'b1),
.sync(hsync_i),
.cnt_offs(windowLeft),
.pixsz(pixelWidth),
.maxpix(maxScanpix),
.nxt_pix(nhp),
.pos(col),
.nxt_pos(nxt_col),
.ctr(hctr)
);
*/
// Vertical counter:
//
/*
HVCounter uhv2
(
.rst(rst_i),
.vclk(vclk),
.pixcce(pe_hsync),
.sync(vsync_i),
.cnt_offs(windowTop),
.pixsz(pixelHeight),
.maxpix(maxRowScan),
.nxt_pix(),
.pos(row),
.nxt_pos(nxt_row),
.ctr(scanline)
);
*/
// We generally don't care about the exact reset point, unless debugging in
// simulation. The counters will eventually cycle to a proper state. A little
// bit of logic / routing can be avoided by omitting the reset.
`ifdef SIM
wire sym_rst = rst_i;
`else
wire sym_rst = 1'b0;
`endif
// Raw scanline counter
vid_counter #(12) u_vctr (.rst(sym_rst), .clk(vclk), .ce(pe_hsync), .ld(pe_vsync), .d(windowTop), .q(scanline), .tc());
vid_counter #(12) u_hctr (.rst(sym_rst), .clk(vclk), .ce(1'b1), .ld(pe_hsync), .d(windowLeft), .q(hctr), .tc());
// Vertical pixel height counter, synchronized to scanline #0
reg [3:0] vpx;
wire nvp = vpx==pixelHeight;
always @(posedge vclk)
if (sym_rst)
vpx <= 4'b0;
else begin
if (pe_hsync) begin
if (scanline==12'd0)
vpx <= 4'b0;
else if (nvp)
vpx <= 4'd0;
else
vpx <= vpx + 4'd1;
end
end
reg [3:0] hpx;
assign nhp = hpx==pixelWidth;
always @(posedge vclk)
if (sym_rst)
hpx <= 4'b0;
else begin
if (hctr==12'd0)
hpx <= 4'b0;
else if (nhp)
hpx <= 4'd0;
else
hpx <= hpx + 4'd1;
end
// The scanline row within a character bitmap
always @(posedge vclk)
if (sym_rst)
rowscan <= 5'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
rowscan <= yscroll;
else if (rowscan==maxRowScan)
rowscan <= 5'd0;
else
rowscan <= rowscan + 5'd1;
end
end
assign nxt_col = colscan==maxScanpix;
always @(posedge vclk)
if (sym_rst)
colscan <= 6'd0;
else begin
if (nhp) begin
if (hctr==12'd0)
colscan <= xscroll;
else if (nxt_col)
colscan <= 6'd0;
else
colscan <= colscan + 2'd1;
end
end
// The screen row
always @(posedge vclk)
if (sym_rst)
row <= 8'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
row <= 8'd0;
else if (rowscan==maxRowScan)
row <= row + 8'd1;
end
end
// The screen column
always @(posedge vclk)
if (sym_rst)
col <= 8'd0;
else begin
if (hctr==12'd0)
col <= 8'd0;
else if (nhp) begin
if (nxt_col)
col <= col + 8'd1;
end
end
// More useful, the offset of the start of the text display on a line.
always @(posedge vclk)
if (sym_rst)
rowcol <= 16'd0;
else begin
if (pe_hsync & nvp) begin
if (scanline==12'd0)
rowcol <= 8'd0;
else if (rowscan==maxRowScan)
rowcol <= rowcol + numCols;
end
end
// Takes 3 clock for scanline to become stable, but should be stable before any
// chars are displayed.
reg [13:0] rxmslp1;
always @(posedge vclk)
maxScanlinePlusOne <= maxRowScan + 4'd1;
//always @(posedge vclk)
// rxmslp1 <= row * maxScanlinePlusOne;
//always @(posedge vclk)
// scanline <= scanline - rxmslp1;
// Blink counter
//
always @(posedge vclk)
if (sym_rst)
bcnt <= 6'd0;
else begin
if (pe_vsync)
bcnt <= bcnt + 6'd1;
end
reg blink_en;
always @(posedge vclk)
blink_en <= (cursorPos+4==txtAddr) && (rowscan[4:0] >= cursorStart) && (rowscan[4:0] <= cursorEnd);
VT151 ub2
(
.e_n(!blink_en),
.s(rBlink),
.i0(1'b1), .i1(1'b0), .i2(bcnt[4]), .i3(bcnt[5]),
.i4(1'b1), .i5(1'b0), .i6(bcnt[4]), .i7(bcnt[5]),
.z(blink),
.z_n()
);
always @(posedge vclk)
if (ld_shft)
bkColor32 <= {txtZorder1,2'b00,txtBkColor};
always @(posedge vclk)
if (nhp)
bkColor32d <= bkColor32;
always @(posedge vclk)
if (ld_shft)
fgColor32 <= {txtZorder1,2'b00,txtFgColor};
always @(posedge vclk)
if (nhp)
fgColor32d <= fgColor32;
always @(posedge vclk)
if (ld_shft)
bgt <= txtBkCode1==txtTcCode;
always @(posedge vclk)
if (nhp)
bgtd <= bgt;
// Convert character bitmap to pixels
reg [63:0] charout1;
always @(posedge vclk)
charout1 <= blink ? (char_bmp ^ curout) : char_bmp;
// Convert parallel to serial
ParallelToSerial ups1
(
.rst(rst_i),
.clk(vclk),
.mcm(mcm),
.ce(nhp),
.ld(ld_shft),
.a(maxScanpix[5:3]),
.qin(2'b0),
.d(charout1),
.qh(pix)
);
/*
always_ff @(posedge vclk)
if (rst_i) begin
pix <= 64'd0;
end
else begin
if (nhp) begin
if (ld_shft)
pix <= charout1;
else begin
if (mcm)
pix <= {2'b00,pix[63:2]};
else
pix <= {1'b0,pix[63:1]};
end
end
end
*/
reg [1:0] pix1;
always_ff @(posedge vclk)
if (nhp)
pix1 <= pix[1:0];
// Pipelining Effect:
// - character output is delayed by 2 or 3 character times relative to the video counters
// depending on the resolution selected
// - this means we must adapt the blanking signal by shifting the blanking window
// two or three character times.
wire bpix = hctr[2] ^ rowscan[4];// ^ blink;
always_ff @(posedge vclk)
if (nhp)
iblank <= (row >= numRows) || (col >= numCols + charOutDelay) || (col < charOutDelay);
// Choose between input RGB and controller generated RGB
// Select between foreground and background colours.
// Note the ungated dot clock must be used here, or output from other
// controllers would not be visible if the clock were gated off.
always_ff @(posedge dot_clk_i)
casez({controller_enable&xonoff_i,blank_i,iblank,border_i,bpix,mcm,pix1})
8'b01??????: zrgb_o <= 32'h00000000;
8'b11??????: zrgb_o <= 32'h00000000;
8'b1001????: zrgb_o <= bdrColor;
//6'b10010?: zrgb_o <= 32'hFFBF2020;
//6'b10011?: zrgb_o <= 32'hFFDFDFDF;
8'b1000?00?: zrgb_o <= (zrgb_i[31:24] > bkColor32d[31:24]) ? zrgb_i : bkColor32d;
// 8'b1000?0?0: zrgb_o <= bkColor32d;
8'b1000?01?: zrgb_o <= fgColor32d; // ToDo: compare z-order
8'b1000?100: zrgb_o <= (zrgb_i[31:24] > bkColor32d[31:24]) ? zrgb_i : bkColor32d;
8'b1000?101: zrgb_o <= fgColor32d;
8'b1000?110: zrgb_o <= {8'hFF,tileColor1};
8'b1000?111: zrgb_o <= {8'hFF,tileColor2};
// 6'b1010?0: zrgb_o <= bgtd ? zrgb_i : bkColor32d;
// 6'b1010?1: zrgb_o <= fgColor32d;
default: zrgb_o <= zrgb_i;
endcase
endmodule