//============================================================================
|
//============================================================================
|
// Implementation of the Sinclair ZX Spectrum ULA
|
// Sinclair ZX Spectrum ULA
|
//
|
//
|
// This module contains video support.
|
// This module contains video support.
|
//
|
//
|
// Note: There is no reset signal in this VGA design since all relevant
|
// Note: There is no reset signal in this VGA design since all relevant
|
// counters will reset themselves within one display frame as the
|
// counters will reset themselves within one display frame as the
|
// pixel clock keeps ticking.
|
// pixel clock keeps ticking.
|
//
|
//
|
// Copyright (C) 2014-2016 Goran Devic
|
// Copyright (C) 2014-2016 Goran Devic
|
//
|
//
|
// This program is free software; you can redistribute it and/or modify it
|
// This program is free software; you can redistribute it and/or modify it
|
// under the terms of the GNU General Public License as published by the Free
|
// under the terms of the GNU General Public License as published by the Free
|
// Software Foundation; either version 2 of the License, or (at your option)
|
// Software Foundation; either version 2 of the License, or (at your option)
|
// any later version.
|
// any later version.
|
//
|
//
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
// more details.
|
// more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
//============================================================================
|
//============================================================================
|
module video
|
module video
|
(
|
(
|
input wire clk_pix, // Input VGA pixel clock of 25.175 MHz
|
input wire clk_pix, // Input VGA pixel clock of 25.175 MHz
|
|
|
output wire [3:0] VGA_R, // Output VGA R component
|
output wire [3:0] VGA_R, // Output VGA R component
|
output wire [3:0] VGA_G, // Output VGA G component
|
output wire [3:0] VGA_G, // Output VGA G component
|
output wire [3:0] VGA_B, // Output VGA B component
|
output wire [3:0] VGA_B, // Output VGA B component
|
output reg VGA_HS, // Output VGA horizontal sync
|
output reg VGA_HS, // Output VGA horizontal sync
|
output reg VGA_VS, // Output VGA vertical sync
|
output reg VGA_VS, // Output VGA vertical sync
|
output wire vs_nintr, // Vertical retrace interrupt
|
output wire vs_nintr, // Vertical retrace interrupt
|
|
|
output wire [12:0] vram_address,// Address request to the video RAM
|
output wire [12:0] vram_address,// Address request to the video RAM
|
input wire [7:0] vram_data, // Data read from the video RAM
|
input wire [7:0] vram_data, // Data read from the video RAM
|
|
|
input wire [2:0] border // Border color index value
|
input wire [2:0] border // Border color index value
|
);
|
);
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// VGA 640x480 Sync pulses generator
|
// VGA 640x480 Sync pulses generator
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
reg [9:0] vga_hc; // Horizontal counter
|
reg [9:0] vga_hc; // Horizontal counter
|
reg [9:0] vga_vc; // Vertical counter
|
reg [9:0] vga_vc; // Vertical counter
|
reg [4:0] frame; // Frame counter, used for the flash attribute
|
reg [4:0] frame; // Frame counter, used for the flash attribute
|
|
|
always @(posedge clk_pix)
|
always @(posedge clk_pix)
|
begin
|
begin
|
vga_hc <= vga_hc + 10'b1; // With each pixel clock, advance the horizontal counter
|
vga_hc <= vga_hc + 10'b1; // With each pixel clock, advance the horizontal counter
|
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
// Horizontal sync and line end timings
|
// Horizontal sync and line end timings
|
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
case (vga_hc)
|
case (vga_hc)
|
96: VGA_HS <= 1;
|
96: VGA_HS <= 1;
|
800: begin
|
800: begin
|
VGA_HS <= 0;
|
VGA_HS <= 0;
|
vga_hc <= 0;
|
vga_hc <= 0;
|
vga_vc <= vga_vc + 10'b1;
|
vga_vc <= vga_vc + 10'b1;
|
end
|
end
|
endcase
|
endcase
|
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
// Vertical sync and display end timings
|
// Vertical sync and display end timings
|
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
case (vga_vc)
|
case (vga_vc)
|
2: VGA_VS <= 1;
|
2: VGA_VS <= 1;
|
525: begin
|
525: begin
|
VGA_VS <= 0;
|
VGA_VS <= 0;
|
vga_vc <= 0;
|
vga_vc <= 0;
|
frame <= frame + 5'b1;
|
frame <= frame + 5'b1;
|
end
|
end
|
endcase
|
endcase
|
end
|
end
|
|
|
// Generate interrupt at around the time of the vertical retrace start
|
// Generate interrupt at around the time of the vertical retrace start
|
assign vs_nintr = (vga_vc=='0 && vga_hc[9:7]=='0)? '0 : '1;
|
assign vs_nintr = (vga_vc=='0 && vga_hc[9:7]=='0)? '0 : '1;
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// VGA active display area 640x480
|
// VGA active display area 640x480
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
wire disp_enable;
|
wire disp_enable;
|
assign disp_enable = vga_hc>=144 && vga_hc<784 && vga_vc>=35 && vga_vc<515;
|
assign disp_enable = vga_hc>=144 && vga_hc<784 && vga_vc>=35 && vga_vc<515;
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Fetch screen data from RAM based on the current video counters
|
// Fetch screen data from RAM based on the current video counters
|
// Spectrum resolution of 256x192 is line-doubled to 512x384 sub-frame
|
// Spectrum resolution of 256x192 is line-doubled to 512x384 sub-frame
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
wire screen_en;
|
wire screen_en;
|
assign screen_en = vga_hc>=208 && vga_hc<720 && vga_vc>=83 && vga_vc<467;
|
assign screen_en = vga_hc>=208 && vga_hc<720 && vga_vc>=83 && vga_vc<467;
|
|
|
reg [7:0] bits_prefetch; // Line bitmap data prefetch register
|
reg [7:0] bits_prefetch; // Line bitmap data prefetch register
|
reg [7:0] attr_prefetch; // Attribute data prefetch register
|
reg [7:0] attr_prefetch; // Attribute data prefetch register
|
|
|
// At the first clock of each new character, prefetch values are latched into these:
|
// At the first clock of each new character, prefetch values are latched into these:
|
reg [7:0] bits; // Current line bitmap data register
|
reg [7:0] bits; // Current line bitmap data register
|
reg [7:0] attr; // Current attribute data register
|
reg [7:0] attr; // Current attribute data register
|
|
|
wire [4:0] pix_x; // Column 0-31
|
wire [4:0] pix_x; // Column 0-31
|
wire [7:0] pix_y; // Active display pixel Y coordinate
|
wire [7:0] pix_y; // Active display pixel Y coordinate
|
// We use 16 clocks for 1 byte of display; also prefetch 1 byte (+16)
|
// We use 16 clocks for 1 byte of display; also prefetch 1 byte (+16)
|
wire [9:0] xd = vga_hc-10'd192; // =vga_hc-208+16
|
wire [9:0] xd = vga_hc-10'd192; // =vga_hc-208+16
|
assign pix_x = xd[8:4]; // Effectively divide by 16
|
assign pix_x = xd[8:4]; // Effectively divide by 16
|
wire [9:0] yd = vga_vc-10'd83; // Lines are (also) doubled vertically
|
wire [9:0] yd = vga_vc-10'd83; // Lines are (also) doubled vertically
|
assign pix_y = yd[8:1]; // Effectively divide by 2
|
assign pix_y = yd[8:1]; // Effectively divide by 2
|
|
|
always @(posedge clk_pix)
|
always @(posedge clk_pix)
|
begin
|
begin
|
case (vga_hc[3:0])
|
case (vga_hc[3:0])
|
// Format the address into the bitmap which is a swizzle of coordinate parts
|
// Format the address into the bitmap which is a swizzle of coordinate parts
|
10: vram_address <= {pix_y[7:6], pix_y[2:0], pix_y[5:3], pix_x};
|
10: vram_address <= {pix_y[7:6], pix_y[2:0], pix_y[5:3], pix_x};
|
12: begin
|
12: begin
|
bits_prefetch <= vram_data;
|
bits_prefetch <= vram_data;
|
// Format the address into the attribute map
|
// Format the address into the attribute map
|
vram_address <= {3'b110, pix_y[7:3], pix_x};
|
vram_address <= {3'b110, pix_y[7:3], pix_x};
|
end
|
end
|
14: attr_prefetch <= vram_data;
|
14: attr_prefetch <= vram_data;
|
// Last tick before a new character: load working bitmap and attribute registers
|
// Last tick before a new character: load working bitmap and attribute registers
|
15: begin
|
15: begin
|
attr <= attr_prefetch;
|
attr <= attr_prefetch;
|
bits <= bits_prefetch;
|
bits <= bits_prefetch;
|
end
|
end
|
endcase
|
endcase
|
end
|
end
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Pixel data generator
|
// Pixel data generator
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
wire [2:0] ink; // INK color (index into the palette)
|
wire [2:0] ink; // INK color (index into the palette)
|
wire [2:0] paper; // PAPER color
|
wire [2:0] paper; // PAPER color
|
wire bright; // BRIGHT attribute bit
|
wire bright; // BRIGHT attribute bit
|
wire flash; // FLASH attribute bit
|
wire flash; // FLASH attribute bit
|
wire pixbit; // Current pixel to render
|
wire pixbit; // Current pixel to render
|
wire inverted; // Are the pixel's attributes inverted?
|
wire inverted; // Are the pixel's attributes inverted?
|
|
|
// Output a pixel bit based on the VGA horizontal counter. This could have been
|
// Output a pixel bit based on the VGA horizontal counter. This could have been
|
// a shift register but a mux works as well since we are writing out each pixel
|
// a shift register but a mux works as well since we are writing out each pixel
|
// twice (required by this VGA clock rate)
|
// twice (required by this VGA clock rate)
|
always @(*) // always_comb
|
always @(*) // always_comb
|
begin
|
begin
|
case (vga_hc[3:1])
|
case (vga_hc[3:1])
|
0: pixbit = bits[7];
|
0: pixbit = bits[7];
|
1: pixbit = bits[6];
|
1: pixbit = bits[6];
|
2: pixbit = bits[5];
|
2: pixbit = bits[5];
|
3: pixbit = bits[4];
|
3: pixbit = bits[4];
|
4: pixbit = bits[3];
|
4: pixbit = bits[3];
|
5: pixbit = bits[2];
|
5: pixbit = bits[2];
|
6: pixbit = bits[1];
|
6: pixbit = bits[1];
|
7: pixbit = bits[0];
|
7: pixbit = bits[0];
|
endcase
|
endcase
|
end
|
end
|
|
|
assign flash = attr[7];
|
assign flash = attr[7];
|
assign bright = attr[6];
|
assign bright = attr[6];
|
assign inverted = flash & frame[4];
|
assign inverted = flash & frame[4];
|
assign ink = inverted? attr[5:3] : attr[2:0];
|
assign ink = inverted? attr[5:3] : attr[2:0];
|
assign paper = inverted? attr[2:0] : attr[5:3];
|
assign paper = inverted? attr[2:0] : attr[5:3];
|
|
|
// The final color index depends on where we are (active display area, border) and
|
// The final color index depends on where we are (active display area, border) and
|
// whether we are rendering INK or PAPER color, including the brightness bit
|
// whether we are rendering INK or PAPER color, including the brightness bit
|
assign cindex = screen_en? pixbit? {bright,ink} : {bright,paper} : {1'b0,border[2:0]};
|
assign cindex = screen_en? pixbit? {bright,ink} : {bright,paper} : {1'b0,border[2:0]};
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// Color lookup table
|
// Color lookup table
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
wire [3:0] cindex;
|
wire [3:0] cindex;
|
wire [11:0] pix_rgb;
|
wire [11:0] pix_rgb;
|
|
|
always @(*) // always_comb
|
always @(*) // always_comb
|
begin
|
begin
|
case (cindex[3:0])
|
case (cindex[3:0])
|
// Normal color
|
// Normal color
|
0: pix_rgb = 12'h000; // BLACK
|
0: pix_rgb = 12'h000; // BLACK
|
1: pix_rgb = 12'h00D; // BLUE
|
1: pix_rgb = 12'h00D; // BLUE
|
2: pix_rgb = 12'hD00; // RED
|
2: pix_rgb = 12'hD00; // RED
|
3: pix_rgb = 12'hD0D; // MAGENTA
|
3: pix_rgb = 12'hD0D; // MAGENTA
|
4: pix_rgb = 12'h0D0; // GREEN
|
4: pix_rgb = 12'h0D0; // GREEN
|
5: pix_rgb = 12'h0DD; // CYAN
|
5: pix_rgb = 12'h0DD; // CYAN
|
6: pix_rgb = 12'hDD0; // YELLOW
|
6: pix_rgb = 12'hDD0; // YELLOW
|
7: pix_rgb = 12'hDDD; // WHITE
|
7: pix_rgb = 12'hDDD; // WHITE
|
// "Bright" bit is set
|
// "Bright" bit is set
|
8: pix_rgb = 12'h000; // BLACK remains black
|
8: pix_rgb = 12'h000; // BLACK remains black
|
9: pix_rgb = 12'h00F;
|
9: pix_rgb = 12'h00F;
|
10: pix_rgb = 12'hF00;
|
10: pix_rgb = 12'hF00;
|
11: pix_rgb = 12'hF0F;
|
11: pix_rgb = 12'hF0F;
|
12: pix_rgb = 12'h0F0;
|
12: pix_rgb = 12'h0F0;
|
13: pix_rgb = 12'h0FF;
|
13: pix_rgb = 12'h0FF;
|
14: pix_rgb = 12'hFF0;
|
14: pix_rgb = 12'hFF0;
|
15: pix_rgb = 12'hFFF;
|
15: pix_rgb = 12'hFFF;
|
endcase
|
endcase
|
end
|
end
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// VGA RGB output drivers
|
// VGA RGB output drivers
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
assign VGA_R[3:0] = disp_enable? pix_rgb[11:8] : '0;
|
assign VGA_R[3:0] = disp_enable? pix_rgb[11:8] : '0;
|
assign VGA_G[3:0] = disp_enable? pix_rgb[7:4] : '0;
|
assign VGA_G[3:0] = disp_enable? pix_rgb[7:4] : '0;
|
assign VGA_B[3:0] = disp_enable? pix_rgb[3:0] : '0;
|
assign VGA_B[3:0] = disp_enable? pix_rgb[3:0] : '0;
|
|
|
endmodule
|
endmodule
|
|
|