Files
SharpMZ/common/video.vhd
Philip Smart 4a64af4a00 Initial commit
2019-10-25 17:16:34 +01:00

1977 lines
113 KiB
VHDL

---------------------------------------------------------------------------------------------------------
--
-- Name: video.vhd
-- Created: July 2018
-- Author(s): Philip Smart
-- Description: Sharp MZ series Video logic.
-- This module fully emulates the Sharp MZ Personal Computer series video display
-- logic plus extensions for MZ80K, MZ80C, MZ1200, MZ80A, MZ700, MZ80B & MZ2000.
--
-- The display is capable of performing 40x25, 80x25 Mono/Colour display along with
-- a Programmable Character Generator, the MZ-80B/2000 Graphics Options and a bit mapped
-- 320x200/640x200 framebuffer.
--
-- The design is slightly different to the original Sharp's in that I use a dual
-- buffer technique, ie. the original 1K/2K VRAM + ARAM and a pixel mapped displaybuffer.
-- During Horizontal/Vertical blanking, the VRAM+ARAM is copied and expanded into the display
-- buffer which is then displayed during the next display window. Part of the reasoning
-- was to cut down on snow/tearing on the older K/C models (but still provide the
-- blanking signals so any original software works) and also allow the option of
-- disabling the MZ80A/700 wait states.
--
-- As an addition, I added a graphics framebuffer (320x200, 640x200 8 colours)
-- the interface to which is, at the moment, non-standard, but as I get more details
-- on add on cards, I can add mapping layers so this graphics framebuffer can be used
-- by customised software. Pixels drawn in the graphics framebuffer can be blended into
-- the main display buffer via programmable logic mode (ie. XOR, OR etc).
--
-- The MZ80B/2000 use a standard VRAM + CG for 40/80 character display with a Graphics
-- RAM option. These have been added and the output if blended into the display buffer.
--
-- A lot of timing information can be found in the docs/SharpMZ_Notes.xlsx spreadsheet,
-- but the main info is:
-- MZ80K/C/1200/A (Monochrome)
-- Signal Start End Period Comment
-- 64uS 15.625KHz
-- HDISPEN 0 320 40uS
-- HBLANK 318 510 24uS
-- BLNK 318 486 21uS
-- HSYNC 393 438 5.625uS
--
-- 16.64mS 60.10Hz
-- VDISPEN 0 200 12.8mS
-- VSYNC 219 223 256uS
-- VBLANK 201 259 3.712mS not VDISPEN
--
-- MZ700 (Colour)
-- Signal Start End Period Comment
-- 64.056uS 15.611KHz
-- HDISPEN 0 320 36.088uS
-- HBLANK 320 567 27.968uS
-- BLNK 320 548 25.7126uS
-- HSYNC 400 440 4.567375uS
--
-- 16.654mS 50.0374Hz
-- VDISPEN 0 200 12.8112mS
-- VSYNC 212 215 0.19216ms
-- VBLANK 201 311 7.1738mS not VDISPEN
--
-- A Look Up Table was added to allow for VGA display resolutions and upscaling as necessary.
-- All video parameters are now stored in the LUT.
--
-- Copyright: (c) 2018 Philip Smart <philip.smart@net2net.org>
--
-- History: July 2018 - Initial module written.
-- August 2018 - Main portions written, including the display buffer.
-- September 2018 - Added the graphics framebuffer.
-- - Reworked the transfer VRAM/GRAM -> Framebuffer logic, too conceptual
-- and caused timing issues.
-- Reworked the Framebuffer display, too conceptual.
-- Added MZ80B/MZ2000 logic.
-- October 2018 - Parameterised graphics modes via a LUT.
-- November 2018 - Added VGA upscaling.
--
---------------------------------------------------------------------------------------------------------
-- This source file 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 Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http:--www.gnu.org-licenses->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use pkgs.clkgen_pkg.all;
use pkgs.mctrl_pkg.all;
use pkgs.functions_pkg.all;
entity video is
Port (
RST_n : in std_logic; -- Reset
-- Different operations modes.
CONFIG : in std_logic_vector(CONFIG_WIDTH);
-- Clocks
CLKBUS : in std_logic_vector(CLKBUS_WIDTH); -- Clock signals created by clkgen module.
-- CPU Signals
T80_A : in std_logic_vector(13 downto 0); -- CPU Address Bus
T80_RD_n : in std_logic; -- CPU Read Signal
T80_WR_n : in std_logic; -- CPU Write Signal
T80_MREQ_n : in std_logic; -- CPU Memory Request
T80_BUSACK_n : in std_logic; -- CPU Bus Acknowledge
T80_WAIT_n : out std_logic; -- CPU Wait Request
T80_DI : in std_logic_vector(7 downto 0); -- CPU Data Bus in
T80_DO : out std_logic_vector(7 downto 0); -- CPU Data Bus out
-- Selects.
CS_VRAM_n : in std_logic; -- VRAM Select
CS_MEM_G_n : in std_logic; -- Memory mapped Peripherals Select
CS_GRAM_n : in std_logic; -- Colour GRAM Select
CS_GRAM_80B_n : in std_logic; -- MZ80B GRAM Select
CS_IO_GFB_n : in std_logic; -- Framebuffer register IO Select range.
CS_IO_G_n : in std_logic; -- MZ80B Graphics Options IO Select range.
-- Video Signals
VGATE_n : in std_logic; -- Video Output Control
INVERSE_n : in std_logic; -- Invert video display.
CONFIG_CHAR80 : in std_logic; -- 40 Char = 0, 80 Char = 1 select.
HBLANK : out std_logic; -- Horizontal Blanking
VBLANK : out std_logic; -- Vertical Blanking
HSYNC_n : out std_logic; -- Horizontal Sync
VSYNC_n : out std_logic; -- Vertical Sync
ROUT : out std_logic_vector(7 downto 0); -- Red Output
GOUT : out std_logic_vector(7 downto 0); -- Green Output
BOUT : out std_logic_vector(7 downto 0); -- Green Output
-- HPS Interface
IOCTL_DOWNLOAD : in std_logic; -- HPS Downloading to FPGA.
IOCTL_UPLOAD : in std_logic; -- HPS Uploading from FPGA.
IOCTL_CLK : in std_logic; -- HPS I/O Clock..
IOCTL_WR : in std_logic; -- HPS Write Enable to FPGA.
IOCTL_RD : in std_logic; -- HPS Read Enable from FPGA.
IOCTL_ADDR : in std_logic_vector(24 downto 0); -- HPS Address in FPGA to write into.
IOCTL_DOUT : in std_logic_vector(31 downto 0); -- HPS Data to be written into FPGA.
IOCTL_DIN : out std_logic_vector(31 downto 0) -- HPS Data to be read into HPS.
);
end video;
architecture RTL of video is
type VIDEOLUT is array (integer range 0 to 19, integer range 0 to 28) of integer range 0 to 2000;
-- Constants
--
constant MAX_SUBROW : integer := 8;
constant MENU_FG_RED : integer := 15;
constant MENU_FG_GREEN : integer := 14;
constant MENU_FG_BLUE : integer := 13;
constant MENU_BG_RED : integer := 12;
constant MENU_BG_GREEN : integer := 11;
constant MENU_BG_BLUE : integer := 10;
--
-- Video Timings for different machines and display configuration.
--
constant FB_PARAMS : VIDEOLUT := (
-- Display window variables:-
-- H_DSP_START, H_DSP_END,H_DSP_WND_START, H_DSP_WND_END, H_MNU_START, H_MNU_END, H_HDR_START, H_HDR_END, H_FTR_START, H_FTR_END, V_DSP_START, V_DSP_END,V_DSP_WND_START, V_DSP_WND_END, V_MNU_START, V_MNU_END, V_HDR_START, V_HDR_END, V_FTR_START, V_FTR_END, H_LINE_END, V_LINE_END, MAX_COLUMNS, H_SYNC_START, H_SYNC_END, V_SYNC_START, V_SYNC_END, H_PX, V_PX
( 0, 320, 0, 320, 32, 288, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 511, 259, 40, 320 + 73, 320 + 73 + 45, 200 + 19, 200 + 19 + 4, 0, 0), -- 0 MZ80B/2000 machines have a monochrome 60Hz display with scan of 512 x 260 for a 320x200 viewable area in 40Char mode.
( 0, 640, 0, 640, 192, 448, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 1023, 259, 80, 640 + 146, 640 + 146 + 90, 200 + 19, 200 + 19 + 4, 0, 0), -- 1 MZ80B/2000 machines have a monochrome 60Hz display with scan of 1024 x 260 for a 640x200 viewable area in 80Char mode.
( 0, 320, 0, 320, 32, 288, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 511, 259, 40, 320 + 73, 320 + 73 + 45, 200 + 19, 200 + 19 + 4, 0, 0), -- 2 MZ80K/C/1200/A machines have a monochrome 60Hz display with scan of 512 x 260 for a 320x200 viewable area.
( 0, 640, 0, 640, 192, 448, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 1023, 259, 80, 640 + 146, 640 + 146 + 90, 200 + 19, 200 + 19 + 4, 0, 0), -- 3 MZ80K/C/1200/A machines with an adapted monochrome 60Hz display with scan of 1024 x 260 for a 640x200 viewable area.
( 0, 320, 0, 320, 32, 288, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 567, 311, 40, 320 + 80, 320 + 80 + 40, 200 + 45, 200 + 45 + 3, 0, 0), -- 4 MZ700 has a colour 50Hz display with scan of 568 x 320 for a 320x200 viewable area.
( 0, 640, 0, 640, 192, 448, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 1134, 311, 80, 640 + 160, 640 + 160 + 80, 200 + 45, 200 + 45 + 3, 0, 0), -- 5 MZ700 has colour 50Hz display with scan of 1136 x 320 for a 640x200 viewable area.
( 0, 320, 0, 320, 32, 288, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 511, 259, 40, 320 + 73, 320 + 73 + 45, 200 + 19, 200 + 19 + 4, 0, 0), -- 6 MZ80K/C/1200/A machines with MZ700 style colour @ 60Hz display with scan of 512 x 260 for a 320x200 viewable area.
( 0, 640, 0, 640, 192, 448, 0, 0, 0, 0, 0, 200, 0, 200, 36, 164, 0, 0, 0, 0, 1023, 259, 80, 640 + 146, 640 + 146 + 90, 200 + 19, 200 + 19 + 4, 0, 0), -- 7 MZ80K/C/1200/A machines with MZ700 style colour @ 60Hz display with scan of 1024 x 260 for a 640x200 viewable area.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 519, 40, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 1, 1), -- 8 640x480 @ 60Hz timings for 40Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 799, 524, 40, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 1, 1), -- 9 640x480 @ 60Hz timings for 40Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 508, 80, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 0, 1), -- 10 640x480 @ 60Hz timings for 80Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 799, 524, 80, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 0, 1), -- 11 640x480 @ 60Hz timings for 80Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 508, 40, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 1, 1), -- 12 640x480 @ 60Hz timings for 80Char mode colour.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 799, 524, 40, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 1, 1), -- 13 640x480 @ 60Hz timings for 40Char mode colour.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 508, 80, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 0, 1), -- 14 640x480 @ 60Hz timings for 80Char mode colour.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 799, 524, 80, 640 + 16, 640 + 16 + 96, 480 + 10, 480 + 10 + 2, 0, 1), -- 15 640x480 @ 60Hz timings for 80Char mode colour.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 519, 40, 640 + 24, 640 + 24 + 40, 480 + 9, 480 + 9 + 2, 1, 1), -- 16 640x480 @ 72Hz timings for 40Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 519, 80, 640 + 24, 640 + 24 + 40, 480 + 9, 480 + 9 + 2, 0, 1), -- 17 640x480 @ 72Hz timings for 80Char mode monochrome.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 519, 40, 640 + 24, 640 + 24 + 40, 480 + 9, 480 + 9 + 2, 1, 1), -- 18 640x480 @ 72Hz timings for 40Char mode colour.
( 0, 640, 0, 640, 192, 448, 0, 640, 0, 640, 0, 480, 39, 439, 111, 367, 8, 24, 444, 479, 831, 519, 80, 640 + 24, 640 + 24 + 40, 480 + 9, 480 + 9 + 2, 0, 1) -- 19 640x480 @ 72Hz timings for 80Char mode colour.
);
--
-- Registers
--
signal VIDEOMODE : integer range 0 to 20;
signal VIDEOMODE_LAST : integer range 0 to 20;
signal VIDEOMODE_CHANGED : std_logic;
signal MAX_COLUMN : integer range 0 to 80;
signal FB_ADDR : std_logic_vector(13 downto 0); -- Frame buffer actual address
signal FB_ADDR_STATUS : std_logic_vector(11 downto 0); -- Status display frame buffer actual address
signal FB_ADDR_MENU : std_logic_vector(12 downto 0); -- Menu display frame buffer actual address
signal OFFSET_ADDR : std_logic_vector(7 downto 0); -- Display Offset - for MZ1200/80A machines with 2K VRAM
signal SR_G_DATA : std_logic_vector(7 downto 0); -- Shift Register to serialise Green pixels.
signal SR_R_DATA : std_logic_vector(7 downto 0); -- Shift Register to serialise Red pixels.
signal SR_B_DATA : std_logic_vector(7 downto 0); -- Shift Register to serialise Blue pixels.
signal SR_G_MENU : std_logic_vector(7 downto 0); -- Shift Register to serialise Menu pixels.
signal SR_R_MENU : std_logic_vector(7 downto 0); -- Shift Register to serialise Menu pixels.
signal SR_B_MENU : std_logic_vector(7 downto 0); -- Shift Register to serialise Menu pixels.
signal DISPLAY_DATA : std_logic_vector(23 downto 0);
signal DISPLAY_STATUS : std_logic_vector(23 downto 0);
signal DISPLAY_MENU : std_logic_vector(23 downto 0);
signal XFER_ADDR : std_logic_vector(10 downto 0);
signal XFER_SUB_ADDR : std_logic_vector(2 downto 0);
signal XFER_VRAM_DATA : std_logic_vector(15 downto 0);
signal XFER_GRAM_DATA : std_logic_vector(39 downto 0); -- 3 x 16Kb Colour Graphics + GRAM I (MZ80B) + GRAM II (MZ80B)
signal XFER_MAPPED_DATA : std_logic_vector(23 downto 0);
signal XFER_WEN : std_logic;
signal XFER_VRAM_ADDR : std_logic_vector(10 downto 0);
signal XFER_DST_ADDR : std_logic_vector(13 downto 0);
signal XFER_CGROM_ADDR : std_logic_vector(11 downto 0);
signal CGROM_DATA : std_logic_vector(7 downto 0); -- Font Data To Display
signal DISPLAY_INVERT : std_logic; -- Invert display Mode of MZ80A/1200
signal H_SHIFT_CNT : integer range 0 to 7;
signal H_MNU_SHIFT_CNT : integer range 0 to 7;
signal H_PX : integer range 0 to 3; -- Variable to indicate if horizontal pixels should be multiplied (for conversion to alternate formats).
signal H_PX_CNT : integer range 0 to 3; -- Variable to indicate if horizontal pixels should be multiplied (for conversion to alternate formats).
signal V_PX : integer range 0 to 3; -- Variable to indicate if vertical pixels should be multiplied (for conversion to alternate formats).
signal V_PX_CNT : integer range 0 to 3; -- Variable to indicate if vertical pixels should be multiplied (for conversion to alternate formats).
--
-- CPU/Video Access
--
signal VRAM_VIDEO_DATA : std_logic_vector(7 downto 0); -- Display data output to CPU.
signal VRAM_ADDR : std_logic_vector(11 downto 0); -- VRAM Address.
signal VRAM_DI : std_logic_vector(7 downto 0); -- VRAM Data in.
signal VRAM_DO : std_logic_vector(7 downto 0); -- VRAM Data out.
signal VRAM_WEN : std_logic; -- VRAM Write enable signal.
signal VRAM_CLK : std_logic; -- Clock used to access the VRAM (CPU or IOCTL_CLK).
signal VRAM_CLK_EN : std_logic; -- Clock enable for VRAM.
signal GRAM_VIDEO_DATA : std_logic_vector(7 downto 0); -- Graphics display data output to CPU.
signal GRAM_ADDR : std_logic_vector(13 downto 0); -- Graphics RAM Address.
signal GRAM_DI_R : std_logic_vector(7 downto 0); -- Graphics Red RAM Data.
signal GRAM_DI_G : std_logic_vector(7 downto 0); -- Graphics Green RAM Data.
signal GRAM_DI_B : std_logic_vector(7 downto 0); -- Graphics Blue RAM Data.
signal GRAM_DI_GI : std_logic_vector(7 downto 0); -- Graphics Option GRAM I for MZ80B
signal GRAM_DI_GII : std_logic_vector(7 downto 0); -- Graphics Option GRAM II for MZ80B
signal GRAM_DO_R : std_logic_vector(7 downto 0); -- Graphics Red RAM Data out.
signal GRAM_DO_G : std_logic_vector(7 downto 0); -- Graphics Green RAM Data out.
signal GRAM_DO_B : std_logic_vector(7 downto 0); -- Graphics Blue RAM Data out.
signal GRAM_DO_GI : std_logic_vector(7 downto 0); -- Graphics Option GRAM I Data out for MZ80B.
signal GRAM_DO_GII : std_logic_vector(7 downto 0); -- Graphics Option GRAM II Data out for MZ80B.
signal GRAM_WEN_R : std_logic; -- Graphics Red RAM Write enable signal.
signal GRAM_WEN_G : std_logic; -- Graphics Green RAM Write enable signal.
signal GRAM_WEN_B : std_logic; -- Graphics Blue RAM Write enable signal.
signal GRAM_WEN_GI : std_logic; -- Graphics Option GRAM I Write enable signal for MZ80B.
signal GRAM_WEN_GII : std_logic; -- Graphics Option GRAM II Write enable signal for MZ80B.
signal GRAM_CLK : std_logic; -- Clock used to access the GRAM (CPU or IOCTL_CLK).
signal GRAM_CLK_EN : std_logic; -- Clock enable for GRAM.
signal GRAM_MODE : std_logic_vector(7 downto 0); -- Programmable mode register to control GRAM operations.
signal GRAM_R_FILTER : std_logic_vector(7 downto 0); -- Red pixel writer filter.
signal GRAM_G_FILTER : std_logic_vector(7 downto 0); -- Green pixel writer filter.
signal GRAM_B_FILTER : std_logic_vector(7 downto 0); -- Blue pixel writer filter.
signal GRAM_OPT_WRITE : std_logic; -- Graphics write to GRAMI (0) or GRAMII (1) for MZ80B/MZ2000
signal GRAM_OPT_OUT1 : std_logic; -- Graphics enable GRAMI output to display
signal GRAM_OPT_OUT2 : std_logic; -- Graphics enable GRAMII output to display
signal T80_MA : std_logic_vector(11 downto 0); -- CPU Address Masked according to machine model.
signal CS_INVERT_n : std_logic; -- Chip Select to enable Inverse mode.
signal CS_SCROLL_n : std_logic; -- Chip Select to perform a hardware scroll.
signal CS_GRAM_OPT_n : std_logic; -- Chip Select to write the graphics options for MZ80B/MZ2000.
signal CS_FB_CTL_n : std_logic; -- Chip Select to write to the Graphics mode register.
signal CS_FB_RED_n : std_logic; -- Chip Select to write to the Red pixel per byte indirect write register.
signal CS_FB_GREEN_n : std_logic; -- Chip Select to write to the Green pixel per byte indirect write register.
signal CS_FB_BLUE_n : std_logic; -- Chip Select to write to the Blue pixel per byte indirect write register.
signal CS_PCG_n : std_logic;
signal WAITi_n : std_logic; -- Wait
signal WAITii_n : std_logic; -- Wait(delayed)
signal VWEN : std_logic; -- Write enable to VRAM.
signal GWEN_R : std_logic; -- Write enable to Red GRAM.
signal GWEN_G : std_logic; -- Write enable to Green GRAM.
signal GWEN_B : std_logic; -- Write enable to Blue GRAM.
signal GWEN_GI : std_logic; -- Write enable to for GRAMI option on MZ80B/2000.
signal GWEN_GII : std_logic; -- Write enable to for GRAMII option on MZ80B/2000.
--
-- Internal Signals
--
signal H_COUNT : unsigned(10 downto 0); -- Horizontal pixel counter
signal H_BLANKi : std_logic; -- Horizontal Blanking
signal H_SYNC_ni : std_logic; -- Horizontal Blanking
signal H_DSP_START : integer range 0 to 2047;
signal H_DSP_END : integer range 0 to 2047;
signal H_DSP_WND_START : integer range 0 to 2047; -- Window within the horizontal display when data is output.
signal H_DSP_WND_END : integer range 0 to 2047;
signal H_MNU_START : integer range 0 to 2047;
signal H_MNU_END : integer range 0 to 2047;
signal H_HDR_START : integer range 0 to 2047;
signal H_HDR_END : integer range 0 to 2047;
signal H_FTR_START : integer range 0 to 2047;
signal H_FTR_END : integer range 0 to 2047;
signal H_SYNC_START : integer range 0 to 2047;
signal H_SYNC_END : integer range 0 to 2047;
signal H_LINE_END : integer range 0 to 2047;
signal V_COUNT : unsigned(10 downto 0); -- Vertical pixel counter
signal V_BLANKi : std_logic; -- Vertical Blanking
signal V_SYNC_ni : std_logic; -- Horizontal Blanking
signal V_DSP_START : integer range 0 to 2047;
signal V_DSP_END : integer range 0 to 2047;
signal V_DSP_WND_START : integer range 0 to 2047; -- Window within the vertical display when data is output.
signal V_DSP_WND_END : integer range 0 to 2047;
signal V_MNU_START : integer range 0 to 2047;
signal V_MNU_END : integer range 0 to 2047;
signal V_HDR_START : integer range 0 to 2047;
signal V_HDR_END : integer range 0 to 2047;
signal V_FTR_START : integer range 0 to 2047;
signal V_FTR_END : integer range 0 to 2047;
signal V_SYNC_START : integer range 0 to 2047;
signal V_SYNC_END : integer range 0 to 2047;
signal V_LINE_END : integer range 0 to 2047;
signal VRAM_WAIT : std_logic; -- Horizontal Blanking Memory Access
--
-- CG-ROM
--
signal CGROM_DO : std_logic_vector(7 downto 0);
signal CGROM_BANK : std_logic_vector(3 downto 0);
--
-- PCG
--
signal CGRAM_DO : std_logic_vector(7 downto 0);
signal CG_ADDR : std_logic_vector(11 downto 0);
signal CGRAM_ADDR : std_logic_vector(11 downto 0);
signal PCG_DATA : std_logic_vector(7 downto 0);
signal CGRAM_DI : std_logic_vector(7 downto 0);
signal CGRAM_WE_n : std_logic;
signal CGRAM_WEN : std_logic;
signal CGRAM_SEL : std_logic;
--
-- HPS Control.
--
signal IOCTL_CS_VRAM_n : std_logic; -- Chip Select to allow the HPS to access the VRAM.
signal IOCTL_CS_GRAM_n : std_logic; -- Chip Select to allow the HPS to access the GRAM.
signal IOCTL_CS_GRAM_80B_n : std_logic; -- Chip Select to allow the HPS to access the MZ80B/2000 GRAM option memory.
signal IOCTL_CS_CGROM_n : std_logic;
signal IOCTL_CS_CGRAM_n : std_logic;
signal IOCTL_CS_STRAM_n : std_logic;
signal IOCTL_CS_MNURAM_n : std_logic;
signal IOCTL_CS_CONFIG_n : std_logic;
signal IOCTL_WEN_VRAM : std_logic; -- Write Enable to allow the HPS to write to VRAM.
signal IOCTL_WEN_GRAM_R : std_logic; -- Write Enable to allow the HPS to write to the Red GRAM.
signal IOCTL_WEN_GRAM_G : std_logic; -- Write Enable to allow the HPS to write to the Green GRAM.
signal IOCTL_WEN_GRAM_B : std_logic; -- Write Enable to allow the HPS to write to the Blue GRAM.
signal IOCTL_WEN_GRAM_GI : std_logic; -- Write Enable to allow the HPS to write to the MZ80B GRAM I Option RAM.
signal IOCTL_WEN_GRAM_GII : std_logic; -- Write Enable to allow the HPS to write to the MZ80B GRAM II Option RAM.
signal IOCTL_WEN_STRAM : std_logic; -- Write Enable to allow the HPS to write to the Status Frame Buffer RAM for Green.
signal IOCTL_WEN_MNURAM : std_logic; -- Write Enable to allow the HPS to write to the Menu Frame Buffer RAM for Green.
signal IOCTL_WEN_CGROM : std_logic;
signal IOCTL_WEN_CGRAM : std_logic;
signal IOCTL_DIN_VRAM : std_logic_vector(7 downto 0);
signal IOCTL_DIN_GRAM : std_logic_vector(7 downto 0);
signal IOCTL_DIN_PCG : std_logic_vector(15 downto 0);
signal IOCTL_DIN_CGROM : std_logic_vector(7 downto 0);
signal IOCTL_DIN_CGRAM : std_logic_vector(7 downto 0);
signal IOCTL_DIN_STRAM : std_logic_vector(23 downto 0);
signal IOCTL_DIN_MNURAM : std_logic_vector(23 downto 0);
signal IOCTL_DIN_CONFIG : std_logic_vector(15 downto 0);
--
-- Components
--
component dpram
generic (
init_file : string;
widthad_a : natural;
width_a : natural;
widthad_b : natural;
width_b : natural;
outdata_reg_a : string := "UNREGISTERED";
outdata_reg_b : string := "UNREGISTERED"
);
Port (
clock_a : in std_logic ;
clocken_a : in std_logic := '1';
address_a : in std_logic_vector (widthad_a-1 downto 0);
data_a : in std_logic_vector (width_a-1 downto 0);
wren_a : in std_logic;
q_a : out std_logic_vector (width_a-1 downto 0);
clock_b : in std_logic ;
clocken_b : in std_logic := '1';
address_b : in std_logic_vector (widthad_b-1 downto 0);
data_b : in std_logic_vector (width_b-1 downto 0);
wren_b : in std_logic;
q_b : out std_logic_vector (width_b-1 downto 0)
);
end component;
begin
--
-- Instantiation
--
-- Video memory as seen by the MZ Series. This is a 1K or 2K or 2K + 2K Attribute RAM
-- organised as 4K x 8 on the CPU side and 2K x 16 on the display side, top bits are not used for MZ80K/C/1200/A.
--
VRAM0 : dpram
GENERIC MAP (
init_file => null,
widthad_a => 12,
width_a => 8,
widthad_b => 11,
width_b => 16,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => VRAM_CLK,
clocken_a => VRAM_CLK_EN,
address_a => VRAM_ADDR,
data_a => VRAM_DI,
wren_a => VRAM_WEN,
q_a => VRAM_DO,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_VRAM_ADDR,
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_VRAM_DATA
);
-- Graphics frame buffer memory. This is an enhancement and allows for 320x200 or 640x200 pixel display in 8 colours. It matches
-- the output frame buffer in size, so the contents are blended by a programmable logical operator (ie. OR) with the expanded Video
-- Ram contents to create the output display.
--
GRAMG : dpram -- GREEN
GENERIC MAP (
init_file => null,
widthad_a => 14,
width_a => 8,
widthad_b => 14,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => GRAM_CLK,
clocken_a => GRAM_CLK_EN,
address_a => GRAM_ADDR,
data_a => GRAM_DI_G,
wren_a => GRAM_WEN_G,
q_a => GRAM_DO_G,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR, -- FB Destination address is used as GRAM is on a 1:1 mapping with FB.
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_GRAM_DATA(7 downto 0)
);
--
GRAMR : dpram -- RED
GENERIC MAP (
init_file => null,
widthad_a => 14,
width_a => 8,
widthad_b => 14,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => GRAM_CLK,
clocken_a => GRAM_CLK_EN,
address_a => GRAM_ADDR,
data_a => GRAM_DI_R,
wren_a => GRAM_WEN_R,
q_a => GRAM_DO_R,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR, -- FB Destination address is used as GRAM is on a 1:1 mapping with FB.
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_GRAM_DATA(15 downto 8)
);
--
GRAMB : dpram -- BLUE
GENERIC MAP (
init_file => null,
widthad_a => 14,
width_a => 8,
widthad_b => 14,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => GRAM_CLK,
clocken_a => GRAM_CLK_EN,
address_a => GRAM_ADDR,
data_a => GRAM_DI_B,
wren_a => GRAM_WEN_B,
q_a => GRAM_DO_B,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR, -- FB Destination address is used as GRAM is on a 1:1 mapping with FB.
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_GRAM_DATA(23 downto 16)
);
-- MZ80B Graphics RAM Option I
--
GRAMI : dpram
GENERIC MAP (
init_file => null,
widthad_a => 13,
width_a => 8,
widthad_b => 13,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => GRAM_CLK,
clocken_a => GRAM_CLK_EN,
address_a => GRAM_ADDR(12 downto 0),
data_a => GRAM_DI_GI,
wren_a => GRAM_WEN_GI,
q_a => GRAM_DO_GI,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR(12 downto 0), -- FB Destination address is used as GRAM is on a 1:1 mapping with FB.
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_GRAM_DATA(31 downto 24)
);
-- MZ80B Graphics RAM Option II
--
GRAMII : dpram
GENERIC MAP (
init_file => null,
widthad_a => 13,
width_a => 8,
widthad_b => 13,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for CPU access.
clock_a => GRAM_CLK,
clocken_a => GRAM_CLK_EN,
address_a => GRAM_ADDR(12 downto 0),
data_a => GRAM_DI_GII,
wren_a => GRAM_WEN_GII,
q_a => GRAM_DO_GII,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (SOURCE).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR(12 downto 0), -- FB Destination address is used as GRAM is on a 1:1 mapping with FB.
data_b => (others => '0'),
wren_b => '0',
q_b => XFER_GRAM_DATA(39 downto 32)
);
-- Display Buffer Memory, organised in a Row x Col format, where Address = (Row * MAX_COLUMN * 8) + Col,
-- but in real terms it is a 320x200x3 or 640x200x3 frame buffer.
--
FRAMEBUF0 : dpram
GENERIC MAP (
init_file => null,
widthad_a => 14,
width_a => 24,
widthad_b => 14,
width_b => 24,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_DATA,
-- Port B used for VRAM -> DISPLAY BUFFER transfer (DESTINATION).
clock_b => CLKBUS(CKMASTER),
clocken_b => '1',
address_b => XFER_DST_ADDR,
data_b => XFER_MAPPED_DATA,
wren_b => XFER_WEN
--q_b =>
);
-- A small pixel mapped buffer to display status information in the border area of the frame
-- on VGA scaled output.
--
STATUSBUFG : dpram
GENERIC MAP (
init_file => null,
widthad_a => 12,
width_a => 8,
widthad_b => 12,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_STATUS,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_STATUS(7 downto 0),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(11 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_WEN_STRAM,
q_b => IOCTL_DIN_STRAM(7 downto 0)
);
--
STATUSBUFR : dpram
GENERIC MAP (
init_file => null,
widthad_a => 12,
width_a => 8,
widthad_b => 12,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_STATUS,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_STATUS(15 downto 8),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(11 downto 0),
data_b => IOCTL_DOUT(15 downto 8),
wren_b => IOCTL_WEN_STRAM,
q_b => IOCTL_DIN_STRAM(15 downto 8)
);
--
STATUSBUFB : dpram
GENERIC MAP (
init_file => null,
widthad_a => 12,
width_a => 8,
widthad_b => 12,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_STATUS,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_STATUS(23 downto 16),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(11 downto 0),
data_b => IOCTL_DOUT(23 downto 16),
wren_b => IOCTL_WEN_STRAM,
q_b => IOCTL_DIN_STRAM(23 downto 16)
);
--
MENUBUFG : dpram
GENERIC MAP (
init_file => null,
widthad_a => 13,
width_a => 8,
widthad_b => 13,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_MENU,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_MENU(7 downto 0),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(12 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_WEN_MNURAM,
q_b => IOCTL_DIN_MNURAM(7 downto 0)
);
--
MENUBUFR : dpram
GENERIC MAP (
init_file => null,
widthad_a => 13,
width_a => 8,
widthad_b => 13,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_MENU,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_MENU(15 downto 8),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(12 downto 0),
data_b => IOCTL_DOUT(15 downto 8),
wren_b => IOCTL_WEN_MNURAM,
q_b => IOCTL_DIN_MNURAM(15 downto 8)
);
--
MENUBUFB : dpram
GENERIC MAP (
init_file => null,
widthad_a => 13,
width_a => 8,
widthad_b => 13,
width_b => 8,
outdata_reg_b => "UNREGISTERED"
)
PORT MAP (
-- Port A used for Display output.
clock_a => CLKBUS(CKMASTER),
clocken_a => CLKBUS(CKENVIDEO),
address_a => FB_ADDR_MENU,
data_a => (others => '0'),
wren_a => '0',
q_a => DISPLAY_MENU(23 downto 16),
-- Port B used for IOCTL access.
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(12 downto 0),
data_b => IOCTL_DOUT(23 downto 16),
wren_b => IOCTL_WEN_MNURAM,
q_b => IOCTL_DIN_MNURAM(23 downto 16)
);
-- 0 = MZ80K CGROM = 2Kbytes -> 0000:07ff
-- 1 = MZ80C CGROM = 2Kbytes -> 0800:0fff
-- 2 = MZ1200 CGROM = 2Kbytes -> 1000:17ff
-- 3 = MZ80A CGROM = 2Kbytes -> 1800:1fff
-- 4 = MZ700 CGROM = 4Kbytes -> 2000:2fff
--
CGROM0 : dpram
GENERIC MAP (
init_file => "./software/mif/combined_cgrom.mif",
widthad_a => 15,
width_a => 8,
widthad_b => 15,
width_b => 8
)
PORT MAP (
clock_a => CLKBUS(CKMASTER),
clocken_a => '1',
address_a => CGROM_BANK & CG_ADDR(10 downto 0),
data_a => (others => '0'),
wren_a => '0',
q_a => CGROM_DO,
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(14 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_WEN_CGROM,
q_b => IOCTL_DIN_CGROM
);
CGRAM : dpram
GENERIC MAP (
init_file => null,
widthad_a => 12,
width_a => 8,
widthad_b => 12,
width_b => 8
)
PORT MAP (
clock_a => CLKBUS(CKMASTER),
clocken_a => '1',
address_a => CG_ADDR(11 downto 0),
data_a => CGRAM_DI,
wren_a => CGRAM_WEN,
q_a => CGRAM_DO,
clock_b => IOCTL_CLK,
clocken_b => '1',
address_b => IOCTL_ADDR(11 downto 0),
data_b => IOCTL_DOUT(7 downto 0),
wren_b => IOCTL_WEN_CGRAM,
q_b => IOCTL_DIN_CGRAM
);
-- Clock as maximum system speed to minimise transfer time.
--
process( RST_n, CLKBUS(CKMASTER) )
variable XFER_CYCLE : integer range 0 to 10;
variable XFER_ENABLED : std_logic; -- Enable transfer of VRAM/GRAM to framebuffer.
variable XFER_PAUSE : std_logic; -- Pause transfer of VRAM/GRAM to framebuffer during data display period.
variable XFER_SRC_COL : integer range 0 to 80;
variable XFER_DST_SUBROW : integer range 0 to 7;
begin
if RST_n='0' then
XFER_VRAM_ADDR <= (others => '0');
XFER_DST_ADDR <= (others => '0');
XFER_CGROM_ADDR <= (others => '0');
XFER_ENABLED := '0';
XFER_PAUSE := '0';
XFER_SRC_COL := 0;
XFER_DST_SUBROW := 0;
XFER_CYCLE := 0;
XFER_WEN <= '0';
XFER_MAPPED_DATA <= (others => '0');
-- Copy at end of Display based on the highest clock to minimise time,
--
elsif rising_edge(CLKBUS(CKMASTER)) then
-- Every time we reach the end of the visible display area we enable copying of the VRAM and GRAM into the
-- display framebuffer, ready for the next frame display. This starts to occur a fixed set of rows after
-- they have been displayed, initially only during the hblank period of a row, but the during the full row
-- in the vblank period.
--
if V_COUNT = 0 then
XFER_ENABLED := '1';
end if;
-- During the actual data display, we pause until the start of the hblanking period.
--
if XFER_WEN = '0' and H_BLANKi = '0' and V_BLANKi = '0' then -- XFER_WEN = '0' and (((V_COUNT >= V_DSP_START and V_COUNT < V_DSP_END) and (H_COUNT >= H_DSP_START and H_COUNT < H_DSP_END)) or (H_COUNT >= H_LINE_END-1)) then
XFER_PAUSE := '1';
else
XFER_PAUSE := '0';
end if;
-- If we are in the active transfer window, start transfer.
--
if XFER_ENABLED = '1' and XFER_PAUSE = '0' then
-- Once we reach the end of the framebuffer, disable the copying until next frame.
--
if XFER_DST_ADDR = 16383 then
XFER_ENABLED := '0';
end if;
-- Finite state machine to implement read, map and write.
case (XFER_CYCLE) is
when 0 =>
XFER_MAPPED_DATA <= (others => '0');
XFER_CYCLE := 1;
-- Get the source character and map via the PCG to a slice of the displayed character.
-- Recalculate the destination address based on this loops values.
when 1 =>
-- Setup the PCG address based on the read character.
XFER_CGROM_ADDR <= XFER_VRAM_DATA(15) & XFER_VRAM_DATA(7 downto 0) & std_logic_vector(to_unsigned(XFER_DST_SUBROW, 3));
XFER_CYCLE := 2;
-- Graphics mode:- 7/6 = Operator (00=OR,01=AND,10=NAND,11=XOR),
-- 5 = GRAM Output Enable 0 = active.
-- 4 = VRAM Output Enable, 0 = active.
-- 3/2 = Write mode (00=Page 1:Red, 01=Page 2:Green, 10=Page 3:Blue, 11=Indirect),
-- 1/0 = Read mode (00=Page 1:Red, 01=Page2:Green, 10=Page 3:Blue, 11=Not used).
--
-- Extra cycle for CGROM to latch, use time to decide which mode we are processing.
when 2 =>
-- Check to see if VRAM is disabled, if it is, skip.
--
if CONFIG(VRAMDISABLE) = '0' and GRAM_MODE(4) = '0' and (CONFIG(NORMAL) = '1' or CONFIG(NORMAL80) = '1') then
-- Monochrome modes?
XFER_CYCLE := 4;
elsif CONFIG(VRAMDISABLE) = '0' and GRAM_MODE(4) = '0' and (CONFIG(COLOUR) = '1' or CONFIG(COLOUR80) = '1') then
-- Colour modes?
XFER_CYCLE := 3;
else
-- Disabled or unrecognised mode.
XFER_CYCLE := 5;
end if;
-- Colour modes?
-- Expand and store the slice of the character with colour expansion.
--
when 3 =>
if CGROM_DATA(7) = '0' then
XFER_MAPPED_DATA(7) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(15) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(23) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(7) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(15) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(23) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(6) = '0' then
XFER_MAPPED_DATA(6) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(14) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(22) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(6) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(14) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(22) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(5) = '0' then
XFER_MAPPED_DATA(5) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(13) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(21) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(5) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(13) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(21) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(4) = '0' then
XFER_MAPPED_DATA(4) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(12) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(20) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(4) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(12) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(20) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(3) = '0' then
XFER_MAPPED_DATA(3) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(11) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(19) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(3) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(11) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(19) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(2) = '0' then
XFER_MAPPED_DATA(2) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(10) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(18) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(2) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(10) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(18) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(1) = '0' then
XFER_MAPPED_DATA(1) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(9) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(17) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(1) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(9) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(17) <= XFER_VRAM_DATA(12);
end if;
if CGROM_DATA(0) = '0' then
XFER_MAPPED_DATA(0) <= XFER_VRAM_DATA(10);
XFER_MAPPED_DATA(8) <= XFER_VRAM_DATA(9);
XFER_MAPPED_DATA(16) <= XFER_VRAM_DATA(8);
else
XFER_MAPPED_DATA(0) <= XFER_VRAM_DATA(14);
XFER_MAPPED_DATA(8) <= XFER_VRAM_DATA(13);
XFER_MAPPED_DATA(16) <= XFER_VRAM_DATA(12);
end if;
XFER_CYCLE := 6;
-- Monochrome modes?
-- Expand and store the slice of the character.
--
when 4 =>
if CGROM_DATA(7) = '1' then
XFER_MAPPED_DATA(7) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(15) <= '1';
XFER_MAPPED_DATA(23) <= '1';
end if;
end if;
if CGROM_DATA(6) = '1' then
XFER_MAPPED_DATA(6) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(14) <= '1';
XFER_MAPPED_DATA(22) <= '1';
end if;
end if;
if CGROM_DATA(5) = '1' then
XFER_MAPPED_DATA(5) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(13) <= '1';
XFER_MAPPED_DATA(21) <= '1';
end if;
end if;
if CGROM_DATA(4) = '1' then
XFER_MAPPED_DATA(4) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(12) <= '1';
XFER_MAPPED_DATA(20) <= '1';
end if;
end if;
if CGROM_DATA(3) = '1' then
XFER_MAPPED_DATA(3) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(11) <= '1';
XFER_MAPPED_DATA(19) <= '1';
end if;
end if;
if CGROM_DATA(2) = '1' then
XFER_MAPPED_DATA(2) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(10) <= '1';
XFER_MAPPED_DATA(18) <= '1';
end if;
end if;
if CGROM_DATA(1) = '1' then
XFER_MAPPED_DATA(1) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(9) <= '1';
XFER_MAPPED_DATA(17) <= '1';
end if;
end if;
if CGROM_DATA(0) = '1' then
XFER_MAPPED_DATA(0) <= '1';
if CONFIG(MZ_KC) = '1' then
XFER_MAPPED_DATA(8) <= '1';
XFER_MAPPED_DATA(16) <= '1';
end if;
end if;
XFER_CYCLE := 5;
when 5 =>
-- If invert option selected, invert green.
--
if (CONFIG(MZ_80B) = '1' and INVERSE_n = '0') or (CONFIG(MZ_A) = '1' and DISPLAY_INVERT = '1') then
XFER_MAPPED_DATA(7 downto 0) <= not XFER_MAPPED_DATA(7 downto 0);
end if;
XFER_CYCLE := 6;
when 6 =>
-- Graphics ram enabled?
--
if CONFIG(GRAMDISABLE) = '0' and GRAM_MODE(5) = '0' then
-- Merge in the graphics data using defined mode.
--
case GRAM_MODE(7 downto 6) is
when "00" =>
XFER_MAPPED_DATA <= XFER_MAPPED_DATA or reverse_vector(XFER_GRAM_DATA(23 downto 16)) & reverse_vector(XFER_GRAM_DATA(15 downto 8)) & reverse_vector(XFER_GRAM_DATA(7 downto 0));
when "01" =>
XFER_MAPPED_DATA <= XFER_MAPPED_DATA and reverse_vector(XFER_GRAM_DATA(23 downto 16)) & reverse_vector(XFER_GRAM_DATA(15 downto 8)) & reverse_vector(XFER_GRAM_DATA(7 downto 0));
when "10" =>
XFER_MAPPED_DATA <= XFER_MAPPED_DATA nand reverse_vector(XFER_GRAM_DATA(23 downto 16)) & reverse_vector(XFER_GRAM_DATA(15 downto 8)) & reverse_vector(XFER_GRAM_DATA(7 downto 0));
when "11" =>
XFER_MAPPED_DATA <= XFER_MAPPED_DATA xor reverse_vector(XFER_GRAM_DATA(23 downto 16)) & reverse_vector(XFER_GRAM_DATA(15 downto 8)) & reverse_vector(XFER_GRAM_DATA(7 downto 0));
end case;
end if;
XFER_CYCLE := 7;
when 7 =>
-- For MZ80B, if enabled, blend in the graphics memory.
--
if CONFIG(MZ_80B) = '1' and XFER_DST_ADDR < 8192 then
if GRAM_OPT_OUT1 = '1' and GRAM_OPT_OUT2 = '1' then
XFER_MAPPED_DATA(15 downto 8) <= XFER_MAPPED_DATA(15 downto 8) or reverse_vector(XFER_GRAM_DATA(31 downto 24)) or reverse_vector(XFER_GRAM_DATA(39 downto 32));
elsif GRAM_OPT_OUT1 = '1' then
XFER_MAPPED_DATA(15 downto 8) <= XFER_MAPPED_DATA(15 downto 8) or reverse_vector(XFER_GRAM_DATA(31 downto 24));
elsif GRAM_OPT_OUT2 = '1' then
XFER_MAPPED_DATA(15 downto 8) <= XFER_MAPPED_DATA(15 downto 8) or reverse_vector(XFER_GRAM_DATA(39 downto 32));
end if;
end if;
XFER_CYCLE := 8;
-- Commence write of mapped data.
when 8 =>
XFER_WEN <= '1';
XFER_CYCLE := 9;
-- Complete write and update address.
when 9 =>
-- Write cycle to framebuffer finished.
XFER_WEN <= '0';
XFER_CYCLE := 10;
when 10 =>
-- For each source character, we generate 8 lines in the frame buffer. Thus we need to
-- process the same source row 8 times, each time incrementing the sub-row which is used
-- to extract the next pixel set from the CG. This data is thus written into the destination as:-
-- <Row:0,CGLine:0,0 .. MAX_COLUMN -1> <Row:0,CGLine:1,0.. MAX_COLUMN -1> .. <Row:0,CGLine:7,0.. MAX_COLUMN -1>
-- ..
-- <Row:24,CGLine:0,0 .. MAX_COLUMN -1><Row:24,CGLine:1,0.. MAX_COLUMN -1> .. <Row:24,CGLine:7,0.. MAX_COLUMN -1>
--
-- To achieve this, we keep a note of the column and sub-row, incrementing the source address until end of line
-- then winding it back if we are still rendering the Characters for a given row.
-- Destination address always increments every clock cycle to take the next pixel set.
--
if XFER_SRC_COL < MAX_COLUMN - 1 then
XFER_SRC_COL := XFER_SRC_COL + 1;
XFER_VRAM_ADDR <= XFER_VRAM_ADDR + 1;
else
if XFER_DST_SUBROW < MAX_SUBROW -1 then
XFER_SRC_COL := 0;
XFER_DST_SUBROW := XFER_DST_SUBROW + 1;
XFER_VRAM_ADDR <= XFER_VRAM_ADDR - (MAX_COLUMN - 1);
else
XFER_SRC_COL := 0;
XFER_VRAM_ADDR <= XFER_VRAM_ADDR + 1;
XFER_DST_SUBROW := 0;
end if;
end if;
-- Destination address increments every tick.
--
XFER_DST_ADDR <= XFER_DST_ADDR + 1;
XFER_CYCLE := 0;
end case;
end if;
-- On a new cycle, reset the transfer parameters.
--
if V_COUNT = V_LINE_END and H_COUNT = H_LINE_END - 1 then
-- Start of display, setup the start of VRAM for display according to machine.
if CONFIG(MZ_A) = '1' then
XFER_VRAM_ADDR <= (OFFSET_ADDR & "000");
else
XFER_VRAM_ADDR <= (others => '0');
end if;
XFER_DST_ADDR <= (others => '0');
XFER_CGROM_ADDR <= (others => '0');
XFER_SRC_COL := 0;
XFER_DST_SUBROW := 0;
XFER_CYCLE := 0;
XFER_ENABLED := '0';
XFER_WEN <= '0';
XFER_MAPPED_DATA <= (others => '0');
end if;
end if;
end process;
-- Process to generate the video data signals.
--
process( RST_n, CLKBUS )
begin
-- On reset, set the basic parameters which hold the video signal generator in reset
-- then load up the required parameter set and generate the video signal.
--
if RST_n = '0' then
H_DSP_START <= 0;
H_DSP_END <= 0;
H_DSP_WND_START <= 0;
H_DSP_WND_END <= 0;
H_MNU_START <= 0;
H_MNU_END <= 0;
H_HDR_START <= 0;
H_HDR_END <= 0;
H_FTR_START <= 0;
H_FTR_END <= 0;
V_DSP_START <= 0;
V_DSP_END <= 0;
V_DSP_WND_START <= 0;
V_DSP_WND_END <= 0;
V_MNU_START <= 0;
V_MNU_END <= 0;
V_HDR_START <= 0;
V_HDR_END <= 0;
V_FTR_START <= 0;
V_FTR_END <= 0;
MAX_COLUMN <= 0;
H_LINE_END <= 0;
V_LINE_END <= 0;
H_COUNT <= (others => '0');
V_COUNT <= (others => '0');
H_BLANKi <= '1';
V_BLANKi <= '1';
H_SYNC_ni <= '1';
V_SYNC_ni <= '1';
H_PX_CNT <= 0;
V_PX_CNT <= 0;
H_SHIFT_CNT <= 0;
H_MNU_SHIFT_CNT <= 0;
VIDEOMODE_LAST <= 0;
VIDEOMODE_CHANGED <= '1';
FB_ADDR <= (others => '0');
FB_ADDR_STATUS <= (others => '0');
FB_ADDR_MENU <= (others => '0');
-- elsif rising_edge(CLKBUS(CKVIDEO)) then
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENVIDEO) = '1' then
-- If the video mode changes, reset the variables to the initial state. This occurs
-- at the end of a frame to minimise the monitor syncing incorrectly.
--
VIDEOMODE_LAST <= VIDEOMODE;
if VIDEOMODE_LAST /= VIDEOMODE then
VIDEOMODE_CHANGED <= '1';
end if;
if VIDEOMODE_CHANGED = '1' then
-- Iniitialise control registers.
--
FB_ADDR <= (others => '0');
FB_ADDR_STATUS <= (others => '0');
FB_ADDR_MENU <= (others => '0');
VIDEOMODE_CHANGED <= '0';
-- Load up configuration from the look up table based on video mode.
--
H_DSP_START <= FB_PARAMS(VIDEOMODE, 0);
H_DSP_END <= FB_PARAMS(VIDEOMODE, 1);
H_DSP_WND_START <= FB_PARAMS(VIDEOMODE, 2);
H_DSP_WND_END <= FB_PARAMS(VIDEOMODE, 3);
H_MNU_START <= FB_PARAMS(VIDEOMODE, 4);
H_MNU_END <= FB_PARAMS(VIDEOMODE, 5);
H_HDR_START <= FB_PARAMS(VIDEOMODE, 6);
H_HDR_END <= FB_PARAMS(VIDEOMODE, 7);
H_FTR_START <= FB_PARAMS(VIDEOMODE, 8);
H_FTR_END <= FB_PARAMS(VIDEOMODE, 9);
V_DSP_START <= FB_PARAMS(VIDEOMODE, 10);
V_DSP_END <= FB_PARAMS(VIDEOMODE, 11);
V_DSP_WND_START <= FB_PARAMS(VIDEOMODE, 12);
V_DSP_WND_END <= FB_PARAMS(VIDEOMODE, 13);
V_MNU_START <= FB_PARAMS(VIDEOMODE, 14);
V_MNU_END <= FB_PARAMS(VIDEOMODE, 15);
V_HDR_START <= FB_PARAMS(VIDEOMODE, 16);
V_HDR_END <= FB_PARAMS(VIDEOMODE, 17);
V_FTR_START <= FB_PARAMS(VIDEOMODE, 18);
V_FTR_END <= FB_PARAMS(VIDEOMODE, 19);
H_LINE_END <= FB_PARAMS(VIDEOMODE, 20);
V_LINE_END <= FB_PARAMS(VIDEOMODE, 21);
MAX_COLUMN <= FB_PARAMS(VIDEOMODE, 22);
H_SYNC_START <= FB_PARAMS(VIDEOMODE, 23);
H_SYNC_END <= FB_PARAMS(VIDEOMODE, 24);
V_SYNC_START <= FB_PARAMS(VIDEOMODE, 25);
V_SYNC_END <= FB_PARAMS(VIDEOMODE, 26);
H_PX <= FB_PARAMS(VIDEOMODE, 27);
V_PX <= FB_PARAMS(VIDEOMODE, 28);
--
H_COUNT <= (others => '0');
V_COUNT <= (others => '0');
H_BLANKi <= '1';
V_BLANKi <= '1';
H_SYNC_ni <= '1';
V_SYNC_ni <= '1';
H_PX_CNT <= 0;
V_PX_CNT <= 0;
H_SHIFT_CNT <= 0;
H_MNU_SHIFT_CNT <= 0;
else
-- Activate/deactivate signals according to pixel position.
--
if H_COUNT = H_DSP_START then H_BLANKi <= '0'; end if;
--if H_COUNT = H_LINE_END then H_BLANKi <= '0'; end if;
if H_COUNT = H_DSP_END then H_BLANKi <= '1'; end if;
if H_COUNT = H_SYNC_END then H_SYNC_ni <= '1'; end if;
if H_COUNT = H_SYNC_START then H_SYNC_ni <= '0'; end if;
if V_COUNT = V_DSP_START then V_BLANKi <= '0'; end if;
--if V_COUNT = V_LINE_END then V_BLANKi <= '0'; end if;
if V_COUNT = V_DSP_END then V_BLANKi <= '1'; end if;
if V_COUNT = V_SYNC_START then V_SYNC_ni <= '0'; end if;
if V_COUNT = V_SYNC_END then V_SYNC_ni <= '1'; end if;
-- If we are in the active visible area, stream the required output based on the various buffers.
--
if H_COUNT >= H_DSP_START and H_COUNT < H_DSP_END and V_COUNT >= V_DSP_START and V_COUNT < V_DSP_END then
if (V_COUNT >= V_DSP_WND_START and V_COUNT < V_DSP_WND_END) and (H_COUNT >= H_DSP_WND_START and H_COUNT < H_DSP_WND_END) then
-- Update Horizontal Pixel multiplier.
--
if H_PX_CNT = 0 then
H_PX_CNT <= H_PX;
H_SHIFT_CNT <= H_SHIFT_CNT - 1;
-- Main screen.
--
if H_SHIFT_CNT = 0 then -- and (V_COUNT >= V_DSP_WND_START and V_COUNT < V_DSP_WND_END) and (H_COUNT >= H_DSP_WND_START and H_COUNT < H_DSP_WND_END) then
-- During the visible portion of the frame, data is stored in the frame buffer in bytes, 1 bit per pixel x 8 and 3 colors,
-- thus 1 x 8 x 3 or 24 bit. Read out the values into shift registers to be serialised.
--
SR_G_DATA <= DISPLAY_DATA( 7 downto 0);
SR_R_DATA <= DISPLAY_DATA(15 downto 8);
SR_B_DATA <= DISPLAY_DATA(23 downto 16);
FB_ADDR <= FB_ADDR + 1;
else -- H_SHIFT_CNT /= 0 then --and H_COUNT >= H_DSP_START and H_COUNT < H_DSP_END and V_COUNT >= V_DSP_START and V_COUNT < V_DSP_END then
-- During the active display area, if the shift counter is not 0 and the horizontal multiplier is equal to the setting,
-- shift the data in the shift register to display the next pixel.
--
SR_G_DATA <= SR_G_DATA(6 downto 0) & '0';
SR_R_DATA <= SR_R_DATA(6 downto 0) & '0';
SR_B_DATA <= SR_B_DATA(6 downto 0) & '0';
end if;
else
H_PX_CNT <= H_PX_CNT - 1;
end if;
else
-- Blank.
--
SR_G_DATA <= (others => '0');
SR_R_DATA <= (others => '0');
SR_B_DATA <= (others => '0');
H_PX_CNT <= H_PX;
H_SHIFT_CNT <= 1;
end if;
-- If the Status areas or the menu is enabled, create a data stream for the status/menu data to be merged with the main data.
--
if CONFIG(MENUENABLE) = '1' or CONFIG(STATUSENABLE) = '1' then
H_MNU_SHIFT_CNT <= H_MNU_SHIFT_CNT - 1;
-- On each reset of the shift counter, load up Menu, Header, Footer or blank data to be serialised.
--
if H_MNU_SHIFT_CNT = 0 then
-- OSD Menu
--
if CONFIG(MENUENABLE) = '1'
and
((V_COUNT >= V_MNU_START and V_COUNT < V_MNU_END) and (H_COUNT >= H_MNU_START and H_COUNT < H_MNU_END)) then
-- Merge the OSD with the underlying screen data.
--
--for i in 0 to 7 loop
-- if DISPLAY_MENU(i) = '1' and DISPLAY_MENU(MENU_FG_GREEN) = '1' then
-- SR_G_MENU(i) <= '1';
-- elsif DISPLAY_MENU(i) = '0' and DISPLAY_MENU(MENU_BG_GREEN) = '1' then
-- SR_G_MENU(i) <= '1';
-- else
-- SR_G_MENU(i) <= '0';
-- end if;
--end loop;
--for i in 0 to 7 loop
-- if DISPLAY_MENU(i) = '1' and DISPLAY_MENU(MENU_FG_RED) = '1' then
-- SR_R_MENU(i) <= '1';
-- elsif DISPLAY_MENU(i) = '0' and DISPLAY_MENU(MENU_BG_RED) = '1' then
-- SR_R_MENU(i) <= '1';
-- else
-- SR_R_MENU(i) <= '0';
-- end if;
--end loop;
--for i in 0 to 7 loop
-- if DISPLAY_MENU(i) = '1' and DISPLAY_MENU(MENU_FG_BLUE) = '1' then
-- SR_B_MENU(i) <= '1';
-- elsif DISPLAY_MENU(i) = '0' and DISPLAY_MENU(MENU_BG_BLUE) = '1' then
-- SR_B_MENU(i) <= '1';
-- else
-- SR_B_MENU(i) <= '0';
-- end if;
--end loop;
SR_G_MENU <= DISPLAY_MENU( 7 downto 0);
SR_R_MENU <= DISPLAY_MENU(15 downto 8);
SR_B_MENU <= DISPLAY_MENU(23 downto 16);
FB_ADDR_MENU <= FB_ADDR_MENU + 1;
-- Header/Footer
--
elsif CONFIG(STATUSENABLE) = '1'
and
(((H_HDR_START /= H_HDR_END) and ((V_COUNT >= V_HDR_START and V_COUNT < V_HDR_END) and (H_COUNT >= H_HDR_START and H_COUNT < H_HDR_END)))
or
((H_FTR_START /= H_FTR_END) and ((V_COUNT >= V_FTR_START and V_COUNT < V_FTR_END) and (H_COUNT >= H_FTR_START and H_COUNT < H_FTR_END)))) then
-- During the visible portion of the unused header/footer, read out the status frame buffer, 1 bit per pixel x 8 and 3 colours.
--
SR_G_MENU <= DISPLAY_STATUS( 7 downto 0);
SR_R_MENU <= DISPLAY_STATUS(15 downto 8);
SR_B_MENU <= DISPLAY_STATUS(23 downto 16);
FB_ADDR_STATUS <= FB_ADDR_STATUS + 1;
-- Blank.
--
else
SR_G_MENU <= (others => '0');
SR_R_MENU <= (others => '0');
SR_B_MENU <= (others => '0');
H_MNU_SHIFT_CNT <= 0;
end if;
-- Shift on each clock cycle to next active bit if not at start.
--
else
SR_G_MENU <= SR_G_MENU(6 downto 0) & '0';
SR_R_MENU <= SR_R_MENU(6 downto 0) & '0';
SR_B_MENU <= SR_B_MENU(6 downto 0) & '0';
end if;
end if;
else
H_PX_CNT <= 0;
H_SHIFT_CNT <= 0;
H_MNU_SHIFT_CNT <= 0;
end if;
-- Horizontal/Vertical counters are updated each clock cycle to accurately track pixel/timing.
--
if H_COUNT = H_LINE_END then
H_COUNT <= (others => '0');
H_PX_CNT <= 0;
-- Update Vertical Pixel multiplier.
--
if V_PX_CNT = 0 then
V_PX_CNT <= V_PX;
else
V_PX_CNT <= V_PX_CNT - 1;
end if;
-- When we need to repeat a line due to pixel multiplying, wind back the framebuffer address to start of line.
--
if V_COUNT >= V_DSP_WND_START and V_COUNT < V_DSP_WND_END and V_PX /= 0 and V_PX_CNT > 0 then
FB_ADDR <= FB_ADDR - MAX_COLUMN;
end if;
-- For VGA, expand the vertical pixels according to setting.
--
if CONFIG(MENUENABLE) = '1' and (V_COUNT >= V_MNU_START and V_COUNT < V_MNU_END) and V_PX /= 0 and V_PX_CNT > 0 then
FB_ADDR_MENU <= FB_ADDR_MENU - 32;
end if;
-- Once we have reached the end of the active vertical display, reset the framebuffer address.
--
if V_COUNT = V_DSP_END then
FB_ADDR <= (others => '0');
FB_ADDR_STATUS <= (others => '0');
FB_ADDR_MENU <= (others => '0');
end if;
-- End of vertical line, increment to next or reset to beginning.
--
if V_COUNT = V_LINE_END then
V_COUNT <= (others => '0');
V_PX_CNT <= 0;
else
V_COUNT <= V_COUNT + 1;
end if;
else
H_COUNT <= H_COUNT + 1;
end if;
end if;
end if;
end if;
end process;
-- Control Registers
--
-- MZ1200/80A: INVERT display, accessed at E014
-- SCROLL display, accessed at E200 - E2FF, the address determines the offset.
-- F0-F3 clocks the i8253 gate for MZ80B. (not used in this module)
-- F4-F7 set ths MZ80B/MZ2000 graphics options. Bit 0 = 0, Write to Graphics RAM I, Bit 0 = 1, Write to Graphics RAM II.
-- Bit 1 = 1, blend Graphics RAM I output on display, Bit 2 = 1, blend Graphics RAM II output on display.
--
-- IO Range for Graphics enhancements is set by the MCTRL DISPLAY2{7:3] register.
-- x[0|8],<val> sets the graphics mode. 7/6 = Operator (00=OR,01=AND,10=NAND,11=XOR), 5=GRAM Output Enable, 4 = VRAM Output Enable, 3/2 = Write mode (00=Page 1:Red, 01=Page 2:Green, 10=Page 3:Blue, 11=Indirect), 1/0=Read mode (00=Page 1:Red, 01=Page2:Green, 10=Page 3:Blue, 11=Not used).
-- x[1|9],<val> sets the Red bit mask (1 bit = 1 pixel, 8 pixels per byte).
-- x[2|A],<val> sets the Green bit mask (1 bit = 1 pixel, 8 pixels per byte).
-- x[3|B],<val> sets the Blue bit mask (1 bit = 1 pixel, 8 pixels per byte).
-- x[4|C] switches in 1 16Kb page (3 pages) of graphics ram to C000 - FFFF. This overrides all MZ700 page switching functions.
-- x[5|D] switches out the graphics ram and returns to previous state.
--
process( RST_n, CLKBUS(CKMASTER) )
begin
if RST_n='0' then
DISPLAY_INVERT <= '0';
OFFSET_ADDR <= (others => '0');
GRAM_MODE <= "00001100";
GRAM_R_FILTER <= (others => '1');
GRAM_G_FILTER <= (others => '1');
GRAM_B_FILTER <= (others => '1');
GRAM_OPT_WRITE <= '0';
GRAM_OPT_OUT1 <= '0';
GRAM_OPT_OUT2 <= '0';
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
if CS_INVERT_n='0' and T80_RD_n='0' then
DISPLAY_INVERT <= T80_MA(0);
end if;
if CS_SCROLL_n='0' and T80_RD_n='0' then
if CONFIG(NORMAL80) = '1' or CONFIG(COLOUR80) = '1' then
OFFSET_ADDR <= (others => '0');
else
OFFSET_ADDR <= T80_A(7 downto 0);
end if;
end if;
if CS_FB_CTL_n = '0' and T80_WR_n = '0' then
GRAM_MODE <= T80_DI;
end if;
if CS_FB_RED_n = '0' and T80_WR_n = '0' then
GRAM_R_FILTER <= T80_DI;
end if;
if CS_FB_GREEN_n = '0' and T80_WR_n = '0' then
GRAM_G_FILTER <= T80_DI;
end if;
if CS_FB_BLUE_n = '0' and T80_WR_n = '0' then
GRAM_B_FILTER <= T80_DI;
end if;
if CS_GRAM_OPT_n = '0' and T80_WR_n = '0' then
GRAM_OPT_WRITE <= T80_DI(0);
GRAM_OPT_OUT1 <= T80_DI(1);
GRAM_OPT_OUT2 <= T80_DI(2);
end if;
end if;
end if;
end process;
-- Enable Video Wait States - Original design has wait states inserted into the cycle if the CPU accesses the VRAM during display. In the updated design, the VRAM
-- is copied into a framebuffer during the Vertical Blanking period so no wait states are needed. To keep consistency with the original design (for programs which depend on it),
-- the wait states can be enabled by configuration.
--
process( T80_MREQ_n ) begin
if falling_edge(T80_MREQ_n) then
VRAM_WAIT <= H_BLANKi;
end if;
end process;
--
-- Extend wait by 1 cycle
process( CLKBUS(CKMASTER) ) begin
if rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
WAITii_n <= WAITi_n;
end if;
end if;
end process;
--
-- PCG Access Registers
--
-- E010: PCG_DATA (byte to describe 8-pixel row of a character)
-- E011: PCG_ADDR (offset in the PCG in 8-pixel row unit) -> up to 256/8 = 32 characters
-- E012: PCG_CTRL
-- bit 0-1: character selector -> (PCG_ADDR + 256*(PCG_CTRL&3)) -> address in the range of the upper 128 characters font
-- bit 2 : font selector -> PCG_CTRL&2 == 0 -> 1st font else 2nd font
-- bit 3 : select which font for display
-- bit 4 : use programmable font for display
-- bit 5 : set programmable upper font -> PCG_CTRL&20 == 0 -> fixed upper 128 characters else programmable upper 128 characters
-- So if you want to change a character pattern (only doable in the upper 128 characters of a font), you need to:
-- - set bit 5 to 1 : PCG_CTRL[5] = 1
-- - set the font to select : PCG_CTRL[2] = font_number
-- - set the first row address of the character: PCG_ADDR[0..7] = row[0..7] and PCG_CTRL[0..1] = row[8..9]
-- - set the 8 pixels of the row in PCG_DATA
--
process( RST_n, CLKBUS(CKMASTER) ) begin
if RST_n = '0' then
CGRAM_ADDR <= (others=>'0');
PCG_DATA <= (others=>'0');
CGRAM_WE_n <= '1';
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
if CS_PCG_n = '0' and T80_WR_n = '0' then
-- Set the PCG Data to program to RAM.
if T80_A(1 downto 0) = "00" then
PCG_DATA <= T80_DI;
end if;
-- Set the PCG Address in RAM.
if T80_A(1 downto 0) = "01" then
CGRAM_ADDR(7 downto 0) <= T80_DI;
end if;
-- Set the PCG Control register.
if T80_A(1 downto 0) = "10" then
CGRAM_ADDR(11 downto 8) <= (T80_DI(2) and CONFIG(MZ_A)) & '1' & T80_DI(1 downto 0);
CGRAM_WE_n <= not T80_DI(4);
CGRAM_SEL <= T80_DI(5);
end if;
end if;
end if;
end if;
end process;
-- Process to allow the HPS or uC to read the configuration array.
--
process( IOCTL_CLK ) begin
if rising_edge(IOCTL_CLK) then
if IOCTL_ADDR(10) = '1' then
IOCTL_DIN_CONFIG <= "00000000000" & std_logic_vector(to_unsigned(VIDEOMODE, 5));
else
IOCTL_DIN_CONFIG <= std_logic_vector(to_unsigned(FB_PARAMS(to_integer(unsigned(IOCTL_ADDR(4 downto 0))), to_integer(unsigned(IOCTL_ADDR(9 downto 5)))), IOCTL_DIN_CONFIG'length));
end if;
end if;
end process;
--
-- CPU / RAM signals and selects.
--
WAITi_n <= '0' when CS_VRAM_n = '0' and VRAM_WAIT = '0' and H_BLANKi = '0' and (CONFIG(MZ_A) = '1' or CONFIG(MZ700) = '1')
else '1';
T80_WAIT_n <= WAITi_n and WAITii_n when CONFIG(VRAMWAIT) = '1'
else '1';
T80_MA <= "00" & T80_A(9 downto 0) when CONFIG(MZ_KC) = '1'
else
T80_A(11 downto 0);
-- Program Character Generator RAM. E010 - Write cycle (Read cycle = reset memory swap).
CS_PCG_n <= '0' when CS_MEM_G_n = '0' and T80_A(10 downto 4) = "0000001"
else '1'; -- D010 -> D01f
-- Invert display register. E014/E015
CS_INVERT_n <= '0' when CS_MEM_G_n = '0' and CONFIG(MZ_A) = '1' and T80_MA(11 downto 9) = "000" and T80_MA(4 downto 2) = "101"
else '1';
-- Scroll display register. E200 - E2FF
CS_SCROLL_n <= '0' when CS_MEM_G_n = '0' and T80_A(11 downto 8)="0010" and CONFIG(MZ_A)='1'
else '1';
-- MZ80B/MZ2000 Graphics Options Register select. F4-F7
CS_GRAM_OPT_n <= '0' when CS_IO_G_n = '0' and T80_A(1 downto 0) = "00"
else '1';
-- <block>0,<val> sets the graphics mode. 7/6 = Operator (00=OR,01=AND,10=NAND,11=XOR),
-- 5 = GRAM Output Enable, 4 = VRAM Output Enable,
-- 3/2 = Write mode (00=Page 1:Red, 01=Page 2:Green, 10=Page 3:Blue, 11=Indirect),
-- 1/0 = Read mode (00=Page 1:Red, 01=Page2:Green, 10=Page 3:Blue, 11=Not used).
CS_FB_CTL_n <= '0' when CS_IO_GFB_n = '0' and T80_A(2 downto 0) = "000"
else '1';
-- 01,<val> sets the Red bit mask (1 bit = 1 pixel, 8 pixels per byte).
CS_FB_RED_n <= '0' when CS_IO_GFB_n = '0' and T80_A(2 downto 0) = "001"
else '1';
-- 02,<val> sets the Green bit mask (1 bit = 1 pixel, 8 pixels per byte).
CS_FB_GREEN_n <= '0' when CS_IO_GFB_n = '0' and T80_A(2 downto 0) = "010"
else '1';
-- 03,<val> sets the Blue bit mask (1 bit = 1 pixel, 8 pixels per byte).
CS_FB_BLUE_n <= '0' when CS_IO_GFB_n = '0' and T80_A(2 downto 0) = "011"
else '1';
T80_DO <= VRAM_VIDEO_DATA when T80_RD_n = '0' and CS_VRAM_n = '0'
else
GRAM_VIDEO_DATA when T80_RD_n = '0' and CS_GRAM_n = '0'
else
GRAM_DO_GI when T80_RD_n = '0' and CS_GRAM_80B_n = '0' and GRAM_OPT_WRITE = '0'
else
GRAM_DO_GII when T80_RD_n = '0' and CS_GRAM_80B_n = '0' and GRAM_OPT_WRITE = '1'
else
(others=>'0');
VRAM_ADDR <= T80_MA(10 downto 0) & T80_MA(11) when IOCTL_CS_VRAM_n = '1'
else
IOCTL_ADDR(10 downto 0) & IOCTL_ADDR(11);
VRAM_DI <= T80_DI when IOCTL_CS_VRAM_n = '1'
else
IOCTL_DOUT(7 downto 0);
VWEN <= '1' when T80_WR_n='0' and CS_VRAM_n = '0'
else '0';
VRAM_WEN <= VWEN when IOCTL_CS_VRAM_n = '1'
else
IOCTL_WEN_VRAM;
VRAM_VIDEO_DATA <= VRAM_DO when IOCTL_CS_VRAM_n = '1'
else
(others=>'0');
IOCTL_DIN_VRAM <= VRAM_DO when IOCTL_CS_VRAM_n = '0'
else
(others=>'0');
VRAM_CLK <= CLKBUS(CKMASTER) when IOCTL_CS_VRAM_n = '1'
else
IOCTL_CLK;
VRAM_CLK_EN <= CLKBUS(CKENCPU) when IOCTL_CS_VRAM_n = '1'
else
'1';
-- CGROM Data to CG RAM, either ROM -> RAM copy or Z80 provides map.
--
CGRAM_DI <= CGROM_DO when CGRAM_SEL = '1' -- Data from ROM
else
PCG_DATA when CGRAM_SEL = '0' -- Data from PCG
else (others=>'0');
CGRAM_WEN <= not (CGRAM_WE_n or CS_PCG_n) and not T80_WR_n;
--
-- Font select
--
CGROM_DATA <= CGROM_DO when CONFIG(PCGRAM)='0'
else
PCG_DATA when CS_PCG_n='0' and T80_A(1 downto 0)="10" and T80_WR_n='0'
else
CGRAM_DO when CONFIG(PCGRAM)='1'
else (others => '1');
CG_ADDR <= CGRAM_ADDR(11 downto 0) when CGRAM_WE_n = '0'
else XFER_CGROM_ADDR;
CGROM_BANK <= "0000" when CONFIG(MZ80K) = '1'
else
"0001" when CONFIG(MZ80C) = '1'
else
"0010" when CONFIG(MZ1200) = '1'
else
"0011" when CONFIG(MZ80A) = '1'
else
"0100" when CONFIG(MZ700) = '1' and XFER_CGROM_ADDR(11) = '0'
else
"0101" when CONFIG(MZ700) = '1' and XFER_CGROM_ADDR(11) = '1'
else
"0110" when CONFIG(MZ800) = '1' and XFER_CGROM_ADDR(11) = '0'
else
"0111" when CONFIG(MZ800) = '1' and XFER_CGROM_ADDR(11) = '1'
else
"1000" when CONFIG(MZ80B) = '1'
else
"1001" when CONFIG(MZ2000) = '1'
else
"1111";
-- As the Graphics RAM is an odd size, 16384 x 3 colour planes, it has to be in 3 seperate 16K blocks to avoid wasting memory (or having it synthesized away),
-- thus there are 3 sets of signals, 1 per colour.
--
GRAM_ADDR <= T80_A(13 downto 0) when IOCTL_CS_GRAM_n = '1'
else
IOCTL_ADDR(13 downto 0);
-- direct writes when accessing individual pages.
GRAM_DI_R <= T80_DI when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "00"
else
T80_DI and GRAM_R_FILTER when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "11"
else
IOCTL_DOUT(7 downto 0) when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "00"
else
(others=>'0');
-- direct writes when accessing individual pages.
GRAM_DI_G <= T80_DI when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "01"
else
T80_DI and GRAM_G_FILTER when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "11"
else
IOCTL_DOUT(7 downto 0) when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "01"
else
(others=>'0');
-- direct writes when accessing individual pages.
GRAM_DI_B <= T80_DI when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "10"
else
T80_DI and GRAM_B_FILTER when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(3 downto 2) = "11"
else
IOCTL_DOUT(7 downto 0) when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "10"
else
(others=>'0');
GWEN_R <= '1' when T80_WR_n = '0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "00"
else
'1' when T80_WR_n = '0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "11"
else
'0';
GRAM_WEN_R <= GWEN_R when IOCTL_CS_GRAM_n = '1'
else
IOCTL_WEN_GRAM_R;
GWEN_G <= '1' when T80_WR_n='0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "01"
else
'1' when T80_WR_n='0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "11"
else
'0';
GRAM_WEN_G <= GWEN_G when IOCTL_CS_GRAM_n = '1'
else
IOCTL_WEN_GRAM_G;
GWEN_B <= '1' when T80_WR_n='0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "10"
else
'1' when T80_WR_n='0' and CS_GRAM_n = '0' and GRAM_MODE(3 downto 2) = "11"
else
'0';
GRAM_WEN_B <= GWEN_B when IOCTL_CS_GRAM_n = '1'
else
IOCTL_WEN_GRAM_B;
GRAM_VIDEO_DATA <= GRAM_DO_R when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(1 downto 0) = "00"
else
GRAM_DO_G when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(1 downto 0) = "01"
else
GRAM_DO_B when IOCTL_CS_GRAM_n = '1' and GRAM_MODE(1 downto 0) = "10"
else
(others=>'0');
GRAM_CLK <= CLKBUS(CKMASTER) when IOCTL_CS_GRAM_n = '1'
else
IOCTL_CLK;
GRAM_CLK_EN <= CLKBUS(CKENCPU) when IOCTL_CS_GRAM_n = '1'
else
'1';
-- MZ80B/MZ2000 Graphics Option RAM.
--
GRAM_DI_GI <= T80_DI when IOCTL_CS_GRAM_80B_n = '1'
else
IOCTL_DOUT(7 downto 0) when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_ADDR(13) = '0'
else
(others=>'0');
GRAM_DI_GII <= T80_DI when IOCTL_CS_GRAM_80B_n = '1'
else
IOCTL_DOUT(7 downto 0) when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_ADDR(13) = '1'
else
(others=>'0');
GWEN_GI <= '1' when T80_WR_n = '0' and CS_GRAM_80B_n = '0' and GRAM_OPT_WRITE = '0'
else
'0';
GRAM_WEN_GI <= GWEN_GI when IOCTL_CS_GRAM_80B_n = '1'
else
IOCTL_WEN_GRAM_GI;
GWEN_GII <= '1' when T80_WR_n='0' and CS_GRAM_80B_n = '0' and GRAM_OPT_WRITE = '1'
else
'0';
GRAM_WEN_GII <= GWEN_GII when IOCTL_CS_GRAM_80B_n = '1'
else
IOCTL_WEN_GRAM_GII;
--
-- HPS Access - match whole address, additional LE but easier to read.
--
IOCTL_WEN_VRAM <= '1' when IOCTL_CS_VRAM_n = '0' and IOCTL_WR = '1'
else '0';
IOCTL_WEN_GRAM_R <= '1' when IOCTL_CS_GRAM_n = '0' and IOCTL_WR = '1' and IOCTL_ADDR(15 downto 14) = "00"
else '0';
IOCTL_WEN_GRAM_G <= '1' when IOCTL_CS_GRAM_n = '0' and IOCTL_WR = '1' and IOCTL_ADDR(15 downto 14) = "01"
else '0';
IOCTL_WEN_GRAM_B <= '1' when IOCTL_CS_GRAM_n = '0' and IOCTL_WR = '1' and IOCTL_ADDR(15 downto 14) = "10"
else '0';
IOCTL_WEN_GRAM_GI <= '1' when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_WR = '1' and IOCTL_ADDR(13) = '0'
else '0';
IOCTL_WEN_GRAM_GII <= '1' when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_WR = '1' and IOCTL_ADDR(13) = '1'
else '0';
IOCTL_WEN_CGROM <= '1' when IOCTL_CS_CGROM_n = '0' and IOCTL_WR = '1'
else '0';
IOCTL_WEN_CGRAM <= '1' when IOCTL_CS_CGRAM_n = '0' and IOCTL_WR = '1'
else '0';
IOCTL_WEN_STRAM <= '1' when IOCTL_CS_STRAM_n = '0' and IOCTL_WR = '1'
else '0';
IOCTL_WEN_MNURAM <= '1' when IOCTL_CS_MNURAM_n = '0' and IOCTL_WR = '1'
else '0';
IOCTL_CS_VRAM_n <= '0' when IOCTL_ADDR(24 downto 12) = "0001100000000"
else '1';
IOCTL_CS_GRAM_n <= '0' when IOCTL_ADDR(24 downto 16) = "000110001" and IOCTL_ADDR(15 downto 14) /= "11"
else '1';
IOCTL_CS_GRAM_80B_n <= '0' when IOCTL_ADDR(24 downto 14) = "00011000111"
else '1';
IOCTL_CS_CGROM_n <= '0' when IOCTL_ADDR(24 downto 17) = "00101000"
else '1';
IOCTL_CS_CGRAM_n <= '0' when IOCTL_ADDR(24 downto 17) = "00110000"
else '1';
IOCTL_CS_STRAM_n <= '0' when IOCTL_ADDR(24 downto 16) = "000110010" and IOCTL_ADDR(15 downto 12) = "0000"
else '1';
IOCTL_CS_MNURAM_n <= '0' when IOCTL_ADDR(24 downto 16) = "000110010" and IOCTL_ADDR(15 downto 12) = "0010"
else '1';
IOCTL_CS_CONFIG_n <= '0' when IOCTL_ADDR(24 downto 16) = "000110010" and IOCTL_ADDR(15 downto 12) = "0100"
else '1';
IOCTL_DIN <= X"000000" & IOCTL_DIN_VRAM when IOCTL_CS_VRAM_n = '0' and IOCTL_RD = '1'
else
X"000000" & IOCTL_DIN_GRAM when IOCTL_CS_GRAM_n = '0' and IOCTL_RD = '1'
else
X"FF0000" & IOCTL_DIN_CGROM when IOCTL_CS_CGROM_n = '0' and IOCTL_RD = '1'
else
X"000000" & IOCTL_DIN_CGRAM when IOCTL_CS_CGRAM_n = '0' and IOCTL_RD = '1'
else
X"00" & IOCTL_DIN_STRAM(23 downto 0) when IOCTL_CS_STRAM_n = '0' and IOCTL_RD = '1'
else
X"0000" & IOCTL_DIN_MNURAM(15 downto 0) when IOCTL_CS_MNURAM_n = '0' and IOCTL_RD = '1'
else
X"0000" & IOCTL_DIN_CONFIG when IOCTL_CS_CONFIG_n = '0' and IOCTL_RD = '1'
else
(others=>'0');
IOCTL_DIN_GRAM <= GRAM_DO_R when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "00"
else
GRAM_DO_G when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "01"
else
GRAM_DO_B when IOCTL_CS_GRAM_n = '0' and IOCTL_ADDR(15 downto 14) = "10"
else
GRAM_DO_GI when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_ADDR(13) = '0'
else
GRAM_DO_GII when IOCTL_CS_GRAM_80B_n = '0' and IOCTL_ADDR(13) = '1'
else
(others=>'0');
-- Work out the current video mode, which is used to look up the parameters for frame generation.
--
VIDEOMODE <= 0 when CONFIG(VGAMODE) = "11" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '0'
else
1 when CONFIG(VGAMODE) = "11" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '1'
else
2 when CONFIG(VGAMODE) = "11" and CONFIG(NORMAL) = '1'
else
3 when CONFIG(VGAMODE) = "11" and CONFIG(NORMAL80) = '1'
else
4 when CONFIG(VGAMODE) = "11" and CONFIG(COLOUR) = '1' and CONFIG(MZ700) = '1'
else
5 when CONFIG(VGAMODE) = "11" and CONFIG(COLOUR80) = '1' and CONFIG(MZ700) = '1'
else
6 when CONFIG(VGAMODE) = "11" and CONFIG(COLOUR) = '1'
else
7 when CONFIG(VGAMODE) = "11" and CONFIG(COLOUR80) = '1'
else
8 when CONFIG(VGAMODE) = "00" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '0'
else
10 when CONFIG(VGAMODE) = "00" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '1'
else
9 when CONFIG(VGAMODE) = "01" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '0'
else
11 when CONFIG(VGAMODE) = "01" and CONFIG(MZ_80B) = '1' and CONFIG_CHAR80 = '1'
else
8 when CONFIG(VGAMODE) = "00" and CONFIG(NORMAL) = '1'
else
9 when CONFIG(VGAMODE) = "01" and CONFIG(NORMAL) = '1'
else
10 when CONFIG(VGAMODE) = "00" and CONFIG(NORMAL80) = '1'
else
11 when CONFIG(VGAMODE) = "01" and CONFIG(NORMAL80) = '1'
else
12 when CONFIG(VGAMODE) = "00" and CONFIG(COLOUR) = '1' and CONFIG(MZ700) = '1'
else
13 when CONFIG(VGAMODE) = "01" and CONFIG(COLOUR) = '1' and CONFIG(MZ700) = '1'
else
18 when CONFIG(VGAMODE) = "10" and CONFIG(COLOUR) = '1' and CONFIG(MZ700) = '1'
else
14 when CONFIG(VGAMODE) = "00" and CONFIG(COLOUR80) = '1' and CONFIG(MZ700) = '1'
else
15 when CONFIG(VGAMODE) = "01" and CONFIG(COLOUR80) = '1' and CONFIG(MZ700) = '1'
else
19 when CONFIG(VGAMODE) = "10" and CONFIG(COLOUR80) = '1' and CONFIG(MZ700) = '1'
else
12 when CONFIG(VGAMODE) = "00" and CONFIG(COLOUR) = '1'
else
13 when CONFIG(VGAMODE) = "01" and CONFIG(COLOUR) = '1'
else
18 when CONFIG(VGAMODE) = "10" and CONFIG(COLOUR) = '1'
else
14 when CONFIG(VGAMODE) = "00" and CONFIG(COLOUR80) = '1'
else
15 when CONFIG(VGAMODE) = "01" and CONFIG(COLOUR80) = '1'
else
19 when CONFIG(VGAMODE) = "10" and CONFIG(COLOUR80) = '1'
else
16 when CONFIG(VGAMODE) = "10" and CONFIG(NORMAL) = '1'
else
17 when CONFIG(VGAMODE) = "10" and CONFIG(NORMAL80) = '1'
else
2;
--
-- Video Output Signals
--
VBLANK <= V_BLANKi;
HBLANK <= H_BLANKi;
VSYNC_n <= V_SYNC_ni;
HSYNC_n <= H_SYNC_ni;
ROUT <= (others => SR_R_DATA(7) or SR_R_MENU(7)) when H_BLANKi='0' or VGATE_n='1'
else
(others => '0');
GOUT <= (others => SR_G_DATA(7) or SR_G_MENU(7)) when H_BLANKi='0' or VGATE_n='1'
else
(others => '0');
BOUT <= (others => SR_B_DATA(7) or SR_B_MENU(7)) when H_BLANKi='0' or VGATE_n='1'
else
(others => '0');
end RTL;