📄 vgachr 8080.v
字号:
wrtchr <= 0; // remove ready to write
outrdy <= 1; // set ready to send
state <= `term_idle;
end
end
default: state <= 6'bx;
endcase
end
// Enable drive to character memory
assign cmdata = cmdatae ? cmdatai: 8'bz;
// Enable drive to character attributes
assign cmattr = cmattre ? cmattri: 8'bz;
// Enable drive for data output
assign data = read&select ? datao: 8'bz;
endmodule
////////////////////////////////////////////////////////////////////////////////
//
// MEMORY MAPPED CHARACTER DISPLAY
//
// Contains the 20x8 characters used in a 640x480, 24 lines of 80 characters
// display. This display is accessed vi a 1920 character memory, which can be
// written and read. It is dual port, so has no restrictions on read and write.
//
// This block can either be directly mapped to CPU main memory, or can be run
// via a terminal emulator.
//
// Note there wasn't a mode for 24 lines, or for 640x480 pixels for alpha mode
// on the PC. Instead, 640x480 was a standard VGA mode for graphics. I like it
// because it unifies graphics and alpha modes, and has even divisions to
// character cells. Its a bit difficult to fit characters into a 8x20 cell,
// but the modes used for alpha, like 640x400 or 640x300 are not 4:3 aspect
// ratios, and really work by stretching the pixels vertically. We just do
// the same thing by explicit mapping.
//
`define clkfreq 50000000 // input clock frequency
`define blinkfreq 1 // blink cycle in seconds
`define blinkmax (`clkfreq*`blinkfreq) // total blink cycle
module chrmemmap(rst_n, clk, r, g, b, hsync_n, vsync_n, addr, read, write,
data, attr, cursor);
input rst_n; // reset
input clk; // master clock
output [2:0] r, g, b; // R,G,B color output buses
output hsync_n; // horizontal sync pulse
output vsync_n; // vertical sync pulse
input [10:0] addr; // address to read or write
input read; // read from address
input write; // write from address
inout [7:0] data; // data to be written/read
inout [4:0] attr; // attributes to be written/read
input [10:0] cursor; // cursor address
reg [15:0] pixeldata; // 16 bit pixel feed
reg [6:0] chrcnt; // character counter
reg [4:0] rowcnt; // character row counter
reg [4:0] lincnt; // line counter
reg [10:0] scnadr; // screen character buffer address
reg [7:0] curchr; // current character indexed by scnadr
reg [4:0] curatr; // current attribute indexed by scnadr
reg [1:0] fchsta; // character fetch state, 0 = load high, 1 = load low
wire [10:0] chradr; // character generator address
wire [7:0] chrdata; // character generator data
reg [7:0] datao; // intermediate for data output
reg [4:0] attro; // intermediate for attribute output
reg [7:0] pixdatl; // pixel data low holding
reg [31:0] blinkcnt; // blink cycle counter
reg blon; // blink on/off
// storage for character based screen
reg [7:0] scnbuf[1919:0]; // screen
reg [4:0] atrbuf[1919:0]; // attributes
assign rst = ~rst_n; // change reset polarity
vga vgai(.rst(rst), .clk(clk), .pixel_data_in(pixeldata), .rd(rd), .eof(eof),
.r(r), .g(g), .b(b), .hsync_n(hsync_n), .vsync_n(vsync_n),
.blank(blank));
chrrom crom(chradr, chrdata); // place character generator
// run the blink cycle counter
always @(posedge clk)
if (rst) begin
blinkcnt <= 0; // clear blink cycle
blon <= 0; // clear cursor blink on
end else begin
blinkcnt <= blinkcnt+1; // count blink cycle
// check blink cycle maxed, recycle if so
if (blinkcnt >= `blinkmax) begin
blinkcnt <= 0; // clear blink count
blon <= ~blon; // flip blink state
end
end
// run the character to screen scan
always @(posedge clk)
if (rst) begin // if reset
// ????? SIMULATION PLUG
// starting the character counter at the end of the line allows the scan
// to cross the area the CPU is filling, and is ok at hardware time.
// chrcnt <= 7'h0; // clear counters
chrcnt <= 80-20; // clear counters
// ????? SIMULATION PLUG
// Starting the row count at 1 allows pixels to appear on the simulation,
// and produces only a single bad line in real hardware
rowcnt <= 5'h0;
// rowcnt <= 5'h1;
lincnt <= 5'h0;
scnadr <= 11'h0;
fchsta <= 0;
end else if (eof) begin // if end of frame
chrcnt <= 7'h0; // clear counters
rowcnt <= 5'h0;
lincnt <= 5'h0;
scnadr <= 11'h0;
fchsta <= 1; // set to fetch first set of characters
end else if (rd || fchsta) begin
if (fchsta == 1 || fchsta == 3) begin
// advance counters
if (chrcnt < 79) chrcnt <= chrcnt+1; // next character count
else begin // next row
chrcnt <= 0; // reset character
if (rowcnt < 19) rowcnt <= rowcnt+1; // next character row
else begin // next line
rowcnt <= 0; // reset row
lincnt <= lincnt+1; // next line
scnadr <= scnadr+80; // advance character fetch
end
end
end
// Choose high or low character, and next state. Note we have to flip the
// characters left to right to be correct.
case (fchsta)
0: fchsta <= 1; // delay until rd cycle is over
1: begin
// Set low bits of pixel register, and reverse if cursor matches the
// current position, or if reverse attribute is on. Turn it all off
// if blank is on.
if ((scnadr+chrcnt == cursor)^curatr[2])
pixdatl <= ~(curatr[0]|(curatr[1]&blon)? 8'h00: // blink and blank
(curatr[3]&rowcnt == 14? 8'hff: // underline
{ chrdata[0], chrdata[1], chrdata[2], chrdata[3],
chrdata[4], chrdata[5], chrdata[6], chrdata[7] }));
else
pixdatl <= curatr[0]|(curatr[1]&blon)? 8'h00: // blink and blank
(curatr[3]&rowcnt == 14? 8'hff: // underline
{ chrdata[0], chrdata[1], chrdata[2], chrdata[3],
chrdata[4], chrdata[5], chrdata[6], chrdata[7] });
fchsta <= 2; // next state
end
2: fchsta <= 3; // delay a cycle for ROM read time
3: begin
// Set low bits of pixel register, and reverse if cursor matches the
// current position, or if reverse attribute is on. Turn it all off
// if blank is on.
if ((scnadr+chrcnt == cursor)^curatr[2])
pixeldata <= pixdatl |
(~(curatr[0]|(curatr[1]&blon)? 8'h00: // blink and blank
(curatr[3]&rowcnt == 14? 8'hff: // underline
{ chrdata[0], chrdata[1], chrdata[2], chrdata[3],
chrdata[4], chrdata[5], chrdata[6], chrdata[7] })) &
8'hff) << 8;
else
pixeldata <= pixdatl |
(curatr[0]|(curatr[1]&blon)? 8'h00: // blink and blank
(curatr[3]&rowcnt == 14? 8'hff: // underline
{ chrdata[0], chrdata[1], chrdata[2], chrdata[3],
chrdata[4], chrdata[5], chrdata[6], chrdata[7] })) << 8;
fchsta <= 0; // back to start
end
endcase
end
// operate dual port screen character RAM
always @(posedge clk) begin
// set current indexed character without parity
curchr <= scnbuf[scnadr+chrcnt] & 8'h7f;
if (write) scnbuf[addr] <= data;
datao <= scnbuf[addr];
end
// operate dual port screen attribute RAM
always @(posedge clk) begin
// set current indexed character without parity
curatr <= atrbuf[scnadr+chrcnt];
if (write) atrbuf[addr] <= attr;
attro <= atrbuf[addr];
end
// create character address from character in buffer and current row
assign chradr =
(curchr < 8'h20 || curchr == 8'h7f) ? 11'h0: (curchr-8'h20)*20+rowcnt;
// Enable drive for data output
assign data = read ? datao: 8'bz;
assign attr = read ? attro: 8'bz;
endmodule
////////////////////////////////////////////////////////////////////////////////
//
// CHARACTER ROM
//
// Contains the 20x8 characters used in a 640x480, 25 lines of 80 characters
// display.
//
module chrrom(addr, data);
input [10:0] addr;
output [7:0] data;
reg [7:0] data;
always @(addr) case (addr)
// Character set starting with space, and ending with '~'
// The design of characters is such that they are embedded into an odd
// number of pixels, 7x19, with one space pixel to the right and bottom
// of each cell. The character layout works best on an odd cell width
// and height, because this gives a clear center to the character.
// Each characters baseline is marked. The baseline is where the capital
// characters sit, and only the descenders go below this position.
11'h000: data = 8'b00000000; // ' '
11'h001: data = 8'b00000000;
11'h002: data = 8'b00000000;
11'h003: data = 8'b00000000;
11'h004: data = 8'b00000000;
11'h005: data = 8'b00000000;
11'h006: data = 8'b00000000;
11'h007: data = 8'b00000000;
11'h008: data = 8'b00000000;
11'h009: data = 8'b00000000;
11'h00A: data = 8'b00000000;
11'h00B: data = 8'b00000000;
11'h00C: data = 8'b00000000; // B
11'h00D: data = 8'b00000000;
11'h00E: data = 8'b00000000;
11'h00F: data = 8'b00000000;
11'h010: data = 8'b00000000;
11'h011: data = 8'b00000000;
11'h012: data = 8'b00000000;
11'h013: data = 8'b00000000;
11'h014: data = 8'b00010000; // '!'
11'h015: data = 8'b00010000;
11'h016: data = 8'b00010000;
11'h017: data = 8'b00010000;
11'h018: data = 8'b00010000;
11'h019: data = 8'b00010000;
11'h01A: data = 8'b00010000;
11'h01B: data = 8'b00010000;
11'h01C: data = 8'b00010000;
11'h01D: data = 8'b00010000;
11'h01E: data = 8'b00000000;
11'h01F: data = 8'b00010000;
11'h020: data = 8'b00010000; // B
11'h021: data = 8'b00000000;
11'h022: data = 8'b00000000;
11'h023: data = 8'b00000000;
11'h024: data = 8'b00000000;
11'h025: data = 8'b00000000;
11'h026: data = 8'b00000000;
11'h027: data = 8'b00000000;
11'h028: data = 8'b00000000; // '"'
11'h029: data = 8'b00101000;
11'h02A: data = 8'b00101000;
11'h02B: data = 8'b00000000;
11'h02C: data = 8'b00000000;
11'h02D: data = 8'b00000000;
11'h02E: data = 8'b00000000;
11'h02F: data = 8'b00000000;
11'h030: data = 8'b00000000;
11'h031: data = 8'b00000000;
11'h032: data = 8'b00000000;
11'h033: data = 8'b00000000;
11'h034: data = 8'b00000000; // B
11'h035: data = 8'b00000000;
11'h036: data = 8'b00000000;
11'h037: data = 8'b00000000;
11'h038: data = 8'b00000000;
11'h039: data = 8'b00000000;
11'h03A: data = 8'b00000000;
11'h03B: data = 8'b00000000;
11'h03C: data = 8'b01000100; // '#'
11'h03D: data = 8'b01000100;
11'h03E: data = 8'b01000100;
11'h03F: data = 8'b01000100;
11'h040: data = 8'b11111110;
11'h041: data = 8'b01000100;
11'h042: data = 8'b01000100;
11'h043: data = 8'b01000100;
11'h044: data = 8'b11111110;
11'h045: data = 8'b01000100;
11'h046: data = 8'b01000100;
11'h047: data = 8'b01000100;
11'h048: data = 8'b01000100; // B
11'h049: data = 8'b00000000;
11'h04A: data = 8'b00000000;
11'h04B: data = 8'b00000000;
11'h04C: data = 8'b00000000;
11'h04D: data = 8'b00000000;
11'h04E: data = 8'b00000000;
11'h04F: data = 8'b00000000;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -