aboutsummaryrefslogtreecommitdiffstats
path: root/display.v
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@trantor.hos.anvin.org>2010-09-18 15:23:56 -0700
committerH. Peter Anvin <hpa@trantor.hos.anvin.org>2010-09-18 15:23:56 -0700
commit58b28518d10b10b92143f64a6e438c214bb7860d (patch)
tree1cd1b15b96b5a5b98e0c9bd393e55e17052f1bd0 /display.v
parent7318780bb586bc8ea3f5cc6d55c996cae84a8cc4 (diff)
downloadabc80-58b28518d10b10b92143f64a6e438c214bb7860d.tar.gz
abc80-58b28518d10b10b92143f64a6e438c214bb7860d.tar.xz
abc80-58b28518d10b10b92143f64a6e438c214bb7860d.zip
abc80: use an original-compatible 6x10 character matrix
Use a 6x10 character matrix instead of a "cheating" 8x16. This is the resolution used by the original system. We use 480-line VGA mode to display this, so we have to use double-scanning.
Diffstat (limited to 'display.v')
-rw-r--r--display.v236
1 files changed, 132 insertions, 104 deletions
diff --git a/display.v b/display.v
index b01c8b2..3ac13e9 100644
--- a/display.v
+++ b/display.v
@@ -1,5 +1,5 @@
module display (
- input clk,
+ input clk, // 18.75 MHz
input width,
input reverse,
@@ -10,34 +10,36 @@ module display (
output [10:0] a,
input [7:0] d,
output [10:0] ga,
- input [7:0] gd,
+ input [5:0] gd,
output reg [2:0] rgb,
output reg vsync,
output reg hsync
);
-// We use the standard VGA "text" monitor timings mode,
-// htime = 31.77 us (31.47 kHz), vtime = 14.27 ms (70 Hz)
-// The standard VGA uses a pixel clock of 25.175 MHz, we use 25 MHz,
-// and round to multiples of 8. The error is about 0.8%, which
-// is far, far less than the margin of error in real systems.
+// We use the standard VGA 640x480 monitor timings mode, htime = 31.77
+// us (31.47 kHz), vtime = 16.68 ms (60 Hz) The standard VGA uses a
+// pixel clock of 25.175 MHz, we use 18.75 MHz, which is 25*3/4 for
+// 480 horizontal pixels; the error is about 0.8%, which is far, far
+// less than the margin of error in real systems.
//
// This gives us the following timings:
//
-// Horizontal: 96 pixels (12 char) sync
-// 40 pixels ( 5 char) back porch/border
-// 640 pixels (80 char) graphics
-// 24 pixels ( 3 char) front porch
+// Horizontal:
+// 480 pixels (80 char) graphics
+// 12 pixels ( 2 char) back porch/border
+// 72 pixels (12 char) sync
+// 30 pixels ( 5 char) front porch
// ----------
-// 800 pixels
+// 594 pixels (31.68 us)
//
-// Vertical: 2 lines sync
-// 41 lines back porch/border
-// 384 lines graphics (24 rows @ 16 pixels)
-// 22 lines front porch
+// Vertical:
+// 480 lines graphics (24 rows x 10 pixels x 2 scans/pixel)
+// 10 lines back porch/border
+// 2 lines sync
+// 33 lines front porch
// ---------
-// 449 lines
+// 525 lines
//
// In this implementation we start timing with the display area in both
// cases, *except* that we prefetch by one character, and therefore start
@@ -49,29 +51,35 @@ module display (
//
// VGA monitors used the sync polarity to determine the mode,
// especially the desired aspect ratio. Therefore, we want to use -hsync
-// and +vsync, meaning hsync is active low and vsync is active high.
+// and -vsync, - meaning active low, + meaning active high.
- parameter x_blank = 640+8;
- parameter x_sync = x_blank+32;
- parameter x_front = x_sync+96;
- parameter x_max = 800;
+ parameter x_blank = 480+6;
+ parameter x_sync = x_blank+(12-6);
+ parameter x_front = x_sync+72;
+ parameter x_max = 594;
- parameter y_blank = 384;
- parameter y_sync = y_blank+30;
+ parameter y_blank = 480;
+ parameter y_sync = y_blank+10;
parameter y_front = y_sync+2;
- parameter y_max = 449;
+ parameter y_max = 525;
- parameter hsync_polarity = 1'b0; // -hsync
- parameter vsync_polarity = 1'b1; // +vsync
+ parameter hsync_minus = 1'b1; // -hsync
+ parameter vsync_minus = 1'b1; // -vsync
reg [9:0] x; // Horizontal pixel count
- reg [8:0] y; // Vertical pixel count
+ reg [9:0] y; // Vertical pixel count
+ reg [6:0] xchr; // Character column (0..99)
+ reg [2:0] xpxl; // Pixel column in character (0..5)
+ reg [4:0] ychr; // Character row (0..26)
+ reg [3:0] ypxl; // Pixel row in character (0..9)
+ reg [4:0] ylu; // Previous character line
+
wire xvideo; // Non-blanked in the x direction
wire yvideo; // Non-blanked in the y direction
- wire [10:0] a80; // Character row assuming 80 columns
- wire [10:0] a80u; // Character row assuming 80 columns (up 1 line)
- reg [5:0] scan_counter; // Counter of total scans (for flashing et al)
- reg [7:0] pixrow; // One character worth of pixels
+ wire [10:0] a80; // Memory address assuming 80 columns
+ wire [10:0] a80u; // Memory address assuming 80 columns (-1 line)
+ reg [4:0] scan_counter; // Counter of total scans (for flashing et al)
+ reg [5:0] pixrow; // One character worth of pixels
reg prefetch; // True for the prefetch character position
// For the current text line
@@ -104,38 +112,42 @@ module display (
wire gsep = wasdble ? wasgsep : isgsep;
wire do_flsh = wasdble ? wasflsh : isflsh;
wire do_hide = wasdble ? washide : ishide;
+
+ // Should we advance the character pixel?
+ wire advance = width | x[0];
// Address mapping for 80 characters; for 40 characters
// we shift this left by 1
- assign a80[3:0] = x[6:3];
- wire [3:0] xmiddle = { 1'b0, x[9:7] };
- wire [3:0] ymiddle = { y[8:7] , y[8:7] };
+ assign a80[3:0] = xchr[3:0];
+ wire [3:0] xmiddle = { 1'b0, xchr[6:4] };
+ wire [3:0] ymiddle = { ychr[4:3] , ychr[4:3] };
assign a80[7:4] = xmiddle+ymiddle;
- assign a80[10:8] = y[6:4];
+ assign a80[10:8] = ychr[2:0];
// Address mapping for 80 characters minus one line
- wire [4:0] ylu = y[8:4] - 1;
- assign a80u[3:0] = x[6:3];
+ assign a80u[3:0] = xchr[3:0];
wire [3:0] yumiddle = { ylu[4:3] , ylu[4:3] };
assign a80u[7:4] = xmiddle+yumiddle;
assign a80u[10:8] = ylu[2:0];
// Final address mapping
// Note: We read the current char between pixels 0 and 1,
- // and the char above between pixels 2 and 3; hence the use of x[1].
- wire [10:0] amap = x[1] ? a80u : a80;
+ // and the char above between pixels 2 and 3; hence the use of xchr[1].
+ wire [10:0] amap = xchr[1] ? a80u : a80;
assign a = width ? amap[10:0] : { 1'b1, amap[10:1] };
// Character generator address mapping
assign ga[10:4] = somechar[6:0];
- assign ga[3:0] = wasdble ? { 1'b1, y[3:1] } : isdble ? { 1'b0, y[3:1] } : y[3:0];
+ assign ga[3:0] = wasdble ? { 1'b1, ypxl[3:1] } :
+ isdble ? { 1'b0, ypxl[3:1] } :
+ ypxl[3:0];
// Video enable signal
assign xvideo = ( x < x_blank );
assign yvideo = ( y < y_blank );
// Flashing
- wire flash_on = scan_counter[5];
+ wire flash_on = scan_counter[4];
// Inverse video. The normal is a flashing inverse cursor, but
// if "noblink" is asserted the cursor is steady inverted. If
@@ -146,13 +158,11 @@ module display (
always @(posedge clk)
begin
- // The x[9:4] means ignore the first character prefetch. We really
- // should realign the counters, here.
if ( xvideo & yvideo & ~prefetch )
if ( testpattern )
// Pixel test pattern for LCD monitor calibration
rgb <= {3{x[0] ^ y[0]}};
- else if ( pixrow[7] & ~(do_flsh & ~flash_on) & ~(do_hide & ~reveal) )
+ else if ( pixrow[5] & ~(do_flsh & ~flash_on) & ~(do_hide & ~reveal) )
rgb <= fg ^ {3{invert}};
else
rgb <= bg ^ {3{invert}};
@@ -160,19 +170,19 @@ module display (
rgb <= 3'b0; // Blank
// Sync pulses
- vsync <= ( y >= y_sync && y < y_front ) ^ ~vsync_polarity;
- hsync <= ( x >= x_sync && x < x_front ) ^ ~hsync_polarity;
-
+ vsync <= ( y >= y_sync && y < y_front ) ^ vsync_minus;
+ hsync <= ( x >= x_sync && x < x_front ) ^ hsync_minus;
+
// Rotating shift register; may be overridden by the below
// The rotation is so that if we're in GHOL mode we already
- // have the reserved value
- if ( width | x[0] )
- pixrow <= { pixrow[6:0], pixrow[7] };
+ // have the previous value
+ if ( advance )
+ pixrow <= { pixrow[4:0], pixrow[5] };
- // This code is run 8 times per character; regardless of width
- if ( width | ~x[3] )
+ // This code is run 6 times per character; regardless of width
+ if ( advance )
begin
- case ( x[2:0] )
+ case ( xpxl )
3'b001:
begin
// Load and process character
@@ -182,10 +192,10 @@ module display (
3'b011:
begin
// Load and process previous-line character
- thatchar <= (y[8:4] == 0) ? 8'h00 : d;
+ thatchar <= (ychr == 0) ? 8'h00 : d;
end
- 3'b111:
+ 3'b101:
begin
// Load a new pixel row?
if ( xvideo )
@@ -268,72 +278,62 @@ module display (
begin
// Generate graphical character
case ( ga[3:0] )
- 4'h0, 4'h1, 4'h2, 4'h3:
+ 4'h0, 4'h1:
begin
- pixrow[7] <= somechar[0];
- pixrow[6] <= somechar[0];
pixrow[5] <= somechar[0];
- pixrow[4] <= somechar[0] & ~gsep;
- pixrow[3] <= somechar[1];
+ pixrow[4] <= somechar[0];
+ pixrow[3] <= somechar[0] & ~gsep;
pixrow[2] <= somechar[1];
pixrow[1] <= somechar[1];
pixrow[0] <= somechar[1] & ~gsep;
end
- 4'h4:
+ 4'h2:
begin
- pixrow[7] <= somechar[0] & ~gsep;
- pixrow[6] <= somechar[0] & ~gsep;
pixrow[5] <= somechar[0] & ~gsep;
pixrow[4] <= somechar[0] & ~gsep;
- pixrow[3] <= somechar[1] & ~gsep;
+ pixrow[3] <= somechar[0] & ~gsep;
pixrow[2] <= somechar[1] & ~gsep;
pixrow[1] <= somechar[1] & ~gsep;
pixrow[0] <= somechar[1] & ~gsep;
end
- 4'h5, 4'h6, 4'h7, 4'h8, 4'h9:
+ 4'h3, 4'h4, 4'h5:
begin
- pixrow[7] <= somechar[2];
- pixrow[6] <= somechar[2];
pixrow[5] <= somechar[2];
- pixrow[4] <= somechar[2] & ~gsep;
- pixrow[3] <= somechar[3];
+ pixrow[4] <= somechar[2];
+ pixrow[3] <= somechar[2] & ~gsep;
pixrow[2] <= somechar[3];
pixrow[1] <= somechar[3];
pixrow[0] <= somechar[3] & ~gsep;
end
- 4'hA:
+ 4'h6:
begin
- pixrow[7] <= somechar[2] & ~gsep;
- pixrow[6] <= somechar[2] & ~gsep;
pixrow[5] <= somechar[2] & ~gsep;
pixrow[4] <= somechar[2] & ~gsep;
- pixrow[3] <= somechar[3] & ~gsep;
+ pixrow[3] <= somechar[2] & ~gsep;
pixrow[2] <= somechar[3] & ~gsep;
pixrow[1] <= somechar[3] & ~gsep;
pixrow[0] <= somechar[3] & ~gsep;
end
- 4'hB, 4'hC, 4'hD, 4'hE:
+ 4'h7, 4'h8:
begin
- pixrow[7] <= somechar[4];
- pixrow[6] <= somechar[4];
pixrow[5] <= somechar[4];
- pixrow[4] <= somechar[4] & ~gsep;
- pixrow[3] <= somechar[6];
+ pixrow[4] <= somechar[4];
+ pixrow[3] <= somechar[4] & ~gsep;
pixrow[2] <= somechar[6];
pixrow[1] <= somechar[6];
pixrow[0] <= somechar[6] & ~gsep;
end
- 4'hF:
+ 4'h9:
begin
- pixrow[7] <= somechar[4] & ~gsep;
- pixrow[6] <= somechar[4] & ~gsep;
pixrow[5] <= somechar[4] & ~gsep;
pixrow[4] <= somechar[4] & ~gsep;
- pixrow[3] <= somechar[6] & ~gsep;
+ pixrow[3] <= somechar[4] & ~gsep;
pixrow[2] <= somechar[6] & ~gsep;
pixrow[1] <= somechar[6] & ~gsep;
pixrow[0] <= somechar[6] & ~gsep;
end
+ default:
+ pixrow <= 5'bxxxxxx;
endcase // case( ga[3:0] )
end // if ( wasdble ? (wasgraph & thatchar[5]) : (isgraph & thischar[5]) )
else if ( (somechar[6:5] != 2'b00) | (wasdble ? wasgrel : isgrel) )
@@ -343,43 +343,71 @@ module display (
prefetch <= 1'b0;
end // if ( xvideo )
inverse <= somechar[7];
- end // case: 3'b111
- endcase // case( x[2:0] )
- end // if ( width | ~x[3] )
+ end // case: 3'b101
+ endcase // case( xpxl )
+ end // if ( advance )
// Counters
if ( x == x_max-1 )
begin
- x <= 0;
+ x <= 10'd0;
+ xchr <= 7'd0;
+ xpxl <= 3'd0;
pixrow <= 8'b0; // Read-ahead spot is blank
prefetch <= 1'b1; // Prefetch character
- inverse <= 0; // Not inverse video
+ inverse <= 1'b0; // Not inverse video
curfg <= 3'b111; // Default fg is white
curbg <= 3'b000; // Default bg is black
- isgraph <= 0; // Not graphic mode
- isflsh <= 0; // Not flashing
- isgsep <= 0; // Not separated
- isdble <= 0; // Not double
- isgrel <= 1; // Release graphics
- ishide <= 0; // Not hidden
+ isgraph <= 1'b0; // Not graphic mode
+ isflsh <= 1'b0; // Not flashing
+ isgsep <= 1'b0; // Not separated
+ isdble <= 1'b0; // Not double
+ isgrel <= 1'b1; // Release graphics
+ ishide <= 1'b0; // Not hidden
oldfg <= 3'b111; // Default fg is white
oldbg <= 3'b000; // Default bg is black
- wasgraph <= 0; // Not graphic mode
- wasflsh <= 0; // Not flashing
- wasgsep <= 0; // Not separated
- wasdble <= 0; // Not double
- wasgrel <= 1; // Release graphics
- washide <= 0; // Not hidden
+ wasgraph <= 1'b0; // Not graphic mode
+ wasflsh <= 1'b0; // Not flashing
+ wasgsep <= 1'b0; // Not separated
+ wasdble <= 1'b0; // Not double
+ wasgrel <= 1'b1; // Release graphics
+ washide <= 1'b0; // Not hidden
+ ylu <= ychr; // Previous character row
if ( y == y_max-1 )
begin
- y <= 0;
+ y <= 10'd0;
+ ychr <= 6'd0;
+ ypxl <= 5'd0;
scan_counter <= scan_counter + 1;
end
else
- y <= y + 1;
+ begin
+ if ( y[0] ) // Double scanning
+ begin
+ if ( ypxl == 4'd9 )
+ begin
+ ypxl <= 4'd0;
+ ychr <= ychr + 1;
+ end
+ else
+ ypxl <= ypxl + 1;
+ end
+ y <= y + 1;
+ end // else: !if( y == y_max-1 )
end // if ( x == x_max-1 )
else
- x <= x + 1;
+ begin
+ if ( width | x[0] )
+ begin
+ if ( xpxl == 3'd5 )
+ begin
+ xpxl <= 0;
+ xchr <= xchr + 1;
+ end
+ else
+ xpxl <= xpxl + 1;
+ end
+ x <= x + 1;
+ end // else: !if( x == x_max-1 )
end // always @ (posedge clk)
endmodule // display
- \ No newline at end of file