/* =============================================================== (C) 2006 Robert Finch All rights reserved. rob@birdcomputer.ca textController.v text controller This source code is free for use and modification for non-commercial or evaluation purposes, provided this copyright statement and disclaimer remains present in the file. If you do modify the code, please state the origin and note that you have modified the code. NO WARRANTY. THIS Work, IS PROVIDED "AS IS" WITH NO WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED. The user must assume the entire risk of using the Work. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY INCIDENTAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES WHATSOEVER RELATING TO THE USE OF THIS WORK, OR YOUR RELATIONSHIP WITH THE AUTHOR. IN ADDITION, IN NO EVENT DOES THE AUTHOR AUTHORIZE YOU TO USE THE WORK IN APPLICATIONS OR SYSTEMS WHERE THE WORK'S FAILURE TO PERFORM CAN REASONABLY BE EXPECTED TO RESULT IN A SIGNIFICANT PHYSICAL INJURY, OR IN LOSS OF LIFE. ANY SUCH USE BY YOU IS ENTIRELY AT YOUR OWN RISK, AND YOU AGREE TO HOLD THE AUTHOR AND CONTRIBUTORS HARMLESS FROM ANY CLAIMS OR LOSSES RELATING TO SUCH UNAUTHORIZED USE. 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. // Window Co-ordinates 0: LEF - left [15: 0] horizontal position (hctr value) 1: TOP - top [15: 0] vertical position (vctr value) 2: RIG - right [15: 0] horizontal position (hctr value) 3: BOT - bottom [15: 0] vertical position (vctr value) 4: SZ - size bits [ 1: 0] size of horizontal pixels - 1 in clock cycles [ 5: 4] size of vertical pixels in scan-lines - 1 [ 15] text screen enable OFF - screen offset [15:0] screen offset 6: BTC [15:0] background transparent color 7: BTC [23:16] background transparent color Webpack 7.1i xc3s1000-4ft256 230 LUTs / 145 slices / 135MHz 5 block rams =============================================================== */ // C64 color codes `define C64_BLACK 4'd0 `define C64_WHITE 4'd1 `define C64_RED 4'd2 `define C64_CYAN 4'd3 `define C64_PURPLE 4'd4 `define C64_GREEN 4'd5 `define C64_BLUE 4'd6 `define C64_YELLOW 4'd7 `define C64_ORANGE 4'd8 `define C64_BROWN 4'd9 `define C64_PINK 4'd10 `define C64_DARK_GREY 4'd11 `define C64_MEDIUM_GREY 4'd12 `define C64_LIGHT_GREEN 4'd13 `define C64_LIGHT_BLUE 4'd14 `define C64_LIGHT_GREY 4'd15 module textController #( parameter pLeft = 108, parameter pTop = 72,//+144, parameter pRight = pLeft + 640, parameter pBottom = pTop + 400 ) ( // Bus interface //------------------------ input rst_i, // reset input clk_i, // clock input cyc_i, // cycle valid input stbT_i, // chip select (strobe) for text RAM input stbC_i, // chip select (strobe) for character bitmap RAM input stbR_i, // chip select (strobe) for registers output ack_o, // transfer acknowledge input we_i, // write input [1:0] sel_i, // byte select input [15:0] adr_i, // address input [15:0] dat_i, // data input output [15:0] dat_o, // data output //------------------------ input vclk, // video dot clock input hSync, // horizontal sync pulse input vSync, // vertical sync pulse input blank, // blanking signal input [24:0] rgbIn, // input pixel stream output reg [24:0] rgbOut // output pixel stream ); //-------------------------------------------------------------------- // Variable Declarations //-------------------------------------------------------------------- reg [11:0] hctr; // horizontal reference counter (counts clocks since hSync) reg [10:0] vctr; // vertical reference counter (counts scanlines since vSync) wire [23:0] out; // output // Global control registers // How big the pixels are: // 1,2,3,or 4 video clocks reg [1:0] hRes; // horizontal resolution reg [1:0] vRes; // vertical resolution // Window format - Referenced to sync position reg [11:0] left; reg [11:0] right; reg [10:0] top; reg [10:0] bottom; reg [23:0] bgTc; // background transparent color reg [15:0] offset; // offset within memory reg textEn; // text output enabled // display and timing signals reg [11:0] hCnt; // horizontal display counter reg [10:0] vCnt; // vertical display counter reg [15:0] txtAddr; // index into memory reg [15:0] txtAddrB; // backup address cache for rescan wire [23:0] txtiOut; // image data output wire [7:0] txtOut; // character code wire [7:0] charOut; // character ROM output wire [3:0] txtBkCode; // background color code wire [3:0] txtFgCode; // foreground color code wire [15:0] tdat_o; wire [7:0] cdat_o; assign dat_o = tdat_o|cdat_o; integer n; // text screen RAM syncRam4kx16_1rw1r textRam0( .wclk(clk_i), .wadr(adr_i[12:1]), .i(dat_i), .wo(tdat_o), .wce(cyc_i & stbT_i), .we(we_i), .wrst(!stbT_i), .rclk(vclk), .radr(txtAddr), .o({txtBkCode,txtFgCode,txtOut}), .rce(1'b1), .rrst(1'b0) ); // character bitmap ROM syncRam2kx8_1rw1r charRam0( .wclk(clk_i), .wadr(adr_i[10:0]), .i(dat_i[7:0]), .wo(cdat_o[7:0]), .wce(cyc_i & stbC_i), .we(we_i), .wrst(!stbC_i), .rclk(vclk), .radr({txtOut[6:0],vCnt[2:0]}), .o(charOut), .rce(1'b1), .rrst(1'b0) ); //-------------------------------------------------------------------- // bus interfacing //-------------------------------------------------------------------- reg ramRdy; always @(posedge clk_i) ramRdy = cyc_i & (stbT_i|stbC_i); assign ack_o = cyc_i ? ( (we_i | stbR_i) ? (stbT_i|stbC_i|stbR_i) : ramRdy ) : 0; //-------------------------------------------------------------------- // register interfacing //-------------------------------------------------------------------- always @(posedge clk_i) if (rst_i) begin left <= pLeft; top <= pTop; right <= pRight; bottom <= pBottom; hRes <= 1; vRes <= 1; offset <= 0; textEn <= 1; bgTc <= 24'h00_00_00; end else begin if (cyc_i & stbR_i & we_i) begin case (adr_i[3:1]) 3'd0: if (sel_i[0]) left <= dat_i; 3'd1: if (sel_i[0]) top <= dat_i; 3'd2: if (sel_i[0]) right <= dat_i; 3'd3: if (sel_i[0]) bottom <= dat_i; 3'd4: if (sel_i[0]) begin hRes <= dat_i[1:0]; vRes <= dat_i[5:4]; textEn <= dat_i[8]; end 3'd5: ; 3'd6: if (sel_i[0]) bgTc[15: 0] <= dat_i; 3'd7: if (sel_i[0]) bgTc[23:16] <= dat_i; endcase end end //------------------------------------------------------------- // Timing counters //------------------------------------------------------------- reg [1:0] hPt; // horizontal pixel toggle reg [1:0] vPt; // vertical pixel toggle reg hGate; reg vGate; reg atLeft; reg atTop; wire inWindow = hGate & vGate; wire inWindow1; // Create a timing reference using horizontal and vertical sync wire hSyncEdge, vSyncEdge; edge_det ed0(.rst(rst_i), .clk(vclk), .ce(1'b1), .i(hSync), .pe(hSyncEdge), .ne(), .ee() ); edge_det ed1(.rst(rst_i), .clk(vclk), .ce(1'b1), .i(vSync), .pe(vSyncEdge), .ne(), .ee() ); always @(posedge vclk) if (rst_i) hctr <= 0; else if (hSyncEdge) hctr <= 0; else hctr <= hctr + 1; always @(posedge vclk) if (rst_i) vctr <= 0; else if (vSyncEdge) vctr <= 0; else if (hSyncEdge) vctr <= vctr + 1; always @(posedge vclk) if (rst_i) hGate <= 0; else if (hctr==left) hGate <= 1; else if (hctr==right) hGate <= 0; always @(posedge vclk) if (rst_i) vGate <= 0; else if (vctr==top) vGate <= 1; else if (vctr==bottom) vGate <= 0; always @(posedge vclk) if (rst_i) atLeft <= 0; else if (hctr==left) atLeft <= 1; else atLeft <= 0; always @(posedge vclk) if (rst_i) atTop <= 0; else if (vctr==top) atTop <= 1; else atTop <= 0; // take care of pixel size scaling wire hNextPixel = hPt==hRes; wire vNextPixel = vPt==vRes; // horizontal pixel toggle counter always @(posedge vclk) if (atLeft) hPt <= 0; else if (hNextPixel) hPt <= 0; else hPt <= hPt + 1; // vertical pixel toggle counter always @(posedge vclk) if (atLeft) begin if (atTop) vPt <= 0; else if (vNextPixel) vPt <= 0; else vPt <= vPt + 1; end // horizontal pixel (X) counter always @(posedge vclk) if (atLeft) hCnt <= 0; else if (hNextPixel) hCnt <= hCnt + 1; // scan line (Y) counter always @(posedge vclk) if (atLeft) if (atTop) vCnt <= 0; else if (vNextPixel) vCnt <= vCnt + 1; //------------------------------------------------------------- // Address counters //------------------------------------------------------------- always @(posedge vclk) // If within the display area if (inWindow) begin // atLeft: // If the next vertical pixel // set backup address to current address // else // set current address to backup address // in order to rescan the line if (atLeft) begin // reset address at top left if (atTop) begin txtAddr <= offset; txtAddrB <= offset; end else if (vNextPixel) begin if (&vCnt[2:0]) txtAddrB <= txtAddr; else txtAddr <= txtAddrB; end else txtAddr <= txtAddrB; // rescan the same addresses end // Not hReset or vReset - somewhere on the scan line // just advance the address when the next pixel should be // fetched else if (hNextPixel) begin if (&hCnt[2:0]) txtAddr <= txtAddr + 1; end end //------------------------------------------------------------- // This table maps a color code to a 24 bit color value. //------------------------------------------------------------- function [23:0] C64ColorMap16; input [3:0] s; begin case (s) `C64_BLACK: C64ColorMap16 = 24'h10_10_10; `C64_WHITE: C64ColorMap16 = 24'hFF_FF_FF; `C64_RED: C64ColorMap16 = 24'hE0_40_40; `C64_CYAN: C64ColorMap16 = 24'h60_FF_FF; `C64_PURPLE: C64ColorMap16 = 24'hE0_60_E0; `C64_GREEN: C64ColorMap16 = 24'h40_E0_40; `C64_BLUE: C64ColorMap16 = 24'h40_40_E0; `C64_YELLOW: C64ColorMap16 = 24'hFF_FF_40; `C64_ORANGE: C64ColorMap16 = 24'hE0_A0_40; `C64_BROWN: C64ColorMap16 = 24'h9C_74_48; `C64_PINK: C64ColorMap16 = 24'hFF_A0_A0; `C64_DARK_GREY: C64ColorMap16 = 24'h54_54_54; `C64_MEDIUM_GREY: C64ColorMap16 = 24'h88_88_88; `C64_LIGHT_GREEN: C64ColorMap16 = 24'hA0_FF_A0; `C64_LIGHT_BLUE: C64ColorMap16 = 24'hA0_A0_FF; `C64_LIGHT_GREY: C64ColorMap16 = 24'hC0_C0_C0; endcase end endfunction //------------------------------------------------------------- // Output stage //------------------------------------------------------------- wire pix = charOut[hCnt[2:0]]; assign out = pix ? C64ColorMap16(txtFgCode) : C64ColorMap16(txtBkCode); // pipeline delay delay2 d1(.clk(vclk), .ce(1'b1), .i(inWindow), .o(inWindow1) ); always @(posedge vclk) begin if (blank) rgbOut <= 0; else if (inWindow1) rgbOut <= out; else rgbOut <= rgbIn; end endmodule