--------------------------------------------------------------------------------------------------------- -- -- 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) -- -- 64uS 15.625KHz -- Signal Start End Period Comment -- HDISPEN 0 320 40uS -- HBLANK 318 510 24uS -- BLNK 318 486 21uS -- HSYNC 393 438 5.625uS -- -- 16.64mS 60.10Hz -- Signal Start End Period Comment -- VDISPEN 0 200 12.8mS -- VSYNC 219 223 256uS -- VBLANK 201 259 3.712mS not VDISPEN -- -- MZ700 (Colour) -- -- 64.056uS 15.611KHz -- Signal Start End Period Comment -- HDISPEN 0 320 36.088uS -- HBLANK 320 567 27.968uS -- BLNK 320 548 25.7126uS -- HSYNC 400 440 4.567375uS -- -- 16.654mS 50.0374Hz -- Signal Start End Period Comment -- 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 -- -- 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 . --------------------------------------------------------------------------------------------------------- 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:- -- .. -- .. -- .. -- -- 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], 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], sets the Red bit mask (1 bit = 1 pixel, 8 pixels per byte). -- x[2|A], sets the Green bit mask (1 bit = 1 pixel, 8 pixels per byte). -- x[3|B], 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'; -- 0, 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, 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, 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, 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;