1 |
8 |
gdevic |
//============================================================================
|
2 |
10 |
gdevic |
// Sinclair ZX Spectrum ULA
|
3 |
8 |
gdevic |
//
|
4 |
|
|
// This module contains video support.
|
5 |
|
|
//
|
6 |
|
|
// Note: There is no reset signal in this VGA design since all relevant
|
7 |
|
|
// counters will reset themselves within one display frame as the
|
8 |
|
|
// pixel clock keeps ticking.
|
9 |
|
|
//
|
10 |
|
|
// Copyright (C) 2014-2016 Goran Devic
|
11 |
|
|
//
|
12 |
|
|
// This program is free software; you can redistribute it and/or modify it
|
13 |
|
|
// under the terms of the GNU General Public License as published by the Free
|
14 |
|
|
// Software Foundation; either version 2 of the License, or (at your option)
|
15 |
|
|
// any later version.
|
16 |
|
|
//
|
17 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
18 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
20 |
|
|
// more details.
|
21 |
|
|
//
|
22 |
|
|
// You should have received a copy of the GNU General Public License along
|
23 |
|
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
24 |
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
25 |
|
|
//============================================================================
|
26 |
|
|
module video
|
27 |
|
|
(
|
28 |
|
|
input wire clk_pix, // Input VGA pixel clock of 25.175 MHz
|
29 |
|
|
|
30 |
|
|
output wire [3:0] VGA_R, // Output VGA R component
|
31 |
|
|
output wire [3:0] VGA_G, // Output VGA G component
|
32 |
|
|
output wire [3:0] VGA_B, // Output VGA B component
|
33 |
|
|
output reg VGA_HS, // Output VGA horizontal sync
|
34 |
|
|
output reg VGA_VS, // Output VGA vertical sync
|
35 |
|
|
output wire vs_nintr, // Vertical retrace interrupt
|
36 |
|
|
|
37 |
|
|
output wire [12:0] vram_address,// Address request to the video RAM
|
38 |
|
|
input wire [7:0] vram_data, // Data read from the video RAM
|
39 |
|
|
|
40 |
|
|
input wire [2:0] border // Border color index value
|
41 |
|
|
);
|
42 |
|
|
|
43 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
44 |
|
|
// VGA 640x480 Sync pulses generator
|
45 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
46 |
|
|
reg [9:0] vga_hc; // Horizontal counter
|
47 |
|
|
reg [9:0] vga_vc; // Vertical counter
|
48 |
|
|
reg [4:0] frame; // Frame counter, used for the flash attribute
|
49 |
|
|
|
50 |
|
|
always @(posedge clk_pix)
|
51 |
|
|
begin
|
52 |
|
|
vga_hc <= vga_hc + 10'b1; // With each pixel clock, advance the horizontal counter
|
53 |
|
|
//---------------------------------------------------------------
|
54 |
|
|
// Horizontal sync and line end timings
|
55 |
|
|
//---------------------------------------------------------------
|
56 |
|
|
case (vga_hc)
|
57 |
|
|
96: VGA_HS <= 1;
|
58 |
|
|
800: begin
|
59 |
|
|
VGA_HS <= 0;
|
60 |
|
|
vga_hc <= 0;
|
61 |
|
|
vga_vc <= vga_vc + 10'b1;
|
62 |
|
|
end
|
63 |
|
|
endcase
|
64 |
|
|
//---------------------------------------------------------------
|
65 |
|
|
// Vertical sync and display end timings
|
66 |
|
|
//---------------------------------------------------------------
|
67 |
|
|
case (vga_vc)
|
68 |
|
|
2: VGA_VS <= 1;
|
69 |
|
|
525: begin
|
70 |
|
|
VGA_VS <= 0;
|
71 |
|
|
vga_vc <= 0;
|
72 |
|
|
frame <= frame + 5'b1;
|
73 |
|
|
end
|
74 |
|
|
endcase
|
75 |
|
|
end
|
76 |
|
|
|
77 |
|
|
// Generate interrupt at around the time of the vertical retrace start
|
78 |
|
|
assign vs_nintr = (vga_vc=='0 && vga_hc[9:7]=='0)? '0 : '1;
|
79 |
|
|
|
80 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
81 |
|
|
// VGA active display area 640x480
|
82 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
83 |
|
|
wire disp_enable;
|
84 |
|
|
assign disp_enable = vga_hc>=144 && vga_hc<784 && vga_vc>=35 && vga_vc<515;
|
85 |
|
|
|
86 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
87 |
|
|
// Fetch screen data from RAM based on the current video counters
|
88 |
|
|
// Spectrum resolution of 256x192 is line-doubled to 512x384 sub-frame
|
89 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
90 |
|
|
wire screen_en;
|
91 |
|
|
assign screen_en = vga_hc>=208 && vga_hc<720 && vga_vc>=83 && vga_vc<467;
|
92 |
|
|
|
93 |
|
|
reg [7:0] bits_prefetch; // Line bitmap data prefetch register
|
94 |
|
|
reg [7:0] attr_prefetch; // Attribute data prefetch register
|
95 |
|
|
|
96 |
|
|
// At the first clock of each new character, prefetch values are latched into these:
|
97 |
|
|
reg [7:0] bits; // Current line bitmap data register
|
98 |
|
|
reg [7:0] attr; // Current attribute data register
|
99 |
|
|
|
100 |
|
|
wire [4:0] pix_x; // Column 0-31
|
101 |
|
|
wire [7:0] pix_y; // Active display pixel Y coordinate
|
102 |
|
|
// We use 16 clocks for 1 byte of display; also prefetch 1 byte (+16)
|
103 |
|
|
wire [9:0] xd = vga_hc-10'd192; // =vga_hc-208+16
|
104 |
|
|
assign pix_x = xd[8:4]; // Effectively divide by 16
|
105 |
|
|
wire [9:0] yd = vga_vc-10'd83; // Lines are (also) doubled vertically
|
106 |
|
|
assign pix_y = yd[8:1]; // Effectively divide by 2
|
107 |
|
|
|
108 |
|
|
always @(posedge clk_pix)
|
109 |
|
|
begin
|
110 |
|
|
case (vga_hc[3:0])
|
111 |
|
|
// Format the address into the bitmap which is a swizzle of coordinate parts
|
112 |
|
|
10: vram_address <= {pix_y[7:6], pix_y[2:0], pix_y[5:3], pix_x};
|
113 |
|
|
12: begin
|
114 |
|
|
bits_prefetch <= vram_data;
|
115 |
|
|
// Format the address into the attribute map
|
116 |
|
|
vram_address <= {3'b110, pix_y[7:3], pix_x};
|
117 |
|
|
end
|
118 |
|
|
14: attr_prefetch <= vram_data;
|
119 |
|
|
// Last tick before a new character: load working bitmap and attribute registers
|
120 |
|
|
15: begin
|
121 |
|
|
attr <= attr_prefetch;
|
122 |
|
|
bits <= bits_prefetch;
|
123 |
|
|
end
|
124 |
|
|
endcase
|
125 |
|
|
end
|
126 |
|
|
|
127 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
128 |
|
|
// Pixel data generator
|
129 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
130 |
|
|
wire [2:0] ink; // INK color (index into the palette)
|
131 |
|
|
wire [2:0] paper; // PAPER color
|
132 |
|
|
wire bright; // BRIGHT attribute bit
|
133 |
|
|
wire flash; // FLASH attribute bit
|
134 |
|
|
wire pixbit; // Current pixel to render
|
135 |
|
|
wire inverted; // Are the pixel's attributes inverted?
|
136 |
|
|
|
137 |
|
|
// Output a pixel bit based on the VGA horizontal counter. This could have been
|
138 |
|
|
// a shift register but a mux works as well since we are writing out each pixel
|
139 |
|
|
// twice (required by this VGA clock rate)
|
140 |
|
|
always @(*) // always_comb
|
141 |
|
|
begin
|
142 |
|
|
case (vga_hc[3:1])
|
143 |
|
|
0: pixbit = bits[7];
|
144 |
|
|
1: pixbit = bits[6];
|
145 |
|
|
2: pixbit = bits[5];
|
146 |
|
|
3: pixbit = bits[4];
|
147 |
|
|
4: pixbit = bits[3];
|
148 |
|
|
5: pixbit = bits[2];
|
149 |
|
|
6: pixbit = bits[1];
|
150 |
|
|
7: pixbit = bits[0];
|
151 |
|
|
endcase
|
152 |
|
|
end
|
153 |
|
|
|
154 |
|
|
assign flash = attr[7];
|
155 |
|
|
assign bright = attr[6];
|
156 |
|
|
assign inverted = flash & frame[4];
|
157 |
|
|
assign ink = inverted? attr[5:3] : attr[2:0];
|
158 |
|
|
assign paper = inverted? attr[2:0] : attr[5:3];
|
159 |
|
|
|
160 |
|
|
// The final color index depends on where we are (active display area, border) and
|
161 |
|
|
// whether we are rendering INK or PAPER color, including the brightness bit
|
162 |
|
|
assign cindex = screen_en? pixbit? {bright,ink} : {bright,paper} : {1'b0,border[2:0]};
|
163 |
|
|
|
164 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
165 |
|
|
// Color lookup table
|
166 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
167 |
|
|
wire [3:0] cindex;
|
168 |
|
|
wire [11:0] pix_rgb;
|
169 |
|
|
|
170 |
|
|
always @(*) // always_comb
|
171 |
|
|
begin
|
172 |
|
|
case (cindex[3:0])
|
173 |
|
|
// Normal color
|
174 |
|
|
0: pix_rgb = 12'h000; // BLACK
|
175 |
|
|
1: pix_rgb = 12'h00D; // BLUE
|
176 |
|
|
2: pix_rgb = 12'hD00; // RED
|
177 |
|
|
3: pix_rgb = 12'hD0D; // MAGENTA
|
178 |
|
|
4: pix_rgb = 12'h0D0; // GREEN
|
179 |
|
|
5: pix_rgb = 12'h0DD; // CYAN
|
180 |
|
|
6: pix_rgb = 12'hDD0; // YELLOW
|
181 |
|
|
7: pix_rgb = 12'hDDD; // WHITE
|
182 |
|
|
// "Bright" bit is set
|
183 |
|
|
8: pix_rgb = 12'h000; // BLACK remains black
|
184 |
|
|
9: pix_rgb = 12'h00F;
|
185 |
|
|
10: pix_rgb = 12'hF00;
|
186 |
|
|
11: pix_rgb = 12'hF0F;
|
187 |
|
|
12: pix_rgb = 12'h0F0;
|
188 |
|
|
13: pix_rgb = 12'h0FF;
|
189 |
|
|
14: pix_rgb = 12'hFF0;
|
190 |
|
|
15: pix_rgb = 12'hFFF;
|
191 |
|
|
endcase
|
192 |
|
|
end
|
193 |
|
|
|
194 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
195 |
|
|
// VGA RGB output drivers
|
196 |
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
197 |
|
|
assign VGA_R[3:0] = disp_enable? pix_rgb[11:8] : '0;
|
198 |
|
|
assign VGA_G[3:0] = disp_enable? pix_rgb[7:4] : '0;
|
199 |
|
|
assign VGA_B[3:0] = disp_enable? pix_rgb[3:0] : '0;
|
200 |
|
|
|
201 |
|
|
endmodule
|