Updates due to WD1793 development

This commit is contained in:
Philip Smart
2022-01-23 14:11:13 +00:00
parent 2abc2b42af
commit cf9b4d4209
26 changed files with 4475 additions and 824 deletions

View File

@@ -638,8 +638,6 @@ architecture rtl of VideoController is
signal FB_PALETTE_B : std_logic_vector(4 downto 0); -- Current palette map value for given video state input.
signal CONFIG_LAST : std_logic_vector(CONFIG_WIDTH); -- Configuration string change detection, used by the Sharp MZ Series Emulator to configure video settings rather than register writes.
signal VIDEO_ADDRi : std_logic_vector(23 downto 0); -- CPU Address bus. Upper byte is for direct addressing (ie /= 0) and used by external systems or a soft CPU.
signal VIDEO_DATA_INi : std_logic_vector(31 downto 0); -- Data bus into video module.
signal VIDEO_DATA_OUTi : std_logic_vector(31 downto 0); -- Data bus out from video module to CPU.
@@ -3646,8 +3644,8 @@ begin
-- 1 = Clear to val. Start Location (16 bit), End Location (16 bit), Red Filter, Green Filter, Blue Filter
--
-- IO Range for Graphics enhancements is set by the Video Mode registers at 0xB0->.
-- 0xB8=<val> sets the mode of the Video Module. Bits [3:0] define the Video Module machine compatibility. 0000 = MZ80K, 0001 = MZ80C, 0010 = MZ1200, 0011 = MZ80A, 0100 = MZ-700, 0101 = MZ-1500, 0110 = MZ-800, 0111 = MZ-80B, 1000 = MZ-2000, 1001 = MZ-2200, 1010 = MZ-2500. [4] = 0 - 40 col, 1 - 80 col, [5] = 0 - mono, 1 - colour.
-- [4] defines the colour mode, 0 = mono, 1 = colour - ignored on certain modes. [5] defines wether PCGRAM is enabled, 0 = disabled, 1 = enabled. [7:6] define the VGA mode.
-- 0xB8=<val> sets the mode of the Video Module. Bits [3:0] define the Video Module machine compatibility. 0000 = MZ80K, 0001 = MZ80C, 0010 = MZ1200, 0011 = MZ80A, 0100 = MZ-700, 0101 = MZ-1500, 0110 = MZ-800, 0111 = MZ-80B, 1000 = MZ-2000, 1001 = MZ-2200, 1010 = MZ-2500.
-- [4] = 0 - 40 col, 1 - 80 col, [5] = 0 - mono, 1 - colour. [5] defines the colour mode, 0 = mono, 1 = colour - ignored on certain modes. [6] defines wether PCGRAM is enabled, 0 = disabled, 1 = enabled.
-- 0xB9=<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).
-- 0xBA=<val> sets the Red bit mask (1 bit = 1 pixel, 8 pixels per byte).
-- 0xBB=<val> sets the Green bit mask (1 bit = 1 pixel, 8 pixels per byte).
@@ -3715,16 +3713,17 @@ begin
CGRAM_WEn <= '1';
GPU_PARAMS <= (others => '0');
GPU_COMMAND <= (others => '0');
CONFIG_LAST <= CONFIG;
CONFIG_LAST <= CONFIG; --(others => '0');
else
-- Change detection, whenever the config value changes, use its values to update the register settings.
-- Only used in the Sharp MZ Series Emulator.
CONFIG_LAST <= CONFIG;
-- if CONFIG /= CONFIG_LAST
-- CONFIG_LAST <= CONFIG;
-- Clear the data available flag on each cycle. If in an active read state the signal will mirror the RD/CS combination.
VIDEO_DATA_AVAILn <= '1';
VIDEO_DATA_AVAILn <= '1';
-- If the GPU goes busy, clear the command register ready for next command.
--
@@ -4294,81 +4293,91 @@ begin
-- Sharp MZ Series Emulation configuration. When using the emulation an external I/O processor changes the settings via a configuration string and not via
-- register writes. This block detects changes and updates internal settings.
--
-- When not using the emulation this vector wont change and therefore no updates will be made.
if CONFIG(VGAMODE) /= CONFIG_LAST(VGAMODE) then
VGA_MODE_REG(CONFIG(VGAMODE)'length-1 downto 0) <= CONFIG(VGAMODE);
end if;
-- Machine mode change?
if CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
--
-- Setup the machine type based on the CONFIG string.
--
if CONFIG(MZ80K) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0000";
elsif CONFIG(MZ80C) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0001";
elsif CONFIG(MZ1200) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0010";
elsif CONFIG(MZ80A) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0011";
elsif CONFIG(MZ700) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0100";
elsif CONFIG(MZ800) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0101";
elsif CONFIG(MZ1500) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0110";
elsif CONFIG(MZ80B) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "0111";
elsif CONFIG(MZ2000) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "1000";
elsif CONFIG(MZ2200) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "1001";
elsif CONFIG(MZ2500) = '1' then
VIDEO_MODE_REG(3 downto 0)<= "1010";
else
VIDEO_MODE_REG(3 downto 0)<= "0100";
-- if CONFIG(CHANGED) = '1' then
--NEED TO TAKE INTO ACCOUNT THE RENDERING, ONLY CHANGE DURING VERTICAL BLANKING
-- When not using the emulation this vector wont change and therefore no updates will be made.
-- if CONFIG(VGAMODE) /= CONFIG_LAST(VGAMODE) then
-- VGA_MODE_REG(CONFIG(VGAMODE)'length-1 downto 0) <= CONFIG(VGAMODE);
-- end if;
-- Machine mode change?
if CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
--
-- Setup the machine type based on the CONFIG string.
--
if CONFIG(MZ80K) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0000" then
VIDEO_MODE_REG(3 downto 0)<= "0000";
elsif CONFIG(MZ80C) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0001" then
VIDEO_MODE_REG(3 downto 0)<= "0001";
elsif CONFIG(MZ1200) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0010" then
VIDEO_MODE_REG(3 downto 0)<= "0010";
elsif CONFIG(MZ80A) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0011" then
VIDEO_MODE_REG(3 downto 0)<= "0011";
elsif CONFIG(MZ700) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0100" then
VIDEO_MODE_REG(3 downto 0)<= "0100";
elsif CONFIG(MZ800) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0101" then
VIDEO_MODE_REG(3 downto 0)<= "0101";
elsif CONFIG(MZ1500) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0110" then
VIDEO_MODE_REG(3 downto 0)<= "0110";
elsif CONFIG(MZ80B) = '1' and VIDEO_MODE_REG(3 downto 0) /= "0111" then
VIDEO_MODE_REG(3 downto 0)<= "0111";
elsif CONFIG(MZ2000) = '1' and VIDEO_MODE_REG(3 downto 0) /= "1000" then
VIDEO_MODE_REG(3 downto 0)<= "1000";
elsif CONFIG(MZ2200) = '1' and VIDEO_MODE_REG(3 downto 0) /= "1001" then
VIDEO_MODE_REG(3 downto 0)<= "1001";
elsif CONFIG(MZ2500) = '1' and VIDEO_MODE_REG(3 downto 0)<= "1010" then
VIDEO_MODE_REG(3 downto 0)<= "1010";
end if;
CONFIG_LAST(CURRENTMACHINE) <= CONFIG(CURRENTMACHINE);
end if;
end if;
-- Display mode change?
if CONFIG(CURRENTDISPLAY) /= CONFIG_LAST(CURRENTDISPLAY) then
if CONFIG(NORMAL80) = '1' or CONFIG(COLOUR80)= '1' then
VIDEO_MODE_REG(4) <= '1';
else
VIDEO_MODE_REG(4) <= '0';
-- Display mode change?
if CONFIG(CURRENTDISPLAY) /= CONFIG_LAST(CURRENTDISPLAY) then
if (CONFIG(NORMAL80) = '1' or CONFIG(COLOUR80)= '1') and VIDEO_MODE_REG(4) /= '1' then
VIDEO_MODE_REG(4) <= '1';
else
VIDEO_MODE_REG(4) <= '0';
end if;
if (CONFIG(COLOUR) = '1' or CONFIG(COLOUR80)= '1') and VIDEO_MODE_REG(5) /= '1' then
VIDEO_MODE_REG(5) <= '1';
else
VIDEO_MODE_REG(5) <= '0';
end if;
CONFIG_LAST(CURRENTDISPLAY) <= CONFIG(CURRENTDISPLAY);
end if;
if CONFIG(COLOUR) = '1' or CONFIG(COLOUR80)= '1' then
VIDEO_MODE_REG(5) <= '1';
else
VIDEO_MODE_REG(5) <= '0';
-- Option changes?
if CONFIG(VRAMDISABLE) /= CONFIG_LAST(VRAMDISABLE) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(4) <= CONFIG(VRAMDISABLE);
CONFIG_LAST(VRAMDISABLE) <= CONFIG(VRAMDISABLE);
end if;
end if;
-- Option changes?
if CONFIG(VRAMDISABLE) /= CONFIG_LAST(VRAMDISABLE) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(4) <= CONFIG(VRAMDISABLE);
end if;
if CONFIG(GRAMDISABLE) /= CONFIG_LAST(VRAMDISABLE) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(5) <= CONFIG(GRAMDISABLE);
end if;
if CONFIG(VRAMWAIT) /= CONFIG_LAST(VRAMWAIT) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(6) <= CONFIG(VRAMWAIT);
end if;
if CONFIG(PCGRAM) /= CONFIG_LAST(PCGRAM) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(7) <= CONFIG(PCGRAM);
end if;
-- GRAM installed change?
if CONFIG(GRAPHICSOPTION) /= CONFIG_LAST(GRAPHICSOPTION) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
OPTION_REG(0) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMI);
OPTION_REG(1) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMII);
OPTION_REG(2) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMIII);
OPTION_REG(3) <= CONFIG(GRAPHICSOPTION)(OPT_PCG);
end if;
-- OSD Menu/Status change?
if CONFIG(MENUENABLE) /= CONFIG_LAST(MENUENABLE) then
VGA_ATTR_REG(6) <= CONFIG(MENUENABLE);
end if;
if CONFIG(STATUSENABLE) /= CONFIG_LAST(STATUSENABLE) then
VGA_ATTR_REG(7) <= CONFIG(STATUSENABLE);
end if;
if CONFIG(GRAMDISABLE) /= CONFIG_LAST(VRAMDISABLE) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(5) <= CONFIG(GRAMDISABLE);
CONFIG_LAST(GRAMDISABLE) <= CONFIG(GRAMDISABLE);
end if;
if CONFIG(VRAMWAIT) /= CONFIG_LAST(VRAMWAIT) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(6) <= CONFIG(VRAMWAIT);
CONFIG_LAST(VRAMWAIT) <= CONFIG(VRAMWAIT);
end if;
if CONFIG(PCGRAM) /= CONFIG_LAST(PCGRAM) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
GRAM_MODE_REG(7) <= CONFIG(PCGRAM);
CONFIG_LAST(PCGRAM) <= CONFIG(PCGRAM);
end if;
-- GRAM installed change?
if CONFIG(GRAPHICSOPTION) /= CONFIG_LAST(GRAPHICSOPTION) or CONFIG(CURRENTMACHINE) /= CONFIG_LAST(CURRENTMACHINE) then
OPTION_REG(0) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMI);
OPTION_REG(1) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMII);
OPTION_REG(2) <= CONFIG(GRAPHICSOPTION)(OPT_GRAMIII);
OPTION_REG(3) <= CONFIG(GRAPHICSOPTION)(OPT_PCG);
CONFIG_LAST(GRAPHICSOPTION) <= CONFIG(GRAPHICSOPTION);
end if;
-- OSD Menu/Status change?
if CONFIG(MENUENABLE) /= CONFIG_LAST(MENUENABLE) then
VGA_ATTR_REG(6) <= CONFIG(MENUENABLE);
CONFIG_LAST(MENUENABLE) <= CONFIG(MENUENABLE);
end if;
if CONFIG(STATUSENABLE) /= CONFIG_LAST(STATUSENABLE) then
VGA_ATTR_REG(7) <= CONFIG(STATUSENABLE);
CONFIG_LAST(STATUSENABLE) <= CONFIG(STATUSENABLE);
end if;
-- end if;
-- Update the video mode according to the stored register value.
--

View File

@@ -111,6 +111,7 @@ architecture rtl of coreMZ is
signal CPUCLK_75MHZ : std_logic;
signal PLL_LOCKED : std_logic;
signal RESETn : std_logic := '0';
signal VRESETn : std_logic := '0';
signal EMURESETn : std_logic := '0';
signal RESET_COUNTER : unsigned(3 downto 0) := (others => '1');
signal CTRLREG_RESET : std_logic := '1'; -- Flag to indicate when a hard reset occurs so that registers can be preloaded based on conditions.
@@ -129,6 +130,8 @@ architecture rtl of coreMZ is
signal CS_CPLD_CFGn : std_logic; -- Chip Select to write to the CPLD configuration register at 0x6E.
signal VZ80_BUSACKni : std_logic; -- Internal combination of BUSACK signals.
signal COLOUR_CARRIER_FREQ : std_logic; -- Modulator colour carrier frequency output by video module.
signal COMPOSITE_SYNCn : std_logic; -- Composite sync (negative) for use with the composite video out.
signal COMPOSITE_SYNC : std_logic; -- Composite sync (positive) for use with the composite video out.
signal HBLANK_OUTi : std_logic; -- Horizontal video blanking.
signal VBLANK_OUTi : std_logic; -- Vertical video blanking.
@@ -206,6 +209,7 @@ architecture rtl of coreMZ is
signal MZ_KEYB_ADDR : std_logic_vector(15 downto 0);
signal MZ_KEYB_DOUT : std_logic_vector(7 downto 0);
signal MZ_KEYB_DIN : std_logic_vector(7 downto 0);
signal MZ_KEYB_INITn : std_logic;
--
-- Master Control signals and configuration.
--
@@ -238,12 +242,14 @@ architecture rtl of coreMZ is
signal MZ_INTR_ADDR : std_logic_vector(15 downto 0);
signal MZ_INTR_DOUT : std_logic_vector(7 downto 0);
signal MZ_INTR_DIN : std_logic_vector(7 downto 0);
signal MZ_INTR_INITn : std_logic;
-- I/O Processor signals.
signal IOP_MCTRL_CSn : std_logic;
signal IOP_MZ80K_CSn : std_logic;
signal IOP_MZ80B_CSn : std_logic;
signal IOP_KEYB_CSn : std_logic;
signal IOP_FDD_CSn : std_logic;
signal IOP_CMT0_CSn : std_logic;
signal IOP_CMT1_CSn : std_logic;
signal IOP_INTR_CSn : std_logic;
@@ -252,11 +258,19 @@ architecture rtl of coreMZ is
signal IOP_MZ80K_DOUT : std_logic_vector(7 downto 0);
signal IOP_MZ80B_DOUT : std_logic_vector(7 downto 0);
signal IOP_KEYB_DOUT : std_logic_vector(7 downto 0);
signal IOP_FDD_DOUT : std_logic_vector(7 downto 0);
signal IOP_CMT0_DOUT : std_logic_vector(7 downto 0);
signal IOP_CMT1_DOUT : std_logic_vector(7 downto 0);
signal IOP_INTR_DOUT : std_logic_vector(7 downto 0);
signal IOP_SND_DOUT : std_logic_vector(7 downto 0);
--
-- FDD
--
signal T80_FDD_INTn : std_logic;
signal MZ_FDD_INTRn : std_logic;
signal MZ_FDD_CSn : std_logic;
signal MZ_FDD_DI : std_logic_vector(7 downto 0);
--
-- CMT
--
signal CMT_BUS_OUT : std_logic_vector(CMT_BUS_OUT_WIDTH);
@@ -270,6 +284,7 @@ architecture rtl of coreMZ is
signal MZ_CMT_ADDR : std_logic_vector(15 downto 0);
signal MZ_CMT_DOUT : std_logic_vector(7 downto 0);
signal MZ_CMT_DIN : std_logic_vector(7 downto 0);
signal MZ_CMT_INITn : std_logic;
signal MZ_CMT_INTRn : std_logic;
--
-- Sound
@@ -281,6 +296,7 @@ architecture rtl of coreMZ is
signal MZ_SND_M1n : std_logic;
signal MZ_SND_ADDR : std_logic_vector(15 downto 0);
signal MZ_SND_DOUT : std_logic_vector(7 downto 0);
signal MZ_SND_INITn : std_logic;
signal MZ_SOUND_LEFT : std_logic;
signal MZ_SOUND_RIGHT : std_logic;
@@ -310,6 +326,7 @@ architecture rtl of coreMZ is
signal MZ80K_CS_ROMn : std_logic;
signal MZ80K_CS_RAMn : std_logic;
signal MZ80K_CS_VRAMn : std_logic;
signal MZ80K_CS_FDDn : std_logic;
signal MZ80K_CS_MEM_Gn : std_logic;
signal MZ80K_CS_IO_Gn : std_logic;
signal MZ80K_CS_GRAMn : std_logic;
@@ -339,6 +356,7 @@ architecture rtl of coreMZ is
signal MZ80B_CS_ROMn : std_logic;
signal MZ80B_CS_RAMn : std_logic;
signal MZ80B_CS_VRAMn : std_logic;
signal MZ80B_CS_FDDn : std_logic;
signal MZ80B_CS_IO_CTRLn : std_logic;
-- signal MZ80B_CS_IO_GFBn : std_logic;
signal MZ80B_CS_IO_Gn : std_logic;
@@ -454,24 +472,37 @@ begin
-- and a counter to set minimum width.
--
FPGARESET: process(CLOCK_50, PLL_LOCKED, MCTRL_RESETn)
variable HOST_RESET_LAST : std_logic_vector(1 downto 0);
begin
if PLL_LOCKED = '0' then
RESET_COUNTER <= (others => '1');
RESETn <= '0';
VRESETn <= '0';
EMURESETn <= '0';
elsif PLL_LOCKED = '1' then
if rising_edge(CLOCK_50) then
if RESET_COUNTER /= 0 then
RESET_COUNTER <= RESET_COUNTER - 1;
--elsif HOST_RESET_LAST = "00" and (VIDEO_WRn = '1' or VIDEO_RDn = '1') then
elsif VIDEO_WRn = '0' and VIDEO_RDn = '0' then
RESETn <= '0';
VRESETn <= '0';
EMURESETn <= '0';
-- RESET_COUNTER <= (others => '1');
elsif MCTRL_RESETn = '0' then
EMURESETn <= '0';
-- VRESETn <= '0';
-- RESETn <= '0';
-- RESET_COUNTER <= (others => '1');
elsif (VIDEO_WRn = '1' or VIDEO_RDn = '1') and MCTRL_RESETn = '1' and RESET_COUNTER = 0 then
-- elsif MCTRL_RESETn = '1' and RESET_COUNTER = 0 then
RESETn <= '1';
VRESETn <= '1';
EMURESETn <= '1';
end if;
HOST_RESET_LAST := VIDEO_WRn & VIDEO_RDn;
end if;
end if;
end process;
@@ -541,7 +572,9 @@ begin
else
MZ80K_WAITn when CONFIG(MZ_K) = '1'
else MZ80B_WAITn;
T80_INTn <= MZ80K_INTn when CONFIG(MZ_K) = '1'
T80_INTn <= '0' when T80_FDD_INTn = '0' and CONFIG(FDDINTEN) = '1'
else
MZ80K_INTn when CONFIG(MZ_K) = '1'
else
MZ80B_INTn;
T80_NMIn <= MZ80K_NMIn when CONFIG(MZ_K) = '1'
@@ -552,15 +585,17 @@ begin
else
CPU_INFO_DATA when CS_CPU_INFOn = '0' -- Read CPU version & hw build information.
else
SYSRAM_DO when SYSRAM_SELECT ='1' -- Read from System RAM
SYSRAM_DO when SYSRAM_SELECT = '1' -- Read from System RAM
else
SYSROM_DO when SYSROM_SELECT ='1' -- Read from System ROM
SYSROM_DO when SYSROM_SELECT = '1' -- Read from System ROM
else
CORE_DATA_OUT(7 downto 0) when CORE_DATA_AVAILn = '0' --CORE_VIDEO_RDn = '0'
else
MZ80B_DI when CONFIG(MZ_B) = '1' and MZ80B_DATA_AVAILn = '0'
MZ_FDD_DI when MZ_FDD_CSn = '0'
else
MZ80K_DI when CONFIG(MZ_K) = '1' and MZ80K_DATA_AVAILn = '0'
MZ80B_DI when (T80_M1n = '0' and T80_IORQn = '0') or (CONFIG(MZ_B) = '1' and MZ80B_DATA_AVAILn = '0')
else
MZ80K_DI when (T80_M1n = '0' and T80_IORQn = '0') or (CONFIG(MZ_K) = '1' and MZ80K_DATA_AVAILn = '0')
else (others => '1');
-- Busack is granted during RESET, through the BUSRQ mechanism or during the period the clock has been disabled. This is necessary
@@ -595,7 +630,7 @@ begin
else
'1' when CORE_ADDR(23 downto 17) = "0001001" and VZ80_BUSACKni = '0'
else '0';
SYSRAM_WEN <= '1' when SYSRAM_SELECT = '1' and CORE_WRn = '0'
SYSRAM_WEN <= '1' when SYSRAM_SELECT = '1' and CORE_WRn = '0'
else '0';
RAMBANK <= (not CORE_ADDR(15))&CORE_ADDR(14 downto 12) when CONFIG(MZ_B) = '1' and MZ80B_CS_SWP_MEMn = '0' and VZ80_BUSACKni = '1'
@@ -653,7 +688,7 @@ begin
CLOCK_50 => CLOCK_50, -- 50MHz main FPGA clock.
-- Reset.
VRESETn => RESETn, -- Internal reset.
VRESETn => VRESETn, -- Internal reset.
-- V[name] = Voltage translated signals which mirror the mainboard signals but at a lower voltage.
-- Address Bus
@@ -684,8 +719,8 @@ begin
HBLANK_OUT => HBLANK_OUTi, -- Horizontal blanking.
VBLANK_OUT => VBLANK_OUTi, -- Vertical blanking.
COLR_OUT => COLOUR_CARRIER_FREQ, -- Composite colour and RF base frequency.
CSYNC_OUTn => CSYNC_OUTn, -- Composite sync (negative).
CSYNC_OUT => CSYNC_OUT, -- Composite sync (positive).
CSYNC_OUTn => COMPOSITE_SYNCn, -- Composite sync (negative).
CSYNC_OUT => COMPOSITE_SYNC, -- Composite sync (positive).
-- RGB & Composite input signals.
VWAITn_V_CSYNC => VWAITn_A21_V_CSYNC, -- Wait signal to the CPU when accessing FPGA video RAM / Composite sync from mainboard.
@@ -751,6 +786,18 @@ begin
------------------------------------------------------------------------------------
-- MZ-80K Group Machine Dependent Hardware
-- IOP Address: 0x310000:0x31FFFF
-- 0x310000:0x310003 - 8255
-- 0x310004:0x310007 - 8254
-- 0x310008:0x31000B - LS367
-- 0x31000C:0x31000F - JOYSTK
-- 0x310010:0x310013 - PIO
-- 0x310014:0x310017 - PSG0
-- 0x310018:0x31001B - PSG1
-- 0x31001C:0x31001F - GCRTC
-- 0x310020:0x310023 - GDMD
-- 0x310024:0x310027 - GRF
-- 0x310028:0x31002B - GWF
-- 0x31002C:0x31002F - GPALLET
------------------------------------------------------------------------------------
MZ80KHW : entity work.mz80k_hw
port map (
@@ -780,6 +827,7 @@ begin
CS_RAMn => MZ80K_CS_RAMn,
CS_VRAMn => MZ80K_CS_VRAMn, -- VRAM Select
CS_MEM_Gn => MZ80K_CS_MEM_Gn, -- Memory mapped Peripherals Select
CS_FDDn => MZ80K_CS_FDDn, -- Floppy Disk Controller select.
CS_IO_Gn => MZ80K_CS_IO_Gn, -- Graphics Options IO Select range
CS_GRAMn => MZ80K_CS_GRAMn, -- Colour GRAM Select
-- CS_IO_GFBn => MZ80K_CS_IO_GFBn, -- Graphics FB IO Select range
@@ -810,7 +858,7 @@ begin
IOP_CSn => IOP_MZ80K_CSn, -- Chip select from the I/O Processor decoding.
IOP_WRn => VZ80_WRni, -- Write Enable.
IOP_RDn => VZ80_RDni, -- Read Enable.
IOP_ADDR => VZ80_ADDRi(3 downto 0), -- Address bus.
IOP_ADDR => VZ80_ADDRi(5 downto 0), -- Address bus.
IOP_DOUT => VZ80_DATAi, -- Data into the MZ80K unit.
IOP_DIN => IOP_MZ80K_DOUT -- Data out from the MZ80K unit.
);
@@ -851,6 +899,7 @@ begin
CS_RAMn => MZ80B_CS_RAMn,
CS_VRAMn => MZ80B_CS_VRAMn, -- VRAM Select
CS_GRAMn => MZ80B_CS_GRAMn, -- MZ80B GRAM Select
CS_FDDn => MZ80B_CS_FDDn, -- Floppy Disk Controller select.
-- CS_IO_GFBn => MZ80B_CS_IO_GFBn, -- Graphics FB IO Select range
CS_IO_Gn => MZ80B_CS_IO_Gn, -- Graphics Options IO Select range
CS_IO_CTRLn => MZ80B_CS_IO_CTRLn, -- PPI/PIO video control registers.
@@ -892,6 +941,65 @@ begin
else '1';
------------------------------------------------------------------------------------
-- FDD Controller
-- IOP Address: 0x330000:0x33FFFF
-- 0x330000 : 0x800 = WD1793 Sector Cache.
-- 0x330800 : 0x1 = WD1793 COMMAND/STATUS Register
-- 0x330801 : 0x1 = WD1793 TRACK Register
-- 0x330802 : 0x1 = WD1793 SECTOR Register
-- 0x330803 : 0x1 = WD1793 DATA Register
-- 0x330804 : 0x1 = WD1793 Sector Cache current byte.
-- 0x330808 : 0x6 = WD1793 Read Address status stack
-- 0x331000 : 0x1 = Control/Status Register
-- 0x331001 : 0x1 = WD1793 active sector.
-- 0x331002 : 0x1 = WD1793 active track.
-- 0x331003 : 0x1 = FDC Signal state and status.
------------------------------------------------------------------------------------
FDD : entity work.fdd
port map (
RSTn => EMURESETn,
-- Clock signals needed by this module.
CLKBUS => CLKBUS,
-- Different operations modes.
CONFIG => CONFIG,
-- Z80 CPU Interface.
Z80_DI => MZ_FDD_DI,
Z80_DO => T80_DATA_OUT,
Z80_ADDR => T80_ADDR,
Z80_RDn => T80_RDn,
Z80_WRn => T80_WRn,
Z80_INTn => T80_FDD_INTn,
-- Module activation signal (allows for different memory address mapping in the controlling module).
-- The signal should include IORQn/MREQn.
FDD_CSn => MZ_FDD_CSn,
-- IOP interrupt. This is raised whenever the WD1793 requires servicing, ie. the IOP_SECTOR_REQ line goes active.
IOP_INTn => MZ_FDD_INTRn,
-- I/O Processor Interface
IOP_CSn => IOP_FDD_CSn, -- Chip select from the I/O Processor decoding.
IOP_WRn => VZ80_WRn, -- Write Enable.
IOP_RDn => VZ80_RDn, -- Read Enable.
IOP_ADDR => VZ80_ADDR(12 downto 0), -- Address bus.
IOP_DOUT => VZ80_DATAi, -- Data into the mctrl unit.
IOP_DIN => IOP_FDD_DOUT -- Data out from the mctrl unit.
);
-- Select address for external access to the Hardware FDD logic.
IOP_FDD_CSn <= '0' when (CORE_ADDR >= std_logic_vector(to_unsigned(16#330000#, CORE_ADDR'LENGTH)) and CORE_ADDR < std_logic_vector(to_unsigned(16#340000#, CORE_ADDR'LENGTH))) and CORE_MREQn = '0' and VZ80_BUSACKni = '0'
else '1';
-- Floppy disk drive access by the T80/active host.
MZ_FDD_CSn <= '0' when CONFIG(MZ_K) = '1' and CONFIG(FDDENABLE) = '1' and MZ80K_CS_FDDn = '0' and VZ80_BUSACKni = '1' -- MZ-80K Group Machines, 80K,80C,1200,80A,700
else
'0' when CONFIG(MZ_B) = '1' and CONFIG(FDDENABLE) = '1' and MZ80B_CS_FDDn = '0' and VZ80_BUSACKni = '1' -- MZ-80B Group Machines, 80B,2000,2200,2500
else '1';
------------------------------------------------------------------------------------
-- CMT Controller
-- IOP Address: 0x340000:0x36FFFF
@@ -966,6 +1074,7 @@ begin
CMT_DOUT => MZ_CMT_DOUT,
CMT_DIN => MZ_CMT_DIN,
CMT_INIT_HW => '0', -- FSM to initialise underlying hardware?
CMT_INIT_DONEn => MZ_CMT_INITn, -- Underlying hardware initialised.
-- Arbiter control signals
CMT_GRANTn => MZ_CMT_GRANTn,
@@ -1019,6 +1128,7 @@ begin
KEYB_DOUT => MZ_KEYB_DOUT,
KEYB_DIN => MZ_KEYB_DIN,
KEYB_INIT_HW => '1', -- FSM to initialise underlying hardware?
KEYB_INIT_DONEn => MZ_KEYB_INITn, -- Underlying hardware initialised.
KEYB_INTRn => MZ_KEYB_INTRn, -- Keyboard generating an external I/O processor interrupt.
-- Arbiter control signals
@@ -1047,7 +1157,12 @@ begin
else (others => '1');
------------------------------------------------------------------------------------
-- Sound generator (8253/bit toggle).
-- Sound module.
-- In host mode creates sound by accessing the underlying hardware (ie 8253/bit
-- toggle) and generates sound from the host speaker.
-- In FPGA mode, provide a full stereo DAC with sound mixing and volume control
-- outputting the stereo audio from the FPGA.
--
-- IOP Address: 0x300200:0x3002FF
------------------------------------------------------------------------------------
SND : entity work.snd
@@ -1066,7 +1181,7 @@ begin
Z80_WRn => T80_WRn,
CS_SNDn => MZ80K_CS_8253n,
-- Physical bus connection to underlying MZ-700 hardware.
-- Physical bus connection to underlying host hardware.
SND_RDn => MZ_SND_RDn,
SND_WRn => MZ_SND_WRn,
SND_MREQn => MZ_SND_MREQn,
@@ -1075,6 +1190,8 @@ begin
SND_ADDR => MZ_SND_ADDR,
SND_DOUT => MZ_SND_DOUT,
SND_DIN => (others => '0'),
SND_INIT_HW => '0', -- FSM to initialise underlying hardware?
SND_INIT_DONEn => MZ_SND_INITn, -- Underlying hardware initialised.
-- Arbiter control signals
SND_GRANTn => MZ_SND_GRANTn,
@@ -1136,18 +1253,22 @@ begin
-- First FPGA side device requiring BUS access.
BUSGRANT0n => MZ_KEYB_GRANTn,
BUSBUSY0n => MZ_KEYB_BUSYn,
BUSINIT0n => MZ_KEYB_INITn,
-- Second FPGA side device requiring BUS access.
BUSGRANT1n => MZ_CMT_GRANTn,
BUSBUSY1n => MZ_CMT_BUSYn,
BUSINIT1n => MZ_CMT_INITn,
-- Third FPGA side device requiring BUS access.
BUSGRANT2n => MZ_SND_GRANTn,
BUSBUSY2n => MZ_SND_BUSYn,
BUSINIT2n => MZ_SND_INITn,
-- Fourth FPGA side device requiring BUS access.
BUSGRANT3n => MZ_INTR_GRANTn,
BUSBUSY3n => MZ_INTR_BUSYn
BUSBUSY3n => MZ_INTR_BUSYn,
BUSINIT3n => MZ_INTR_INITn
);
------------------------------------------------------------------------------------
@@ -1167,7 +1288,7 @@ begin
-- Interrupt sources.
INTR0n => MZ_KEYB_INTRn,
INTR1n => MZ_CMT_INTRn,
INTR2n => '1',
INTR2n => MZ_FDD_INTRn,
INTR3n => '1',
INTR4n => '1',
INTR5n => '1',
@@ -1184,6 +1305,7 @@ begin
INTR_DOUT => MZ_INTR_DOUT,
INTR_DIN => MZ_INTR_DIN,
INTR_INIT_HW => '0',
INTR_INIT_DONEn => MZ_INTR_INITn, -- Underlying hardware initialised.
-- Arbiter control signals
INTR_GRANTn => MZ_INTR_GRANTn,
@@ -1528,6 +1650,8 @@ begin
else
IOP_KEYB_DOUT when IOP_KEYB_CSn = '0' and VZ80_RDni = '0' and VZ80_BUSACKni = '0'
else
IOP_FDD_DOUT when IOP_FDD_CSn = '0' and VZ80_RDni = '0' and VZ80_BUSACKni = '0'
else
IOP_MZ80K_DOUT when IOP_MZ80K_CSn = '0' and VZ80_RDni = '0' and VZ80_BUSACKni = '0'
else
IOP_MZ80B_DOUT when IOP_MZ80B_CSn = '0' and VZ80_RDni = '0' and VZ80_BUSACKni = '0'
@@ -1565,8 +1689,15 @@ begin
MZ80B_KEYB_STALL when CONFIG(MZ_B) = '1'
else '0';
COLR_OUT <= MZ_SOUND_LEFT or MZ_SOUND_RIGHT when MODE_EMU = '1'
else
COLOUR_CARRIER_FREQ;
-- As the tranZPUter design progresses, a short coming is the requirement to output audio from the emulation hardware. We thus, temporarily, use
-- the composite outputs which are rarely used.
COLR_OUT <= COLOUR_CARRIER_FREQ;
CSYNC_OUTn <= MZ_SOUND_LEFT when MODE_EMU = '1'
else
'1'; --COMPOSITE_SYNCn;
CSYNC_OUT <= MZ_SOUND_RIGHT when MODE_EMU = '1'
else
'1'; --COMPOSITE_SYNC;
end architecture;

View File

@@ -0,0 +1,181 @@
## Generated SDC file "VideoController700_constraints.sdc"
## Copyright (C) 1991-2013 Altera Corporation
## Your use of Altera Corporation's design tools, logic functions
## and other software and tools, and its AMPP partner logic
## functions, and any output files from any of the foregoing
## (including device programming or simulation files), and any
## associated documentation or information are expressly subject
## to the terms and conditions of the Altera Program License
## Subscription Agreement, Altera MegaCore Function License
## Agreement, or other applicable license agreement, including,
## without limitation, that your use is for the sole purpose of
## programming logic devices manufactured by Altera and sold by
## Altera or its authorized distributors. Please refer to the
## applicable agreement for further details.
## VENDOR "Altera"
## PROGRAM "Quartus II"
## VERSION "Version 13.1.0 Build 162 10/23/2013 SJ Full Version"
## DATE "Fri Jul 3 00:11:58 2020"
##
## DEVICE "EP3C25E144C8"
##
#**************************************************************
# Time Information
#**************************************************************
set_time_format -unit ns -decimal_places 3
#**************************************************************
# Create Clock
#**************************************************************
create_clock -name {altera_reserved_tck} -period 100.000 -waveform { 0.000 50.000 } [get_ports {altera_reserved_tck}]
create_clock -name {CLOCK_50} -period 20.000 -waveform { 0.000 10.000 } [get_ports {CLOCK_50}]
create_clock -name {VZ80_CLK} -period 50.000 -waveform { 0.000 14.1242938 } [get_ports {VZ80_CLK}]
#create_clock -name {softT80:\CPU0:T80CPU|T80_CLK} -period 50
#**************************************************************
# Create Generated Clock
#**************************************************************
#derive_pll_clocks
#create_generated_clock -name {CPUCLK_75MHZ} -source [get_pins {COREMZPLL1|altpll_component|auto_generated|pll1|inclk[0]}] -duty_cycle 50/1 -multiply_by 3 -divide_by 2 -phase 0.00 -master_clock {CLOCK_50} [get_pins {COREMZPLL1|altpll_component|auto_generated|pll1|clk[0]}]
#**************************************************************
# Set Clock Latency
#**************************************************************
#**************************************************************
# Set Clock Uncertainty
#**************************************************************
derive_clock_uncertainty
#**************************************************************
# Set Input Delay
#**************************************************************
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {CLOCK_50}]
set_input_delay -add_delay -clock [get_clocks {VZ80_CLK}] 1.000 [get_ports {VZ80_CLK}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VIDEO_WRn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VIDEO_RDn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_WRn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_RDn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_IORQn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_MREQn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_M1n}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_ADDR[*]}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_DATA[*]}]
# set_input_delay -add_delay -clock [get_clocks {SYS_CLK}] 1.000 [get_ports {VGA_B_COMPOSITE}]
# set_input_delay -add_delay -clock [get_clocks {SYS_CLK}] 1.000 [get_ports {VGA_G_COMPOSITE}]
# set_input_delay -add_delay -clock [get_clocks {SYS_CLK}] 1.000 [get_ports {VGA_R_COMPOSITE}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VWAITn_A21_V_CSYNC}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A20_RFSHn_V_HSYNCn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A19_HALTn_V_VSYNCn}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_BUSRQn_V_G}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A16_WAITn_V_B}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A18_INTn_V_R}]
set_input_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A17_NMIn_V_COLR}]
# # Required for the Serial Flash Loader.
set_input_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {altera_reserved_tck}]
set_input_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {SFL_IV:\SERIALFLASHLOADER:SFL|altserial_flash_loader:altserial_flash_loader_component|\GEN_ASMI_TYPE_1:asmi_inst~ALTERA_DATA0}]
set_input_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {altera_reserved_tdi}]
set_input_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {altera_reserved_tms}]
#**************************************************************
# Set Output Delay
#**************************************************************
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_DATA[*]}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_WRn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_RDn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_IORQn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_MREQn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_M1n}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_BUSACKn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VWAITn_A21_V_CSYNC}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A20_RFSHn_V_HSYNCn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 5.000 [get_ports {VZ80_A19_HALTn_V_VSYNCn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_B[*]}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_B_COMPOSITE}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_G[*]}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_G_COMPOSITE}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_R[*]}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VGA_R_COMPOSITE}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {HSYNC_OUTn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {VSYNC_OUTn}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {COLR_OUT}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {CSYNC_OUT}]
set_output_delay -add_delay -clock [get_clocks {CLOCK_50}] 1.000 [get_ports {CSYNC_OUTn}]
# # Required for the Serial Flash Loader.
set_output_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {SFL_IV:\SERIALFLASHLOADER:SFL|altserial_flash_loader:altserial_flash_loader_component|\GEN_ASMI_TYPE_1:asmi_inst~ALTERA_DCLK}]
set_output_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {SFL_IV:\SERIALFLASHLOADER:SFL|altserial_flash_loader:altserial_flash_loader_component|\GEN_ASMI_TYPE_1:asmi_inst~ALTERA_SCE}]
set_output_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {SFL_IV:\SERIALFLASHLOADER:SFL|altserial_flash_loader:altserial_flash_loader_component|\GEN_ASMI_TYPE_1:asmi_inst~ALTERA_SDO}]
set_output_delay -add_delay -clock { altera_reserved_tck } 1.000 [get_ports {altera_reserved_tdo}]
#**************************************************************
# Set Clock Groups
#**************************************************************
set_clock_groups -asynchronous -group [get_clocks {altera_reserved_tck}]
#**************************************************************
# Set False Path
#**************************************************************
#**************************************************************
# Set Multicycle Path
#**************************************************************
#set_multicycle_path -from [get_clocks {CLOCK_50}] -to [get_clocks {CPUCLK_75MHZ}] -setup -end 3
#set_multicycle_path -from [get_clocks {CLOCK_50}] -to [get_clocks {CPUCLK_75MHZ}] -hold -end 2
#**************************************************************
# Set Maximum Delay
#**************************************************************
set_max_delay -from {VZ80_BUSRQn_V_G} -to [get_ports {VZ80_ADDR[*]}] 100.00
set_max_delay -from {CPLD_CFG_DATA[*]} -to [get_ports {VZ80_ADDR[*]}] 100.00
set_max_delay -from {CPU_CFG_DATA[*]} -to [get_ports {VZ80_ADDR[*]}] 100.00
set_max_delay -from {CPLD_CFG_DATA[*]} -to [get_ports {VZ80_DATA[*]}] 100.00
set_max_delay -from {CPU_CFG_DATA[*]} -to [get_ports {VZ80_DATA[*]}] 100.00
set_max_delay -from {VZ80_BUSRQn_V_G} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_RDn} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_IORQn} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VIDEO_RDn} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_ADDR[*]} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VWAITn_A21_V_CSYNC} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_A20_RFSHn_V_HSYNCn} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_A19_HALTn_V_VSYNCn} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_A18_INTn_V_R} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_A17_NMIn_V_COLR} -to [get_ports {VZ80_DATA[*]}] 50.00
set_max_delay -from {VZ80_A16_WAITn_V_B} -to [get_ports {VZ80_DATA[*]}] 50.00
#**************************************************************
# Set Minimum Delay
#**************************************************************
set_min_delay -from {VZ80_BUSRQn_V_G} -to [get_ports {VZ80_ADDR[*]}] 1.00
set_min_delay -from {CPLD_CFG_DATA[*]} -to [get_ports {VZ80_ADDR[*]}] 1.00
set_min_delay -from {CPU_CFG_DATA[*]} -to [get_ports {VZ80_ADDR[*]}] 1.00
set_min_delay -from {CPLD_CFG_DATA[*]} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {CPU_CFG_DATA[*]} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_BUSRQn_V_G} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_RDn} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_IORQn} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VIDEO_RDn} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_ADDR[*]} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VWAITn_A21_V_CSYNC} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_A20_RFSHn_V_HSYNCn} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_A19_HALTn_V_VSYNCn} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_A18_INTn_V_R} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_A17_NMIn_V_COLR} -to [get_ports {VZ80_DATA[*]}] 1.00
set_min_delay -from {VZ80_A16_WAITn_V_B} -to [get_ports {VZ80_DATA[*]}] 1.00
#**************************************************************
# Set Input Transition
#**************************************************************

View File

@@ -60,18 +60,22 @@ entity arbiter is
-- First FPGA side device requiring BUS access.
BUSGRANT0n : out std_logic;
BUSBUSY0n : in std_logic;
BUSINIT0n : in std_logic;
-- Second FPGA side device requiring BUS access.
BUSGRANT1n : out std_logic;
BUSBUSY1n : in std_logic;
BUSINIT1n : in std_logic;
-- Third FPGA side device requiring BUS access.
BUSGRANT2n : out std_logic;
BUSBUSY2n : in std_logic;
BUSINIT2n : in std_logic;
-- Fourth FPGA side device requiring BUS access.
BUSGRANT3n : out std_logic;
BUSBUSY3n : in std_logic
BUSBUSY3n : in std_logic;
BUSINIT3n : in std_logic
);
end arbiter;
@@ -164,7 +168,7 @@ begin
-- If device 1 has the bus, wait until it releases it.
when State_Dev0Busy =>
if BUSBUSY0n = '0' then
if BUSBUSY0n = '0' or BUSINIT0n = '1' then
MB_STATE <= State_Dev0Busy;
else
MB_STATE <= State_OfferDev1;
@@ -172,7 +176,7 @@ begin
-- If device 2 has the bus, wait until it releases it.
when State_Dev1Busy =>
if BUSBUSY1n = '0' then
if BUSBUSY1n = '0' or BUSINIT1n = '1' then
MB_STATE <= State_Dev1Busy;
else
MB_STATE <= State_OfferDev2;
@@ -180,7 +184,7 @@ begin
-- If device 3 has the bus, wait until it releases it.
when State_Dev2Busy =>
if BUSBUSY2n = '0' then
if BUSBUSY2n = '0' or BUSINIT2n = '1' then
MB_STATE <= State_Dev2Busy;
else
MB_STATE <= State_OfferDev3;
@@ -188,7 +192,7 @@ begin
-- If device 4 has the bus, wait until it releases it.
when State_Dev3Busy =>
if BUSBUSY3n = '0' then
if BUSBUSY3n = '0' or BUSINIT3n = '1' then
MB_STATE <= State_Dev3Busy;
else
MB_STATE <= State_OfferDev0;

View File

@@ -54,7 +54,7 @@ package clkgen_pkg is
-- Clock bus, various clocks on a single bus construct.
--
subtype CLKBUS_WIDTH is integer range 5 downto 0;
subtype CLKBUS_WIDTH is integer range 6 downto 0;
-- Indexes to the various clocks on the bus.
--
@@ -64,6 +64,7 @@ package clkgen_pkg is
constant CKRTC : integer := 3; -- RTC clock.
constant CKENCPU : integer := 4; -- CPU clock enable.
constant CKENPERIPH : integer := 5; -- Peripheral clock enable.
constant CKENFDC : integer := 6; -- Floppy Disk Controller clock.
end clkgen_pkg;
library ieee;
@@ -113,12 +114,14 @@ signal CK14M1875i : std_logic; -- 14MH
signal CK8M8672i : std_logic; -- 8.8MHz
signal CK8Mi : std_logic; -- 8MHz
signal CK7M709i : std_logic; -- 7MHz
signal CK7M709_LAST : std_logic; -- 7MHz edge detection.
signal CK4Mi : std_logic; -- 4MHz
signal CK3M546875i : std_logic; -- 3.5MHz
signal CK2Mi : std_logic; -- 2MHz
signal CK2M_LAST : std_logic; -- 2MHz edge detection.
signal CK1Mi : std_logic; -- 1MHz
signal CK895Ki : std_logic; -- 895KHz Sound frequency.
signal CK1M1i : std_logic; -- 1.1MHz Sound frequency MZ-800
signal CK895Ki : std_logic; -- 895KHz Sound frequency MZ-700.
signal CK31500i : std_logic; -- Clock base frequency,
signal CK31250i : std_logic; -- Clock base frequency.
signal CK15611i : std_logic; -- Clock base frequency.
@@ -129,11 +132,13 @@ signal CKRTCi : std_logic; -- RTC
--
signal CKENCPUi : std_logic;
signal CKENPERi : std_logic;
signal CKENFDCi : std_logic;
--
-- Clock edge detection for creating clock enables.
--
signal CPUEDGE : std_logic_vector(1 downto 0);
signal PEREDGE : std_logic_vector(1 downto 0);
signal FDCEDGE : std_logic_vector(1 downto 0);
begin
@@ -321,11 +326,12 @@ begin
--
-- Clock Generator - Basic divide circuit for lower end frequencies.
--
process (PLLLOCKED1, CK128Mi, CK2Mi)
process (PLLLOCKED1, CK128Mi, CK7M709i, CK2Mi)
--
-- Divide by counters to create the various Clock enable signals.
--
variable counter1Mi : unsigned(0 downto 0); -- Binary divider to create 1Mi clock.
variable counter1M1i : unsigned(2 downto 0); -- Binary divider to create 1Mi clock.
variable counter895Ki : unsigned(0 downto 0); -- Binary divider to create 895Ki clock.
variable counter31500i : unsigned(5 downto 0); -- Binary divider to create 31500i clock.
variable counter31250i : unsigned(5 downto 0); -- Binary divider to create 31250i clock.
@@ -334,11 +340,13 @@ begin
begin
if PLLLOCKED1 = '0' then
counter1Mi := (others => '0');
counter1M1i := (others => '0');
counter895Ki := (others => '0');
counter31500i := (others => '0');
counter31250i := (others => '0');
counter15611i := (others => '0');
CK1Mi <= '0';
CK1M1i <= '0';
CK895Ki <= '0';
CK31500i <= '0';
CK31250i <= '0';
@@ -350,9 +358,23 @@ begin
--elsif rising_edge(CK2Mi) then
elsif rising_edge(CK128Mi) then
CK2M_LAST <= CK2Mi;
CK7M709_LAST <= CK7M709i;
if CK7M709i = '1' and CK7M709_LAST = '0' then
-- 1100000Hz
if counter1M1i = 3 or counter1M1i = 6 then
CK1M1i <= not CK1M1i;
if counter1M1i = 1 then
counter1M1i:= (others => '0');
else
counter1M1i:= counter1M1i + 1;
end if;
else
counter1M1i := counter1M1i + 1;
end if;
end if;
if CK2Mi = '1' and CK2M_LAST = '0' then
-- 1000000Hz
if counter1Mi = 0 or counter1Mi = 1 then
CK1Mi <= not CK1Mi;
@@ -435,8 +457,10 @@ begin
if PLLLOCKED2 = '0' then
CKENCPUi <= '0';
CKENPERi <= '0';
CKENFDCi <= '0';
CPUEDGE <= "00";
PEREDGE <= "00";
FDCEDGE <= "00";
elsif rising_edge(CKSYS) then
@@ -454,7 +478,10 @@ begin
when "01" =>
CKSOUNDi <= CK895Ki;
when "00" | "10" | "11" =>
when "10" =>
CKSOUNDi <= CK1M1i;
when "00" | "11" =>
CKSOUNDi <= CK2Mi;
end case;
@@ -465,6 +492,14 @@ begin
if PEREDGE = "10" then
CKENPERi <= '1';
end if;
-- Floppy Disk Controller runs at 8MHz so we generate an enable signal based on this clock rate.
FDCEDGE(0) <= FDCEDGE(1);
FDCEDGE(1) <= CK8Mi;
CKENFDCi <= '0';
if FDCEDGE = "10" then
CKENFDCi <= '1';
end if;
-- The CPU speed is configured by the CMT register and CMT state or the CPU register. Select the right
-- frequency and form the clock by flipping on the right flip flag.
@@ -502,22 +537,25 @@ begin
when "00" => -- 2MHz
PEREDGE(1) <= CK2Mi;
when "01" | "10" | "11" => -- 2MHz
when "01" => -- 4MHz
PEREDGE(1) <= CK4Mi;
when "10" | "11" => -- 2MHz
PEREDGE(1) <= CK2Mi;
end case;
end if;
end process;
-- Assign necessary clocks and enables.
--
CKSYS <= CK100Mi; --CK113M5i;
CKSYS <= CK128Mi; --CK100Mi; --CK113M5i;
CLKBUS(CKMASTER) <= CKSYS; -- System wide synchronous logic clock.
CLKBUS(CKHOST) <= CLKHOST; -- Host hardware clock (ie. MZ-700/MZ-2000 host).
CLKBUS(CKSOUND) <= CKSOUNDi; -- Sound base clock, 50/50 duty cycle.
CLKBUS(CKRTC) <= CKRTCi; -- RTC base clock, 50/50 duty cycle.
CLKBUS(CKENCPU) <= CKENCPUi; -- Enable signal for CPU base clock.
CLKBUS(CKENPERIPH) <= CKENPERi; -- Enable signal for Peripheral base clock.
CLKBUS(CKENFDC) <= CKENFDCi; -- Floppy Disk Controller clock enable signal. Required to match correct timing.
PLLLOCKED <= PLLLOCKED1; -- Indicate when the PLL has synchronised and locked.
end RTL;

View File

@@ -93,6 +93,7 @@ architecture RTL of cmt is
signal CMT_STATUS_INTR : std_logic_vector(15 downto 0);
signal CMT_STATUS_LAST : std_logic_vector(15 downto 0);
signal CMT_INTRni : std_logic;
signal CMT_SIGNAL_LAST : std_logic_vector(8 downto 0);
signal CMT_BUS_OUTi : std_logic_vector(CMT_BUS_OUT_WIDTH); -- CMT bus output.
signal BUTTONS_LAST : std_logic_vector(1 downto 0); -- Virtual buttons last sample, used to detect changes.
signal PLAY_READY_SET_CNT : integer range 0 to 32000000 := 0; -- 1 second timer from last cache upload to PLAY_READY being set.
@@ -108,7 +109,7 @@ architecture RTL of cmt is
signal RECORDING : std_logic; -- Signal indicating a Record is underway, Active = 1.
signal RECSEQ : std_logic_vector(2 downto 0); -- Signal, 3 cycles, indicating
signal MOTOR_TOGGLE : std_logic_vector(1 downto 0); -- Signal indicating if the MZ wants to start or toggle the motor.
signal APSS_TIMER_CNT : unsigned(20 downto 0); -- 1 second virtual APSS SEEK time.
signal REEL_TIMER_CNT : unsigned(23 downto 0); -- 4 second virtual REEL Rewind/Forward and APSS SEEK time.
signal WRITEBIT : std_logic; -- Tape data signal sent to the MZ (for playback).
signal READBIT : std_logic; -- Tape data signal eminating from the MZ (for recording).
signal TAPE_MOTOR_ONn : std_logic; -- Virtual motor signal, tape motor running = 0.
@@ -266,13 +267,13 @@ begin
CMT_STATUS_LAST <= (others => '0');
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
if CMT_WAITn = '1' and CLKBUS(CKENCPU) = '1' then
CMT_INTRni <= '1';
-- Store current signal state and compare with previous, any change then raise an interrupt.
CMT_STATUS <= '0' &
CMT_BUS_OUTi(AUTOPLAY) &
CMT_BUS_OUTi(AUTOREW) &
CMT_BUS_OUTi(APSS_AUTOPLAY) &
CMT_BUS_OUTi(APSS_AUTOREW) &
CMT_BUS_OUTi(APSS_STOP) &
CMT_BUS_OUTi(APSS_PLAY) &
CMT_BUS_OUTi(APSS_EJECT) &
@@ -465,16 +466,31 @@ begin
PLAYING <= "000";
RECORDING <= '0';
MOTOR_TOGGLE(0) <= '0';
APSS_TIMER_CNT <= (others => '0');
REEL_TIMER_CNT <= (others => '0');
CMT_BUS_OUTi(APSS_SEEK) <= '0';
CMT_BUS_OUTi(APSS_DIR) <= '0';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '1';
CMT_BUS_OUTi(APSS_AUTOREW) <= '0';
CMT_BUS_OUTi(APSS_AUTOPLAY) <= '0';
CMT_SIGNAL_LAST <= (others => '0');
elsif CLKBUS(CKMASTER)'event and CLKBUS(CKMASTER)='1' then
if CLKBUS(CKENCPU) = '1' then
--if CLKBUS(CKENCPU) = '1' then
if CMT_WAITn = '1' and CLKBUS(CKENCPU) = '1' then
-- Edge detection of activation signals.
CMT_SIGNAL_LAST <= CMT_BUS_IN(pkgs.mctrl_pkg.REEL_MOTOR) &
CMT_BUS_IN(pkgs.mctrl_pkg.AUTOREW) &
CMT_BUS_IN(pkgs.mctrl_pkg.AUTOPLAY) &
CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) &
CMT_BUS_IN(pkgs.mctrl_pkg.PLAY) &
CMT_BUS_IN(pkgs.mctrl_pkg.STOP) &
CMT_BUS_IN(pkgs.mctrl_pkg.CMTREW) &
CMT_BUS_IN(pkgs.mctrl_pkg.CMTFF) &
CMT_BUS_IN(pkgs.mctrl_pkg.SEEK);
-- Store last state so we detect change.
BUTTONS_LAST <= CONFIG(BUTTONS);
@@ -482,6 +498,9 @@ begin
-- Store last state so we can detect a switch to recording or play mode.
PLAYING(1 downto 0) <= PLAYING(2 downto 1);
-- Eject signal only 1 clock wide.
CMT_BUS_OUTi(APSS_EJECT) <= '0';
-- The MZ80K series use a manual cassette deck with motor automation, so we
-- need to simulate buttons and the states therein.
@@ -498,20 +517,27 @@ begin
TAPE_MOTOR_ONn <= '1';
CMT_BUS_OUTi(TAPEREADY) <= '1'; -- Indicates tape ejected.
CMT_BUS_OUTi(WRITEREADY)<= '1'; -- Indicates write mechanism disabled.
when "01" => -- Play
-- Playback mode.
PLAY_BUTTON <= '1';
RECORD_BUTTON <= '0';
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(TAPEREADY) <= '0'; -- Indicates tape loaded, active Low.
CMT_BUS_OUTi(WRITEREADY)<= '1'; -- Indicates write mechanism disabled.
when "10" => -- Record
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '1';
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(TAPEREADY) <= '0'; -- Indicates tape loaded, active Low.
CMT_BUS_OUTi(WRITEREADY)<= '1'; -- Indicates write mechanism enabled.
when "01"|"11" => -- Play/Auto
CMT_BUS_OUTi(WRITEREADY)<= '0'; -- Indicates write mechanism enabled.
when "11" => -- Auto
-- Assume playback mode for Auto unless activity is detected from the MZ,
-- in which case switch to Recording.
PLAY_BUTTON <= '1';
RECORD_BUTTON <= '0';
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(TAPEREADY) <= '0'; -- Indicates tape loaded, active Low.
CMT_BUS_OUTi(WRITEREADY)<= '0'; -- Indicates write mechanism disabled.
CMT_BUS_OUTi(WRITEREADY)<= '0'; -- Indicates write mechanism enabled.
end case;
end if;
@@ -528,7 +554,6 @@ begin
if RCV_SEQ = "11" or RECORDING = '1' then
PLAY_BUTTON <= '0';
RECORD_BUTTON <= '1';
CMT_BUS_OUTi(WRITEREADY) <= '1'; -- Indicates write mechanism disabled.
else
PLAY_BUTTON <= '1';
RECORD_BUTTON <= '0';
@@ -561,17 +586,22 @@ begin
-- Tape is always ready and able to write.
--
CMT_BUS_OUTi(TAPEREADY) <= '0'; -- Indicates tape loaded, active low.
CMT_BUS_OUTi(WRITEREADY) <= '1'; -- Indicates write mechanism disabled.
CMT_BUS_OUTi(WRITEREADY) <= '0'; -- Indicates write mechanism enabled.
-- If seek pulses high, store the direction.
--
if CMT_BUS_IN(pkgs.mctrl_pkg.SEEK) = '1' then
CMT_BUS_OUTi(APSS_DIR) <= CMT_BUS_IN(pkgs.mctrl_pkg.DIRECTION);
end if;
if (CMT_BUS_IN(pkgs.mctrl_pkg.REEL_MOTOR) = '0' and CMT_SIGNAL_LAST(2) = '1') or (CMT_BUS_IN(pkgs.mctrl_pkg.SEEK) = '1' and CMT_SIGNAL_LAST(0) = '0') then
CMT_BUS_OUTi(APSS_SEEK) <= '0';
end if;
-- If Eject goes active, latch and invert it for reading.
--
if CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) = '0' then
-- if CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) = '0' then
if CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) = '0' and CMT_SIGNAL_LAST(5) = '1' then
CMT_BUS_OUTi(APSS_EJECT) <= '1';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
@@ -580,37 +610,38 @@ begin
-- The play motor is started/stopped by the PLAY/STOP signals.
--
if MOTOR_TOGGLE = "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '0' then
--if MOTOR_TOGGLE = "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '0' then
if CMT_BUS_IN(pkgs.mctrl_pkg.PLAY) = '0' and CMT_SIGNAL_LAST(4) = '1' then
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '1';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '0';
end if;
elsif MOTOR_TOGGLE /= "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '1' then
--elsif MOTOR_TOGGLE /= "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '1' then
if (CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '0' and CMT_SIGNAL_LAST(3) = '1') or REEL_TIMER_CNT = X"FFFFFF" then
TAPE_MOTOR_ONn <= '1';
CMT_BUS_OUTi(APSS_STOP) <= '1';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
end if;
-- If REEL_MOTOR pulses high, then engage APSS seek.
--
elsif CMT_BUS_IN(REEL_MOTOR) = '1' then
if CMT_BUS_IN(REEL_MOTOR) = '1' and CMT_SIGNAL_LAST(8) = '0' then
CMT_BUS_OUTi(APSS_SEEK) <= '1';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '0';
APSS_TIMER_CNT <= to_unsigned(1, 21);
REEL_TIMER_CNT <= to_unsigned(1, 24);
end if;
-- If the APSS SEEK action has been started, reset it after 1 second to simulate the real action.
-- If the APSS SEEK action has been started, reset it after 4 seconds to simulate the real action.
--
if APSS_TIMER_CNT > 0 then
APSS_TIMER_CNT <= APSS_TIMER_CNT + 1;
end if;
if APSS_TIMER_CNT = X"FFFFF" then
CMT_BUS_OUTi(APSS_SEEK) <= '0';
if REEL_TIMER_CNT > 0 then
REEL_TIMER_CNT <= REEL_TIMER_CNT + 1;
end if;
-- Update the status as to wether we are playing, recording or idle.
@@ -637,62 +668,84 @@ begin
-- W READY PB4 - WRITEREADY - Write/record ready
-- TAPE END PB3 - ENDOFTAPE - End of tape detected.
--
elsif CONFIG(MZ2000) = '1' then
elsif CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1' then
-- Tape is always ready and able to write.
--
CMT_BUS_OUTi(TAPEREADY) <= '0'; -- L Indicates tape loaded, active low.
CMT_BUS_OUTi(WRITEREADY) <= '1'; -- L Indicates write mechanism disabled.
-- If seek pulses high, store the direction.
--
if CMT_BUS_IN(pkgs.mctrl_pkg.SEEK) = '1' then
CMT_BUS_OUTi(APSS_DIR) <= CMT_BUS_IN(pkgs.mctrl_pkg.DIRECTION);
CMT_BUS_OUTi(WRITEREADY) <= '0'; -- L Indicates write mechanism enabled.
if (CMT_BUS_IN(pkgs.mctrl_pkg.CMTREW) = '1' and CMT_SIGNAL_LAST(2) = '0') or (CMT_BUS_IN(pkgs.mctrl_pkg.CMTFF) = '1' and CMT_SIGNAL_LAST(1) = '0') or (CMT_BUS_IN(pkgs.mctrl_pkg.SEEK) = '1' and CMT_SIGNAL_LAST(0) = '0') then
CMT_BUS_OUTi(APSS_SEEK) <= '0';
end if;
if CMT_BUS_IN(pkgs.mctrl_pkg.AUTOREW) = '0' and CMT_SIGNAL_LAST(7) = '1' then
CMT_BUS_OUTi(APSS_AUTOREW) <= '1';
elsif CMT_BUS_IN(pkgs.mctrl_pkg.AUTOREW) = '1' and CMT_SIGNAL_LAST(7) = '0' then
CMT_BUS_OUTi(APSS_AUTOREW) <= '0';
end if;
if CMT_BUS_IN(pkgs.mctrl_pkg.AUTOPLAY) = '0' and CMT_SIGNAL_LAST(6) = '1' then
CMT_BUS_OUTi(APSS_AUTOPLAY) <= '1';
elsif CMT_BUS_IN(pkgs.mctrl_pkg.AUTOPLAY) = '1' and CMT_SIGNAL_LAST(6) = '0' then
CMT_BUS_OUTi(APSS_AUTOPLAY) <= '0';
end if;
-- If the APSS SEEK action or Rewind/FFwd has been started, count up until the period expires and then issue a STOP action to simulate real action.
--
if REEL_TIMER_CNT > 0 then
REEL_TIMER_CNT <= REEL_TIMER_CNT + 1;
end if;
-- If Eject goes active, latch and invert it for reading.
--
if CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) = '0' then
if CMT_BUS_IN(pkgs.mctrl_pkg.EJECT) = '0' and CMT_SIGNAL_LAST(5) = '1' then
TAPE_MOTOR_ONn <= '1';
CMT_BUS_OUTi(APSS_EJECT) <= '1';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '0';
CMT_BUS_OUTi(APSS_AUTOREW) <= '0';
CMT_BUS_OUTi(APSS_AUTOPLAY) <= '0';
REEL_TIMER_CNT <= (others => '0');
end if;
-- The play motor is started/stopped by the PLAY/STOP signals.
--
if MOTOR_TOGGLE = "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '1' then
TAPE_MOTOR_ONn <= '0';
if CMT_BUS_IN(pkgs.mctrl_pkg.PLAY) = '0' and CMT_SIGNAL_LAST(4) = '1' then
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '1';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '0';
elsif MOTOR_TOGGLE /= "11" and CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '0' then
TAPE_MOTOR_ONn <= '1';
REEL_TIMER_CNT <= (others => '0');
end if;
--
if (CMT_BUS_IN(pkgs.mctrl_pkg.STOP) = '0' and CMT_SIGNAL_LAST(3) = '1') or REEL_TIMER_CNT = X"FFFFFF" then
TAPE_MOTOR_ONn <= '1';
CMT_BUS_OUTi(APSS_STOP) <= '1';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_SEEK) <= '0';
-- If REEL_MOTOR pulses high, then engage APSS seek.
CMT_BUS_OUTi(APSS_AUTOREW) <= '0';
CMT_BUS_OUTi(APSS_AUTOPLAY) <= '0';
REEL_TIMER_CNT <= (others => '0');
end if;
-- Rewind and Fast Forward start a timer and when it expires, a STOP action occurs. If a STOP or other action occurs before the timer has expired
-- it is cancelled. This simulates the tape being rewound or fast forwarded and a secondary action triggered, such as pressing play.
--
elsif CMT_BUS_IN(REEL_MOTOR) = '1' then
CMT_BUS_OUTi(APSS_SEEK) <= '1';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_PLAY) <= '0';
-- APSS Seek is an extension of Rew/FF and in keeping with other models, sets the direction flag and starts a simulated seek forward or backward. This is accomplished by a timer which upon expiry clears the
-- state.
if (CMT_BUS_IN(pkgs.mctrl_pkg.CMTREW) = '0' and CMT_SIGNAL_LAST(2) = '1') or (CMT_BUS_IN(pkgs.mctrl_pkg.CMTFF) = '0' and CMT_SIGNAL_LAST(1) = '1') or (CMT_BUS_IN(pkgs.mctrl_pkg.SEEK) = '0' and CMT_SIGNAL_LAST(0) = '1') then
CMT_BUS_OUTi(APSS_DIR) <= CMT_BUS_IN(pkgs.mctrl_pkg.CMTREW);
TAPE_MOTOR_ONn <= '0';
CMT_BUS_OUTi(APSS_STOP) <= '0';
APSS_TIMER_CNT <= to_unsigned(1, 21);
CMT_BUS_OUTi(APSS_PLAY) <= '0';
CMT_BUS_OUTi(APSS_EJECT) <= '0';
CMT_BUS_OUTi(APSS_SEEK) <= '1';
REEL_TIMER_CNT <= to_unsigned(1, 24);
end if;
-- If the APSS SEEK action has been started, reset it after 1 second to simulate the real action.
--
if APSS_TIMER_CNT > 0 then
APSS_TIMER_CNT <= APSS_TIMER_CNT + 1;
end if;
if APSS_TIMER_CNT = X"FFFFF" then
CMT_BUS_OUTi(APSS_SEEK) <= '0';
end if;
-- Update the status as to wether we are playing, recording or idle.
--
PLAYING(2) <= PLAY_READY and CMT_BUS_OUTi(APSS_PLAY);

View File

@@ -63,6 +63,7 @@ entity cmt_hw is
CMT_DOUT : out std_logic_vector(7 downto 0);
CMT_DIN : in std_logic_vector(7 downto 0);
CMT_INIT_HW : in std_logic;
CMT_INIT_DONEn : out std_logic;
-- Arbiter control signals
CMT_GRANTn : in std_logic;
@@ -351,5 +352,6 @@ begin
CMT_IORQn <= CMT_IORQni;
CMT_M1n <= CMT_M1ni;
CMT_BUSYn <= CMT_BUSYni;
CMT_INIT_DONEn <= CMT_INITi;
end RTL;

View File

@@ -9,6 +9,7 @@ set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) cmt_hw.
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) arbiter.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) iointr.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) snd.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) fdd.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mz80k_hw.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mz80b_hw.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) i8254/i8254_counter.vhd]
@@ -16,6 +17,9 @@ set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) i8254/i
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) i8255/i8255.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z8420/z8420.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z8420/Interrupt.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z8420/z8420_v2.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) z8420/Interrupt_v2.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) wd1793/wd1793.vhd]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) sn76489_audio.vhd]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) sigma_delta_dac.v]
set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) emuMZ_constraints.sdc]

View File

@@ -0,0 +1,356 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: fdd.vhd
-- Created: Jan 2022
-- Author(s): Philip Smart
-- Description: Sharp MZ series Floppy Disk Interface.
-- This module fully emulates the Floppy Disk Controller as used in all the Sharp MZ
-- series computers. The design is based on the Western Digital controller model
-- which is used by the MB8866 and WD1773 controllers found in most interface cards.
-- As the FPGA has limited BRAM resources, cacheing a floppy image is not practical
-- so the I/O processor is used (as it would be anyway to load an entire cached image)
-- and the disk controller only caches 1 sector, with the I/O processor fetching/
-- saving as directed. The advantage being the I/O processor can offer extended
-- services such as EDSK or D88 mapping from SD card to disk controller sector.
--
-- Credits:
-- Copyright: (c) 2018-22 Philip Smart <philip.smart@net2net.org>
--
-- History: Jan 2022 - Initial module.
--
---------------------------------------------------------------------------------------------------------
-- 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 work.clkgen_pkg.all;
use work.mctrl_pkg.all;
entity fdd is
port (
RSTn : in std_logic;
-- Clock signals needed by this module.
CLKBUS : in std_logic_vector(CLKBUS_WIDTH);
-- Different operations modes.
CONFIG : in std_logic_vector(CONFIG_WIDTH);
-- Z80 CPU Interface.
Z80_DI : out std_logic_vector(7 downto 0);
Z80_DO : in std_logic_vector(7 downto 0);
Z80_ADDR : in std_logic_vector(15 downto 0);
Z80_RDn : in std_logic;
Z80_WRn : in std_logic;
Z80_INTn : out std_logic;
-- Module activation signal (allows for different memory address mapping in the controlling module).
-- The signal should include IORQn/MREQn, span 7 bytes.
FDD_CSn : in std_logic;
-- IOP interrupt. This is raised whenever the WD1793 requires servicing, ie. the IOP_SECTOR_REQ line goes active.
IOP_INTn : out std_logic;
-- I/O Processor Interface
IOP_CSn : in std_logic; -- Chip select from the I/O Processor decoding.
IOP_WRn : in std_logic; -- Write Enable.
IOP_RDn : in std_logic; -- Read Enable.
IOP_ADDR : in std_logic_vector(12 downto 0); -- Address bus.
IOP_DOUT : in std_logic_vector(7 downto 0); -- Data into the mctrl unit.
IOP_DIN : out std_logic_vector(7 downto 0) -- Data out from the mctrl unit.
);
end fdd;
architecture RTL of fdd is
type POLARITY_TYPE is array(integer range 0 to 3) of std_logic;
type READONLY_TYPE is array(integer range 0 to 3) of std_logic;
type DISKREADY_TYPE is array(integer range 0 to 3) of std_logic;
type DISK_TYPE is array(integer range 0 to 3) of std_logic_vector(3 downto 0);
-- IO Processor Control signals.
signal IOP_WD1793_CSn : std_logic;
signal IOP_CTRL_CSn : std_logic;
signal IOP_SECTOR_CSn : std_logic;
signal IOP_TRACK_CSn : std_logic;
signal IOP_FDC_CSn : std_logic;
signal IOP_WD1793_DIN : std_logic_vector(7 downto 0);
signal IOP_SECTOR : std_logic_vector(7 downto 0);
signal IOP_TRACK : std_logic_vector(7 downto 0);
signal IOP_WRPROTECT : READONLY_TYPE;
signal IOP_POLARITY : POLARITY_TYPE;
signal IOP_DISKREADY : DISKREADY_TYPE;
signal IOP_TYPE : DISK_TYPE;
signal IOP_DIN_DISKNO : integer range 0 to 3;
signal IOP_READY : std_logic;
signal IOP_SECTOR_REQ : std_logic;
signal IOP_SECTOR_DIR : std_logic;
signal IOP_SERVICE_REQ : std_logic;
signal IOP_SIDE : std_logic;
signal IOP_INTR_CNT : unsigned(7 downto 0);
-- Host side floppy control signals.
signal WD1793_CSn : std_logic;
signal DRIVESEL_CSn : std_logic;
signal SIDESEL_CSn : std_logic;
signal DDENSEL_CSn : std_logic;
signal DISK_DRQ : std_logic;
signal DISK_MOTORON : std_logic;
signal DISK_SELECT : std_logic_vector(2 downto 0);
signal DISK_BUSY : std_logic;
signal DISK_SIDE : std_logic;
signal DISK_DDEN : std_logic;
signal DISK_NO : integer range 0 to 3;
signal WD1793_INT : std_logic;
signal Z80_NOT_DO : std_logic_vector(7 downto 0);
signal Z80_NOT_DI : std_logic_vector(7 downto 0);
signal WD1793_DI : std_logic_vector(7 downto 0);
begin
WD1793: entity work.wd1793
generic map (
MAX_SECTOR_SIZE => 2048 -- Maximum sector size, used for sizing cache and signals.
)
port map (
-- WD177X-00 CPU Interface
CLK => CLKBUS(CKMASTER), -- System or FPGA clock.
CLKEN => CLKBUS(CKENFDC), -- Clock enable running at original 8MHz FDC timing frequency.
MRn => RSTn, -- Async reset
CSn => WD1793_CSn, -- Device enable.
RDn => Z80_RDn, -- I/O read
WRn => Z80_WRn, -- I/O write
ADDR => Z80_ADDR(1 downto 0), -- I/O port addr
DIN => Z80_NOT_DO, -- I/O data in - note: inverted
DOUT => WD1793_DI, -- I/O data out - note: inverted
DRQ => DISK_DRQ, -- DMA request
INTRQ => WD1793_INT, -- Interrupt request for data transfer or service.
BUSY => DISK_BUSY, -- Controller busy with previous command.
RDY => IOP_DISKREADY(DISK_NO), -- Disk loaded and ready.
WPRT => IOP_WRPROTECT(DISK_NO), -- Write protect
POLARITY => IOP_POLARITY(DISK_NO), -- Data in/out polarity, normal = 0, inverse = 1
DDEN => DISK_DDEN, -- Double density enable flag.
SIDE => DISK_SIDE, -- Disk side.
-- Disk parameters, valid and latched prior to a disk command.
DISK_TYPE => IOP_TYPE(DISK_NO), -- Type of disk structure.
-- I/O Processor disk service interface.
IOP_READY => IOP_READY, -- IO Processor ready to service floppy requests.
IOP_SECTOR_REQ => IOP_SECTOR_REQ, -- Request for I/O processor for a sector.
IOP_SECTOR_DIR => IOP_SECTOR_DIR, -- Direction of sector, 0 = sector is written into cache (IOP->WD1793), 1 = sector is read from cache (WD1793->IOP).
IOP_SECTOR_NO => IOP_SECTOR, -- Sector number required.
IOP_TRACK_NO => IOP_TRACK, -- Track number of required sector.
IOP_SIDE => IOP_SIDE, -- Disk side of required sector.
-- I/O Processor interface.
IOP_CSn => IOP_WD1793_CSn, -- Chip select from the I/O Processor decoding.
IOP_WRn => IOP_WRn, -- Write Enable.
IOP_RDn => IOP_RDn, -- Read Enable.
IOP_ADDR => IOP_ADDR(11 downto 0), -- Address bus, width of sector cache + internal register access addresses.
IOP_DOUT => IOP_DOUT, -- Data into the WD1793 unit.
IOP_DIN => IOP_WD1793_DIN -- Data out from the WD1793 unit.
);
-- Sharp used the MB8866 IC which had negated data input/output. The WD177X uses standard data in/out. Thus we need to invert prior to application.
Z80_NOT_DI <= not(WD1793_DI);
Z80_NOT_DO <= not(Z80_DO);
Z80_DI <= Z80_NOT_DI;
IOP_DIN <= IOP_WD1793_DIN when IOP_WD1793_CSn = '0'
else
-- IOP Control register, used to service an interrupt request.
DISK_SELECT & IOP_INTn & IOP_READY & IOP_SIDE & IOP_SERVICE_REQ & IOP_SECTOR_DIR when IOP_CTRL_CSn = '0'
else
-- Sector number register, indicates the current sector being serviced by the WD1793 controller.
IOP_SECTOR when IOP_SECTOR_CSn = '0'
else
-- Track number register, indicates the current track being serviced by the WD1793 controller.
IOP_TRACK when IOP_TRACK_CSn = '0'
else
-- Floppy disk controller (this module) state signals.
'0' & DISK_BUSY & DISK_DRQ & DISK_MOTORON & DISK_DDEN & DISK_SELECT when IOP_FDC_CSn = '0'
else
IOP_TYPE(IOP_DIN_DISKNO) & "00" & IOP_POLARITY(IOP_DIN_DISKNO) & IOP_WRPROTECT(IOP_DIN_DISKNO) when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(3) = '0'
else
std_logic_vector(IOP_INTR_CNT) when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(3 downto 0) = "1000"
else (others => '0');
IOP_DIN_DISKNO <= to_integer(unsigned(IOP_ADDR(1 downto 0)));
-- Sub divide the chip select to finer granularity. The lower 2K accesses the WD1793 sector cache.
IOP_WD1793_CSn <= '0' when IOP_CSn = '0' and IOP_ADDR(12) = '0'
else '1';
IOP_CTRL_CSn <= '0' when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(2 downto 0) = "000"
else '1';
IOP_TRACK_CSn <= '0' when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(2 downto 0) = "001"
else '1';
IOP_SECTOR_CSn <= '0' when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(2 downto 0) = "010"
else '1';
IOP_FDC_CSn <= '0' when IOP_CSn = '0' and IOP_ADDR(12) = '1' and IOP_ADDR(2 downto 0) = "011"
else '1';
-- WD1773 Chip Select.
WD1793_CSn <= '0' when FDD_CSn = '0' and Z80_ADDR(2) = '0'
else '1';
-- 0xDC Drive Select.
DRIVESEL_CSn <= '0' when FDD_CSn = '0' and Z80_ADDR(2 downto 0) = "100"
else '1';
-- 0xDD Head Select (Side).
SIDESEL_CSn <= '0' when FDD_CSn = '0' and Z80_ADDR(2 downto 0) = "101"
else '1';
-- 0xDE Double Density Select.
DDENSEL_CSn <= '0' when FDD_CSn = '0' and Z80_ADDR(2 downto 0) = "110"
else '1';
Z80_INTn <= not WD1793_INT;
-- Process to latch I/O processor data for presentation to the WD1793.
process(CLKBUS, RSTn, CONFIG)
variable IOP_WR_LASTn : std_logic_vector(1 downto 0);
begin
if RSTn = '0' then
IOP_WRPROTECT <= (others => '0');
IOP_POLARITY <= (others => '0');
IOP_TYPE <= (others => (others => '0'));
IOP_READY <= '0';
IOP_WR_LASTn := "11";
elsif rising_edge(CLKBUS(CKMASTER)) then
-- Write on edge detection.
if IOP_CTRL_CSn = '0' and IOP_WR_LASTn = "00" and IOP_WRn = '0' then
IOP_READY <= IOP_DOUT(0);
ADD ERROR FLAGS HERE FOR TRACK AND SECTOR OUT OF BOUNDS
end if;
-- Disk parameters and control signals.
-- Disk type.
IOP_TYPE(0) <= CONFIG(FDDTYPE_0);
IOP_TYPE(1) <= CONFIG(FDDTYPE_1);
IOP_TYPE(2) <= CONFIG(FDDTYPE_2);
IOP_TYPE(3) <= CONFIG(FDDTYPE_3);
-- Disk loaded and ready (0 = No Disk, 1 = Disk loaded).
IOP_DISKREADY(0) <= CONFIG(FDDDISKREADY_0);
IOP_DISKREADY(1) <= CONFIG(FDDDISKREADY_1);
IOP_DISKREADY(2) <= CONFIG(FDDDISKREADY_2);
IOP_DISKREADY(3) <= CONFIG(FDDDISKREADY_3);
-- Polarity (0 = Normal, 1 = Inverted)
IOP_POLARITY(0) <= CONFIG(FDDPOLARITY_0);
IOP_POLARITY(1) <= CONFIG(FDDPOLARITY_1);
IOP_POLARITY(2) <= CONFIG(FDDPOLARITY_2);
IOP_POLARITY(3) <= CONFIG(FDDPOLARITY_3);
-- Monitor the write protect flags.
-- Write protect (1 = write protected, 0 = read/write).
IOP_WRPROTECT(0) <= CONFIG(FDDWRPROTECT_0);
IOP_WRPROTECT(1) <= CONFIG(FDDWRPROTECT_1);
IOP_WRPROTECT(2) <= CONFIG(FDDWRPROTECT_2);
IOP_WRPROTECT(3) <= CONFIG(FDDWRPROTECT_3);
-- Edge record.
IOP_WR_LASTn := IOP_WR_LASTn(0) & IOP_WRn;
end if;
end process;
-- Process to detect a WD1793 service request and raise an interrupt on the I/O processor.
process(CLKBUS, RSTn, CONFIG)
variable IOP_SR_LAST : std_logic;
variable IOP_RD_LAST : std_logic_vector(1 downto 0);
begin
if RSTn = '0' then
IOP_INTn <= '1';
IOP_SERVICE_REQ <= '0';
IOP_SR_LAST := '1';
IOP_RD_LAST := "11";
IOP_INTR_CNT <= (others => '0');
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
-- Interrupt is only 1 clock cycle wide.
IOP_INTn <= '1';
-- If the service request goes active, raise an interrupt.
if IOP_SR_LAST = '0' and IOP_SECTOR_REQ = '1' then
IOP_INTn <= '0';
IOP_INTR_CNT <= IOP_INTR_CNT + 1;
IOP_SERVICE_REQ <= '1';
end if;
-- Service request flag is cleared once the register is read.
if IOP_RD_LAST = "00" and IOP_RDn = '1' then
IOP_SERVICE_REQ <= '0';
end if;
-- Edge detection.
IOP_SR_LAST := IOP_SECTOR_REQ;
IOP_RD_LAST := IOP_RD_LAST(0) & (IOP_RDn or IOP_CTRL_CSn);
end if;
end if;
end process;
-- Process to latch the disk control signals from the host.
process(CLKBUS, RSTn, CONFIG)
variable Z80_WR_LASTn : std_logic_vector(1 downto 0);
begin
if RSTn = '0' then
DISK_SELECT <= "100";
DISK_NO <= 0;
DISK_MOTORON <= '0';
DISK_SIDE <= '0';
DISK_DDEN <= '1';
Z80_WR_LASTn := "00";
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
-- Write edge detect.
if Z80_WR_LASTn = "11" and Z80_WRn = '0' then
-- Drive Select. 2:0 - DS0 = 100, DS1 = 101, DS2 = 110, DS3 = 111
-- Motor Select. 7 - Active low at physical interface, active high in this module.
if DRIVESEL_CSn = '0' then
DISK_SELECT <= Z80_DO(2 downto 0);
DISK_MOTORON <= Z80_DO(7);
end if;
-- Side Select. 0 : 0 = Side 1, 1 = Side 0.
if SIDESEL_CSn = '0' then
DISK_SIDE <= Z80_DO(0);
end if;
-- Double Density Enable. 0 = Double Density Enabled, 1 = Single Density.
if DDENSEL_CSn = '0' then
DISK_DDEN <= Z80_NOT_DO(0);
end if;
end if;
-- Edge record.
Z80_WR_LASTn := Z80_WR_LASTn(0) & Z80_WRn;
-- Ease of use disk number.
DISK_NO <= to_integer(unsigned(DISK_SELECT(1 downto 0)));
end if;
end if;
end process;
end RTL;

View File

@@ -69,6 +69,7 @@ entity iointr is
INTR_DOUT : out std_logic_vector(7 downto 0);
INTR_DIN : in std_logic_vector(7 downto 0);
INTR_INIT_HW : in std_logic;
INTR_INIT_DONEn : out std_logic;
-- Arbiter control signals
INTR_GRANTn : in std_logic;
@@ -104,16 +105,19 @@ architecture RTL of iointr is
-- Interrupt control signals.
signal IR_STATE : IntrStateType;
signal ISR_FIFO : isrArray;
signal ISR : std_logic_vector(7 downto 0);
signal ISR_LAST : std_logic_vector(7 downto 0);
signal ISR_RD_DATA : std_logic_vector(7 downto 0);
signal ISR_DATA : std_logic_vector(7 downto 0);
signal ISR : std_logic_vector( 7 downto 0);
signal ISR_LAST : std_logic_vector( 7 downto 0);
signal ISR_RD_DATA : std_logic_vector( 7 downto 0);
signal ISR_DATA : std_logic_vector( 7 downto 0);
signal ISR_FIFO_WEN : std_logic;
signal ISR_FIFO_RD : natural range 0 to CACHE_SIZE;
signal ISR_FIFO_RD_ADDR : natural range 0 to CACHE_SIZE;
signal ISR_FIFO_WR : natural range 0 to CACHE_SIZE;
signal IOP_RD_LASTn : std_logic_vector(1 downto 0);
signal ISR_FIFO_TIMER : unsigned(15 downto 0);
signal ISR_CNT : unsigned(18 downto 0);
signal ISR_INTR_XMIT_CNT : unsigned( 7 downto 0);
signal ISR_INTR_RCV_CNT : unsigned( 7 downto 0);
-- Mainboard writeback multiplexing signals.
signal INTR_RDni : std_logic;
@@ -122,6 +126,7 @@ architecture RTL of iointr is
signal INTR_IORQni : std_logic;
signal INTR_M1ni : std_logic;
signal INTR_BUSYni : std_logic;
signal INTR_INITi : std_logic;
begin
-- Assign registers to the output data bus to be read by the I/O processor.
@@ -131,6 +136,10 @@ begin
else
std_logic_vector(to_unsigned(ISR_FIFO_RD, 8)) when IOP_ADDR = "0010"
else
std_logic_vector(ISR_INTR_XMIT_CNT) when IOP_ADDR = "0011"
else
std_logic_vector(ISR_INTR_RCV_CNT) when IOP_ADDR = "0100"
else
ISR_DATA when IOP_ADDR(3) = '1'
else (others => '1');
@@ -142,17 +151,15 @@ begin
ISR_FIFO_WEN <= '0';
ISR <= (others => '0');
ISR_LAST <= (others => '0');
ISR_FIFO_TIMER <= (others => '0');
ISR_INTR_RCV_CNT <= (others => '0');
elsif rising_edge(CLKBUS(CKMASTER)) then
-- Detect interrupt edge as trigger.
ISR_LAST <= INTR7n & INTR6n & INTR5n & INTR4n &INTR3n & INTR2n & INTR1n & INTR0n;
ISR <= (others => '0');
ISR_LAST <= INTR7n & INTR6n & INTR5n & INTR4n & INTR3n & INTR2n & INTR1n & INTR0n;
IOP_RD_LASTn <= IOP_RD_LASTn(IOP_RD_LASTn'length-2 downto 0) & IOP_RDn;
-- Write signal to block RAM is only 1 clock wide.
ISR_FIFO_WEN <= '0';
-- Detect the interrupt edge and trigger an interrupt.
if INTR0n = '0' and ISR_LAST(0) = '1' then
ISR(0) <= '1';
@@ -162,7 +169,7 @@ begin
ISR(1) <= '1';
ISR_FIFO_WEN <= '1';
end if;
if INTR2n = '0' and ISR_LAST(2) = '1' then
if INTR2n = '0' and ISR_LAST(2) = '0' then
ISR(2) <= '1';
ISR_FIFO_WEN <= '1';
end if;
@@ -187,11 +194,10 @@ begin
ISR_FIFO_WEN <= '1';
end if;
-- Save any detected interrupts in the FIFO cache. Cache is used as the speed from interrupt to I/O processor being interrupted can be many cycles
-- so potential exists for missing an interrupt.
if ISR_FIFO_WEN = '1' then
ISR_FIFO(ISR_FIFO_WR) <= ISR;
ISR_FIFO_WR <= ISR_FIFO_WR + 1;
-- A timer used to merge interrupt requests. When not zero, any incoming interrupts will be merged if the
-- queue > 0.
if ISR_FIFO_TIMER /= 0 then
ISR_FIFO_TIMER <= ISR_FIFO_TIMER - 1;
end if;
-- Setup the FIFO read address, needs to be stable 1 clock prior to data read.
@@ -206,9 +212,26 @@ begin
if ISR_FIFO_RD /= ISR_FIFO_WR then
ISR_DATA <= ISR_FIFO(ISR_FIFO_RD_ADDR);
ISR_FIFO_RD <= ISR_FIFO_RD+1;
ISR_FIFO_TIMER <= (others => '0');
else
ISR_DATA <= (others => '0');
end if;
-- Save any detected interrupts in the FIFO cache. Cache is used as the speed from interrupt to I/O processor being interrupted can be many cycles
-- so potential exists for missing an interrupt.
elsif ISR_FIFO_WEN = '1' then
-- Merge interrupts where possible as the I/O processor will process them in one interrupt request.
if ISR_FIFO_WR /= ISR_FIFO_RD and ISR_FIFO_TIMER /= 0 then
ISR_FIFO(ISR_FIFO_WR-1) <= ISR_FIFO(ISR_FIFO_WR-1) or ISR;
else
ISR_FIFO(ISR_FIFO_WR) <= ISR;
ISR_FIFO_WR <= ISR_FIFO_WR + 1;
ISR_FIFO_TIMER <= (others => '1');
end if;
ISR_FIFO_WEN <= '0';
ISR <= (others => '0');
ISR_INTR_RCV_CNT <= ISR_INTR_RCV_CNT + 1;
-- I/O processor debug read, view the past cache contents.
elsif IOP_CSn = '0' and IOP_RDn = '0' and IOP_RD_LASTn = "10" and IOP_ADDR(3) = '1' then
ISR_DATA <= ISR_FIFO(ISR_FIFO_RD_ADDR);
@@ -233,6 +256,8 @@ begin
INTR_RDni <= '1';
INTR_M1ni <= '1';
INTR_BUSYni <= '1';
INTR_INITi <= INTR_INIT_HW;
ISR_INTR_XMIT_CNT <= (others => '0');
ISR_CNT <= (others => '0');
elsif rising_edge(CLKBUS(CKHOST)) then
@@ -280,6 +305,7 @@ begin
IR_STATE <= State_Stage2;
when State_Stage2 =>
ISR_INTR_XMIT_CNT <= ISR_INTR_XMIT_CNT + 1;
INTR_BUSYni <= '1';
IR_STATE <= State_Idle;
@@ -296,5 +322,6 @@ begin
INTR_IORQn <= INTR_IORQni;
INTR_M1n <= INTR_M1ni;
INTR_BUSYn <= INTR_BUSYni;
INTR_INIT_DONEn <= INTR_INITi;
end RTL;

View File

@@ -70,6 +70,7 @@ entity keymatrix_mz700 is
KEYB_DOUT : out std_logic_vector(7 downto 0);
KEYB_DIN : in std_logic_vector(7 downto 0);
KEYB_INIT_HW : in std_logic;
KEYB_INIT_DONEn : out std_logic;
KEYB_INTRn : out std_logic;
-- Arbiter control signals
@@ -495,6 +496,23 @@ begin
end if;
end if;
-- Configurable delay, a tick by tick timer and a millisecond timer, halts all actions whilst the timer > 0.
if MB_DELAY_TICKS /= 0 or MB_DELAY_MS /= 0 or MB_DELAY_SEC /= 0 then
if MB_DELAY_TICKS = 0 and MB_DELAY_MS = 0 and MB_DELAY_SEC /= 0 then
MB_DELAY_TICKS <= X"DFC"; -- 1ms with a 3.58MHz clock (the base clock of the underlying hardware).
MB_DELAY_MS <= X"3E7"; -- 999ms.
MB_DELAY_SEC <= MB_DELAY_SEC - 1; -- Decrement second counter.
elsif MB_DELAY_TICKS = 0 and MB_DELAY_MS /= 0 then
MB_DELAY_TICKS <= X"DFC"; -- 1ms with a 3.58MHz clock (the base clock of the underlying hardware).
MB_DELAY_MS <= MB_DELAY_MS - 1;
else
MB_DELAY_TICKS <= MB_DELAY_TICKS - 1;
end if;
end if;
-- Normal run mode we periodically scan the key matrix or on host activity, scan on demand.
-- If a reset has occurred, the underlying hardware needs to be programmed before it can be used.
--
@@ -651,23 +669,6 @@ begin
-- array (matrix) lookup table. The lookup table is then used to map the physical key pressed to the emulation keyboard
-- matrix where the emulation would expect it.
-- Configurable delay, a tick by tick timer and a millisecond timer, halts all actions whilst the timer > 0.
if MB_DELAY_TICKS /= 0 or MB_DELAY_MS /= 0 or MB_DELAY_SEC /= 0 then
if MB_DELAY_TICKS = 0 and MB_DELAY_MS = 0 and MB_DELAY_SEC /= 0 then
MB_DELAY_TICKS <= X"DFC"; -- 1ms with a 3.58MHz clock (the base clock of the underlying hardware).
MB_DELAY_MS <= X"3E7"; -- 999ms.
MB_DELAY_SEC <= MB_DELAY_SEC - 1; -- Decrement second counter.
elsif MB_DELAY_TICKS = 0 and MB_DELAY_MS /= 0 then
MB_DELAY_TICKS <= X"DFC"; -- 1ms with a 3.58MHz clock (the base clock of the underlying hardware).
MB_DELAY_MS <= MB_DELAY_MS - 1;
else
MB_DELAY_TICKS <= MB_DELAY_TICKS - 1;
end if;
end if;
-- If the CMT (hardware unit) is active, only scan the break key row.
--
if CONFIG(CMT_HWMODE) = '1' and CMT_ACTIVE = '1' and MB_STATE = 0 then
@@ -807,7 +808,7 @@ begin
elsif KEYB_INITi = '1' then
-- Ensure we have access to the bus before commencing the FSM.
if KEYB_GRANTn = '0' or MB_STATE /= 0 then
if MB_DELAY_TICKS = 0 and MB_DELAY_MS = 0 and MB_DELAY_SEC = 0 and (KEYB_GRANTn = '0' or MB_STATE /= 0) then
-- Move along the state machine, next state can be overriden if required.
--
@@ -815,14 +816,18 @@ begin
-- FSM.
case MB_STATE is
-- Start up delay allowing reset to settle and host mainboard to initialise.
when 0 =>
MB_DELAY_MS <= X"0FA";
when 1 =>
KEYB_MREQni <= '0'; -- Special signal combination to set CPLD into TZPU Mainboard Access Mode.
KEYB_IORQni <= '0';
KEYB_M1ni <= '1';
KEYB_DOUT <= X"80";
KEYB_BUSYni <= '0';
when 1 =>
when 2 =>
KEYB_MREQni <= '1';
KEYB_IORQni <= '1';
KEYB_M1ni <= '1';
@@ -844,16 +849,16 @@ begin
KEYB_MREQni <= '0';
-- Allow at least 1 cycle for the write pulse.
when 2 =>
when 3 =>
KEYB_WRni <= '0';
-- Terminate write pulse.
when 3 =>
when 4 =>
KEYB_MREQni <= '1';
KEYB_WRni <= '1';
-- After writing a config byte, check if more needed, otherwise exit clearing INIT flag.
when 4 =>
when 5 =>
if KEYB_CFG_BYTE = 4 then
KEYB_INITi <= '0';
KEYB_BUSYni <= '1';
@@ -984,5 +989,6 @@ begin
KEYB_M1n <= KEYB_M1ni;
KEYB_BUSYn <= KEYB_BUSYni;
KEYB_INTRn <= KEYB_INTRni;
KEYB_INIT_DONEn <= KEYB_INITi;
end Behavioral;

View File

@@ -48,7 +48,7 @@ package mctrl_pkg is
-- Config Bus
--
subtype CONFIG_WIDTH is integer range 90 downto 0;
subtype CONFIG_WIDTH is integer range 106 downto 0;
-- Mode signals indicating type of machine we are emulating.
@@ -82,61 +82,85 @@ package mctrl_pkg is
-- Option Roms Enable (some machines by design dont have them, but this emulation allows them to be enabled if needed).
--
subtype USERROM is integer range 31 downto 24; -- User ROM E800 - EFFF enable per machine.
subtype FDCROM is integer range 39 downto 32; -- FDC ROM F000 - FFFF enable per machine.
constant USERROM : integer := 24; -- User ROM E800 - EFFF enable.
constant FDCROM : integer := 25; -- FDC ROM F000 - FFFF enable.
-- Floppy drive/interface options.
constant FDDENABLE : integer := 26; -- Enable the Floppy Disk Controller.
constant FDDINTEN : integer := 27; -- Enable the interrupt request from the FDC controller (non standard).
subtype FDDDISKREADY is integer range 31 downto 28; -- Drive data polarity.
subtype FDDPOLARITY is integer range 35 downto 32; -- Drive data polarity.
subtype FDDWRPROTECT is integer range 39 downto 36; -- Drive write protect/readonly.
constant FDDDISKREADY_0 : integer := 28; -- Drive 0 disk loaded and drive ready.
constant FDDDISKREADY_1 : integer := 29; -- Drive 1 disk loaded and drive ready.
constant FDDDISKREADY_2 : integer := 30; -- Drive 2 disk loaded and drive ready.
constant FDDDISKREADY_3 : integer := 31; -- Drive 3 disk loaded and drive ready.
constant FDDPOLARITY_0 : integer := 32; -- Drive 0 data polarity.
constant FDDPOLARITY_1 : integer := 33; -- Drive 1 data polarity.
constant FDDPOLARITY_2 : integer := 34; -- Drive 2 data polarity.
constant FDDPOLARITY_3 : integer := 35; -- Drive 3 data polarity.
constant FDDWRPROTECT_0 : integer := 36; -- Drive 0 write protect/readonly.
constant FDDWRPROTECT_1 : integer := 37; -- Drive 1 write protect/readonly.
constant FDDWRPROTECT_2 : integer := 38; -- Drive 2 write protect/readonly.
constant FDDWRPROTECT_3 : integer := 39; -- Drive 3 write protect/readonly.
subtype FDDTYPE is integer range 55 downto 40; -- Disk type selection.
subtype FDDTYPE_0 is integer range 43 downto 40; -- Drive 0 disk type setting.
subtype FDDTYPE_1 is integer range 47 downto 44; -- Drive 1 disk type setting.
subtype FDDTYPE_2 is integer range 51 downto 48; -- Drive 2 disk type setting.
subtype FDDTYPE_3 is integer range 55 downto 52; -- Drive 3 disk type setting.
-- RAM options installed.
subtype RAMINSTALLED is integer range 42 downto 40; -- RAM installed in machine.
subtype GRAPHICSOPTION is integer range 50 downto 43; -- GRAM installed in machine.
subtype RAMINSTALLED is integer range 58 downto 56; -- RAM installed in machine.
subtype GRAPHICSOPTION is integer range 66 downto 59; -- GRAM installed in machine.
-- RAM options.
constant OPT_MINRAM : std_logic_vector(2 downto 0) := "000"; -- Minimum RAM installed on machine.
constant OPT_STDRAM : std_logic_vector(2 downto 0) := "001"; -- Standard (considered Normal, but upgraded for early machines) RAM installed on machine.
-- GRAM options.
constant OPT_GRAMI : integer := 43; -- MZ80B GRAMI / MZ2000 GRAM BLUE installed.
constant OPT_GRAMII : integer := 44; -- MZ80B GRAMII / MZ2000 GRAM RED installed.
constant OPT_GRAMIII : integer := 45; -- MZ2000 GRAM GREEN installed.
constant OPT_PCG : integer := 46; -- PCG installed.
constant OPT_MZ1R25 : integer := 47; -- 16K Video RAM installed
constant OPT_GRAMI : integer := 59; -- MZ80B GRAMI / MZ2000 GRAM BLUE installed.
constant OPT_GRAMII : integer := 60; -- MZ80B GRAMII / MZ2000 GRAM RED installed.
constant OPT_GRAMIII : integer := 61; -- MZ2000 GRAM GREEN installed.
constant OPT_PCG : integer := 62; -- PCG installed.
constant OPT_MZ1R25 : integer := 63; -- 16K Video RAM installed
-- Various configurable settings.
--
constant AUDIOSRC : integer := 51; -- Audio source, 0 = sound generator, 1 = tape audio.
subtype AUDIOVOL is integer range 55 downto 52; -- Audio volume, 3:0 = 16 level volume.
subtype AUDIOMIX is integer range 57 downto 56; -- Audio mixer, blend left and right channels.
constant AUDIOHW : integer := 58; -- Audio hardware to use, host or fpga.
subtype TURBO is integer range 61 downto 59; -- 2MHz/4MHz/8MHz/16MHz/32MHz switch (various).
subtype FASTTAPE is integer range 64 downto 62; -- Speed of tape read/write.
subtype BUTTONS is integer range 66 downto 65; -- Various external buttons, such as CMT play/record.
constant PCGRAM : integer := 67; -- PCG ROM(0) or RAM(1) based.
constant VRAMWAIT : integer := 68; -- Insert video wait states on CPU access as per original design.
constant VRAMDISABLE : integer := 69; -- Disable the Video RAM from display output.
constant GRAMDISABLE : integer := 70; -- Disable the graphics RAM from display output.
constant MENUENABLE : integer := 71; -- Enable the OSD menu on display output.
constant STATUSENABLE : integer := 72; -- Enable the OSD menu on display output.
constant BOOT_RESET : integer := 73; -- MZ80B/2000 Boot IPL Reset Enable.
constant CMTASCII_IN : integer := 74; -- Enable CMT conversion of Sharp Ascii <-> Ascii on receipt of data from Sharp.
constant CMTASCII_OUT : integer := 75; -- Enable CMT conversion of Sharp Ascii <-> Ascii on sending data to Sharp.
constant CMT_HWMODE : integer := 76; -- Select the hardware CMT drive (1) or the emulation (0).
subtype MZ800_SWITCH is integer range 80 downto 77; -- MZ800 Hardware selection switch bank.
constant AUDIOSRC : integer := 67; -- Audio source, 0 = sound generator, 1 = tape audio.
subtype AUDIOVOL is integer range 71 downto 68; -- Audio volume, 3:0 = 16 level volume.
subtype AUDIOMIX is integer range 73 downto 72; -- Audio mixer, blend left and right channels.
constant AUDIOHW : integer := 74; -- Audio hardware to use, host or fpga.
subtype TURBO is integer range 77 downto 75; -- 2MHz/4MHz/8MHz/16MHz/32MHz switch (various).
subtype FASTTAPE is integer range 80 downto 81; -- Speed of tape read/write.
subtype BUTTONS is integer range 82 downto 81; -- Various external buttons, such as CMT play/record.
constant PCGRAM : integer := 83; -- PCG ROM(0) or RAM(1) based.
constant VRAMWAIT : integer := 84; -- Insert video wait states on CPU access as per original design.
constant VRAMDISABLE : integer := 85; -- Disable the Video RAM from display output.
constant GRAMDISABLE : integer := 86; -- Disable the graphics RAM from display output.
constant MENUENABLE : integer := 87; -- Enable the OSD menu on display output.
constant STATUSENABLE : integer := 88; -- Enable the OSD menu on display output.
constant BOOT_RESET : integer := 89; -- MZ80B/2000 Boot IPL Reset Enable.
constant CMTASCII_IN : integer := 90; -- Enable CMT conversion of Sharp Ascii <-> Ascii on receipt of data from Sharp.
constant CMTASCII_OUT : integer := 91; -- Enable CMT conversion of Sharp Ascii <-> Ascii on sending data to Sharp.
constant CMT_HWMODE : integer := 92; -- Select the hardware CMT drive (1) or the emulation (0).
subtype MZ800_SWITCH is integer range 96 downto 93; -- MZ800 Hardware selection switch bank.
-- MZ-800 Switches.
constant SWITCH_MZ800 : integer := 77; -- Machine set to MZ800 mode.
constant SWITCH_PRINTER1 : integer := 78; -- MZ/Centronics Printer.
constant SWITCH_PRINTER2 : integer := 79; -- MZ/Centronics Printer.
constant SWITCH_TAPEIN : integer := 80; -- Enable external data recorder input.
constant SWITCH_MZ800 : integer := 93; -- Machine set to MZ800 mode.
constant SWITCH_PRINTER1 : integer := 94; -- MZ/Centronics Printer.
constant SWITCH_PRINTER2 : integer := 95; -- MZ/Centronics Printer.
constant SWITCH_TAPEIN : integer := 96; -- Enable external data recorder input.
-- Derivative settings to program the clock generator.
--
subtype CPUSPEED is integer range 84 downto 81; -- Active CPU Speed.
subtype PERSPEED is integer range 86 downto 85; -- Active Peripheral Speed.
subtype RTCSPEED is integer range 88 downto 87; -- Active RTC Speed.
subtype SNDSPEED is integer range 90 downto 89; -- Active Sound Speed.
subtype CPUSPEED is integer range 100 downto 97; -- Active CPU Speed.
subtype PERSPEED is integer range 102 downto 101; -- Active Peripheral Speed.
subtype RTCSPEED is integer range 104 downto 103; -- Active RTC Speed.
subtype SNDSPEED is integer range 106 downto 105; -- Active Sound Speed.
-- CMT Bus
--
subtype CMT_BUS_OUT_WIDTH is integer range 14 downto 0;
subtype CMT_BUS_OUT_WIDTH is integer range 16 downto 0;
subtype CMT_BUS_IN_WIDTH is integer range 12 downto 0;
-- CMT exported Signals.
@@ -155,7 +179,9 @@ package mctrl_pkg is
constant APSS_EJECT : integer := 11; -- Eject cassette.
constant APSS_PLAY : integer := 12; -- Play cassette.
constant APSS_STOP : integer := 13; -- Stop playing/rwd/ff of cassette.
constant ENDOFTAPE : integer := 14; -- End of tape detected.
constant APSS_AUTOREW : integer := 14; -- Deck set to auto rewind at tape end.
constant APSS_AUTOPLAY : integer := 15; -- Deck set to auto play after APSS action.
constant ENDOFTAPE : integer := 16; -- End of tape detected.
-- CMT imported Signals.
--
@@ -220,28 +246,29 @@ signal REGISTER_AUDIO : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_CMT : std_logic_vector(7 downto 0) := "00011000";
signal REGISTER_CMT2 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_CMT3 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_USERROM : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_FDCROM : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_FDD : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_FDD2 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_FDD3 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_FDD4 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_ROMS : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_SWITCHES : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_12 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_13 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_14 : std_logic_vector(7 downto 0) := "00000000";
signal REGISTER_CTRL : std_logic_vector(7 downto 0) := "00000000";
signal delay : integer range 0 to 63;
signal READ_STATUS : std_logic_vector(15 downto 0);
signal RESET_MACHINEni : std_logic;
signal RESET_MACHINE_TIMER : unsigned(2 downto 0);
signal CMT_BUS_OUT_LAST : std_logic_vector(CMT_BUS_OUT_WIDTH);
signal CONFIG_LAST : std_logic_vector(CONFIG_WIDTH);
begin
-- Synchronise the register update with the configuration signals according to the CPU clock.
--
process (RESETn, CLKBUS(CKMASTER), RESET_MACHINEni)
process (RESETn, CLKBUS, RESET_MACHINEni, IOP_CSn, IOP_WRn)
begin
if RESETn = '0' then
CONFIG(CONFIG_WIDTH) <= "0000000000000000000000000000000000000000000000000001100000000000000000000011001000000001000";
CONFIG(CONFIG_WIDTH) <= "00000000000000000000000000000000000000000000000000000000000000000001100000000000000000000011001000000001000";
elsif CLKBUS(CKMASTER)'event and CLKBUS(CKMASTER)='1' then
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
@@ -453,14 +480,22 @@ begin
-- Setup RTC clock frequency dependent upon model, same with sound and peripheral speed..
-- Setup the peripheral speed.
case REGISTER_MODEL(3 downto 0) is
-- MZ80K MZ80C MZ1200 MZ80A
when "0000" | "0001" | "0010" | "0011" =>
CONFIG(RTCSPEED) <= "00";
CONFIG(SNDSPEED) <= "00";
CONFIG(PERSPEED) <= "00";
when "0100" | "0101" | "0110" =>
-- MZ700 MZ1500
when "0100" | "0110" =>
CONFIG(RTCSPEED) <= "10";
CONFIG(SNDSPEED) <= "00";
CONFIG(SNDSPEED) <= "01";
CONFIG(PERSPEED) <= "00";
-- MZ800
when "0101" =>
CONFIG(RTCSPEED) <= "10";
CONFIG(SNDSPEED) <= "10";
CONFIG(PERSPEED) <= "01";
-- MZ80B MZ2000 MZ2200 MZ2500
when "0111" | "1000" | "1001" | "1010" =>
CONFIG(RTCSPEED) <= "01";
CONFIG(SNDSPEED) <= "01";
@@ -495,8 +530,14 @@ begin
CONFIG(AUDIOVOL) <= REGISTER_AUDIO(4 downto 1);
CONFIG(AUDIOMIX) <= REGISTER_AUDIO(6 downto 5);
CONFIG(AUDIOHW) <= REGISTER_AUDIO(7);
CONFIG(USERROM) <= REGISTER_USERROM;
CONFIG(FDCROM) <= REGISTER_FDCROM;
CONFIG(USERROM) <= REGISTER_ROMS(0);
CONFIG(FDCROM) <= REGISTER_ROMS(1);
CONFIG(FDDENABLE) <= REGISTER_FDD(0);
CONFIG(FDDINTEN) <= REGISTER_FDD(1);
CONFIG(FDDDISKREADY) <= REGISTER_FDD(7 downto 4);
CONFIG(FDDPOLARITY) <= REGISTER_FDD4(6) & REGISTER_FDD4(4) & REGISTER_FDD4(2) & REGISTER_FDD4(0);
CONFIG(FDDWRPROTECT) <= REGISTER_FDD4(7) & REGISTER_FDD4(5) & REGISTER_FDD4(3) & REGISTER_FDD4(1);
CONFIG(FDDTYPE) <= REGISTER_FDD3(7 downto 0) & REGISTER_FDD2(7 downto 0);
CONFIG(MZ800_SWITCH) <= REGISTER_SWITCHES(3 downto 0);
CONFIG(BOOT_RESET) <= REGISTER_CPU(7);
end if;
@@ -521,11 +562,12 @@ begin
REGISTER_CMT <= "00000000";
REGISTER_CMT2 <= "00000000";
REGISTER_CMT3 <= "00000000";
REGISTER_USERROM <= "00000000";
REGISTER_FDCROM <= "00000000";
REGISTER_FDD <= "00000000";
REGISTER_FDD2 <= "00000000";
REGISTER_FDD3 <= "00000000";
REGISTER_FDD4 <= "00000000";
REGISTER_ROMS <= "00000000";
REGISTER_SWITCHES <= "00000000";
REGISTER_12 <= "00000000";
REGISTER_14 <= "00000000";
REGISTER_CTRL <= "00000000";
READ_STATUS <= (others => '0');
RESET_MACHINEni <= '1';
@@ -535,113 +577,116 @@ begin
elsif rising_edge(CLKBUS(CKMASTER)) then
if CLKBUS(CKENCPU) = '1' then
-- Reset apss register if it has been read, ready for next status change.
--
if READ_STATUS(7) = '1' then
REGISTER_CMT2 <= (others => '0');
-- Reset apss register if it has been read, ready for next status change.
--
if READ_STATUS(7) = '1' then
REGISTER_CMT2 <= (others => '0');
end if;
-- CMT Register 2, for bits 0,2,3 & 4, they set an active bit, then upon read it is reset.
--
REGISTER_CMT2(6) <= CMT_BUS_OUT(APSS_AUTOPLAY);
REGISTER_CMT2(5) <= CMT_BUS_OUT(APSS_AUTOREW);
if CMT_BUS_OUT(APSS_STOP) /= CMT_BUS_OUT_LAST(APSS_STOP) and CMT_BUS_OUT(APSS_STOP) = '1' then
REGISTER_CMT2(4) <= CMT_BUS_OUT(APSS_STOP);
end if;
--if CMT_BUS_OUT(APSS_PLAY) /= CMT_BUS_OUT_LAST(APSS_PLAY) and CMT_BUS_OUT(APSS_PLAY) = '1' then
REGISTER_CMT2(3) <= CMT_BUS_OUT(APSS_PLAY);
--end if;
if CMT_BUS_OUT(APSS_EJECT) /= CMT_BUS_OUT_LAST(APSS_EJECT) and CMT_BUS_OUT(APSS_EJECT) = '1' then
REGISTER_CMT2(2) <= '1';
end if;
REGISTER_CMT2(1) <= CMT_BUS_OUT(APSS_DIR);
if CMT_BUS_OUT(APSS_SEEK) /= CMT_BUS_OUT_LAST(APSS_SEEK) and CMT_BUS_OUT(APSS_SEEK) = '1' then
REGISTER_CMT2(0) <= '1';
end if;
CMT_BUS_OUT_LAST <= CMT_BUS_OUT;
READ_STATUS <= (others => '0');
-- For reading of registers, if no specific signal is required, just read back the output latch.
--
-- if IOP_CSn = '0' and IOP_CS_LASTn = "110" and IOP_RDn = '0' then
if IOP_CSn = '0' and IOP_RDn = '0' then
case IOP_ADDR(3 downto 0) is
when "0000" => IOP_DIN <= REGISTER_MODEL; READ_STATUS(0) <= '1';
when "0001" => IOP_DIN <= REGISTER_DISPLAY; READ_STATUS(1) <= '1';
when "0010" => IOP_DIN <= REGISTER_DISPLAY2; READ_STATUS(2) <= '1';
when "0011" => IOP_DIN <= REGISTER_DISPLAY3; READ_STATUS(3) <= '1';
when "0100" => IOP_DIN <= REGISTER_CPU; READ_STATUS(4) <= '1';
when "0101" => IOP_DIN <= REGISTER_AUDIO; READ_STATUS(5) <= '1';
when "0110" => IOP_DIN <= REGISTER_CMT; READ_STATUS(6) <= '1'; -- CMT = Config register, readback configuration value.
when "0111" => IOP_DIN <= REGISTER_CMT2; READ_STATUS(7) <= '1'; -- CMT 2 = APSS status register, read only.
when "1000" => IOP_DIN <= CMT_BUS_OUT(7 downto 0); READ_STATUS(8) <= '1'; -- CMT 3 = CMT status register, read only.
when "1001" => IOP_DIN <= REGISTER_FDD ; READ_STATUS(9) <= '1';
when "1010" => IOP_DIN <= REGISTER_FDD2 ; READ_STATUS(10) <= '1';
when "1011" => IOP_DIN <= REGISTER_FDD3; READ_STATUS(11) <= '1';
when "1100" => IOP_DIN <= REGISTER_FDD4; READ_STATUS(12) <= '1';
when "1101" => IOP_DIN <= REGISTER_ROMS; READ_STATUS(13) <= '1';
when "1110" => IOP_DIN <= REGISTER_SWITCHES; READ_STATUS(14) <= '1';
when "1111" => IOP_DIN <= REGISTER_CTRL; READ_STATUS(15) <= '1'; -- Read = STATUS
end case;
end if;
-- For writing of registers, just assign the input bus to the register.
-- if IOP_CSn = '0' and IOP_CS_LASTn = "100" and IOP_WRn = '0' then
if IOP_CSn = '0' and IOP_WRn = '0' then
case IOP_ADDR(3 downto 0) is
when "0000" =>
-- Assign the model data to the register and preset the default display hardware.
REGISTER_MODEL <= IOP_DOUT(7 downto 0);
case IOP_DOUT(3 downto 0) is
when "0100" | "0101" | "0110" =>
REGISTER_DISPLAY <= REGISTER_DISPLAY(7 downto 3) & "010";
when others =>
REGISTER_DISPLAY <= REGISTER_DISPLAY(7 downto 3) & "000";
end case;
when "0001" =>
REGISTER_DISPLAY <= IOP_DOUT(7 downto 0);
when "0010" =>
-- Check the sanity, certain address ranges are blocked by the underlying machine.
--
if IOP_DOUT(7 downto 4) /= "1111" and IOP_DOUT(7 downto 4) /= "1110" and IOP_DOUT(7 downto 4) /= "1101" then
REGISTER_DISPLAY2 <= IOP_DOUT(7 downto 0);
end if;
when "0011" => REGISTER_DISPLAY3<= IOP_DOUT(7 downto 0);
when "0100" => REGISTER_CPU <= IOP_DOUT(7 downto 0);
when "0101" => REGISTER_AUDIO <= IOP_DOUT(7 downto 0);
when "0110" => REGISTER_CMT <= IOP_DOUT(7 downto 0);
when "0111" => REGISTER_CMT2 <= IOP_DOUT(7 downto 0);
when "1000" => REGISTER_CMT3 <= IOP_DOUT(7 downto 0);
when "1001" => REGISTER_FDD <= IOP_DOUT(7 downto 0);
when "1010" => REGISTER_FDD2 <= IOP_DOUT(7 downto 0);
when "1011" => REGISTER_FDD3 <= IOP_DOUT(7 downto 0);
when "1100" => REGISTER_FDD4 <= IOP_DOUT(7 downto 0);
when "1101" => REGISTER_ROMS <= IOP_DOUT(7 downto 0);
when "1110" => REGISTER_SWITCHES<= IOP_DOUT(7 downto 0);
when "1111" =>
REGISTER_CTRL <= IOP_DOUT(7 downto 1) & '0';
if IOP_DOUT(0) = '1' then
RESET_MACHINEni <= '0';
RESET_MACHINE_TIMER <= (others => '1');
end if;
end case;
end if;
-- Trigger an external reset, hold for several clock cycles to synchronise the reset with the primary reset controller.
--
if RESET_MACHINE_TIMER /= 0 then
RESET_MACHINE_TIMER <= RESET_MACHINE_TIMER - 1;
end if;
-- If reset asserted, on timer expiry clear reset.
if RESET_MACHINEni = '0' and RESET_MACHINE_TIMER = 0 then
RESET_MACHINEni <= '1';
end if;
end if;
-- CMT Register 2, for bits 0,2,3 & 4, they set an active bit, then upon read it is reset.
--
if CMT_BUS_OUT(APSS_STOP) /= CMT_BUS_OUT_LAST(APSS_STOP) and CMT_BUS_OUT(APSS_STOP) = '1' then
REGISTER_CMT2(4) <= CMT_BUS_OUT(APSS_STOP);
end if;
--if CMT_BUS_OUT(APSS_PLAY) /= CMT_BUS_OUT_LAST(APSS_PLAY) and CMT_BUS_OUT(APSS_PLAY) = '1' then
REGISTER_CMT2(3) <= CMT_BUS_OUT(APSS_PLAY);
--end if;
if CMT_BUS_OUT(APSS_EJECT) /= CMT_BUS_OUT_LAST(APSS_EJECT) and CMT_BUS_OUT(APSS_EJECT) = '1' then
REGISTER_CMT2(2) <= '1';
end if;
REGISTER_CMT2(1) <= CMT_BUS_OUT(APSS_DIR);
if CMT_BUS_OUT(APSS_SEEK) /= CMT_BUS_OUT_LAST(APSS_SEEK) and CMT_BUS_OUT(APSS_SEEK) = '1' then
REGISTER_CMT2(0) <= '1';
end if;
CMT_BUS_OUT_LAST <= CMT_BUS_OUT;
READ_STATUS <= (others => '0');
-- For reading of registers, if no specific signal is required, just read back the output latch.
--
-- if IOP_CSn = '0' and IOP_CS_LASTn = "110" and IOP_RDn = '0' then
if IOP_CSn = '0' and IOP_RDn = '0' then
case IOP_ADDR(3 downto 0) is
when "0000" => IOP_DIN <= REGISTER_MODEL; READ_STATUS(0) <= '1';
when "0001" => IOP_DIN <= REGISTER_DISPLAY; READ_STATUS(1) <= '1';
when "0010" => IOP_DIN <= REGISTER_DISPLAY2; READ_STATUS(2) <= '1';
when "0011" => IOP_DIN <= REGISTER_DISPLAY3; READ_STATUS(3) <= '1';
when "0100" => IOP_DIN <= REGISTER_CPU; READ_STATUS(4) <= '1';
when "0101" => IOP_DIN <= REGISTER_AUDIO; READ_STATUS(5) <= '1';
when "0110" => IOP_DIN <= REGISTER_CMT; READ_STATUS(6) <= '1'; -- CMT = Config register, readback configuration value.
when "0111" => IOP_DIN <= REGISTER_CMT2; READ_STATUS(7) <= '1'; -- CMT 2 = APSS status register, read only.
when "1000" => IOP_DIN <= CMT_BUS_OUT(7 downto 0); READ_STATUS(8) <= '1'; -- CMT 3 = CMT status register, read only.
when "1001" => IOP_DIN <= REGISTER_USERROM; READ_STATUS(9) <= '1';
when "1010" => IOP_DIN <= REGISTER_FDCROM; READ_STATUS(10) <= '1';
when "1011" => IOP_DIN <= REGISTER_SWITCHES; READ_STATUS(11) <= '1';
when "1100" => IOP_DIN <= REGISTER_12; READ_STATUS(12) <= '1';
when "1101" => IOP_DIN <= REGISTER_13; READ_STATUS(13) <= '1';
when "1110" => IOP_DIN <= REGISTER_14; READ_STATUS(14) <= '1';
when "1111" => IOP_DIN <= REGISTER_CTRL; READ_STATUS(15) <= '1'; -- Read = STATUS
end case;
end if;
-- For writing of registers, just assign the input bus to the register.
-- if IOP_CSn = '0' and IOP_CS_LASTn = "100" and IOP_WRn = '0' then
if IOP_CSn = '0' and IOP_WRn = '0' then
case IOP_ADDR(3 downto 0) is
when "0000" =>
-- Assign the model data to the register and preset the default display hardware.
REGISTER_MODEL <= IOP_DOUT(7 downto 0);
case IOP_DOUT(3 downto 0) is
when "0100" | "0101" | "0110" =>
REGISTER_DISPLAY <= REGISTER_DISPLAY(7 downto 3) & "010";
when others =>
REGISTER_DISPLAY <= REGISTER_DISPLAY(7 downto 3) & "000";
end case;
when "0001" =>
REGISTER_DISPLAY <= IOP_DOUT(7 downto 0);
when "0010" =>
-- Check the sanity, certain address ranges are blocked by the underlying machine.
--
if IOP_DOUT(7 downto 4) /= "1111" and IOP_DOUT(7 downto 4) /= "1110" and IOP_DOUT(7 downto 4) /= "1101" then
REGISTER_DISPLAY2 <= IOP_DOUT(7 downto 0);
end if;
when "0011" => REGISTER_DISPLAY3<= IOP_DOUT(7 downto 0);
when "0100" => REGISTER_CPU <= IOP_DOUT(7 downto 0);
when "0101" => REGISTER_AUDIO <= IOP_DOUT(7 downto 0);
when "0110" => REGISTER_CMT <= IOP_DOUT(7 downto 0);
when "0111" => REGISTER_CMT2 <= IOP_DOUT(7 downto 0);
when "1000" => REGISTER_CMT3 <= IOP_DOUT(7 downto 0);
when "1001" => REGISTER_USERROM <= IOP_DOUT(7 downto 0);
when "1010" => REGISTER_FDCROM <= IOP_DOUT(7 downto 0);
when "1011" => REGISTER_SWITCHES<= IOP_DOUT(7 downto 0);
when "1100" => REGISTER_12 <= IOP_DOUT(7 downto 0);
when "1101" => REGISTER_13 <= IOP_DOUT(7 downto 0);
when "1110" => REGISTER_14 <= IOP_DOUT(7 downto 0);
when "1111" =>
REGISTER_CTRL <= IOP_DOUT(7 downto 1) & '0';
if IOP_DOUT(0) = '1' then
RESET_MACHINEni <= '0';
RESET_MACHINE_TIMER <= (others => '1');
end if;
end case;
end if;
-- Trigger an external reset, hold for several clock cycles to synchronise the reset with the primary reset controller.
--
if RESET_MACHINE_TIMER /= 0 then
RESET_MACHINE_TIMER <= RESET_MACHINE_TIMER - 1;
end if;
-- If reset asserted, on timer expiry clear reset.
if RESET_MACHINEni = '0' and RESET_MACHINE_TIMER = 0 then
RESET_MACHINEni <= '1';
end if;
end if;
-- Edge detection due to different clock domains.
-- IOP_CS_LASTn := IOP_CS_LASTn(1 downto 0) & IOP_CSn;
end if;

View File

@@ -98,6 +98,7 @@ entity mz80b_hw is
CS_RAMn : out std_logic; -- RAM Select
CS_VRAMn : out std_logic; -- VRAM Select
CS_GRAMn : out std_logic; -- MZ80B/MZ2000/MZ2200 GRAM Option Select
CS_FDDn : out std_logic; -- Floppy disk controller select.
-- CS_IO_GFBn : out std_logic; -- Graphics Framebuffer IO Select range
CS_IO_Gn : out std_logic; -- Graphics Options IO Select range
CS_IO_CTRLn : out std_logic; -- PPI/PIO control register select.
@@ -283,7 +284,42 @@ begin
OUT2 => open
);
-- Z80 PIO used for keyboard, RAM and Video control.
-- -- Z80 PIO used for keyboard, RAM and Video control.
-- --
-- PIO0 : entity work.z8420
-- port map (
-- -- System
-- RSTn => T80_RSTn, -- Only Power On Reset
-- -- Z80 Bus Signals
-- CLK => CLKBUS(CKMASTER),
-- ENA => CLKBUS(CKENCPU),
-- BASEL => T80_A16(1),
-- CDSEL => T80_A16(0),
-- CE => CS_IO_Z80PIOn,
-- RDn => T80_RDn,
-- WRn => T80_WRn,
-- IORQn => T80_IORQn,
-- M1n => T80_M1n and T80_RSTn,
-- DI => T80_DO,
-- DO => PIO_DO,
-- IEI => '1',
-- IEO => open,
-- INTn => Z80PIO_INTn,
-- -- PORT A
-- A_IN => (others => '0'),
-- A_OUT => Z80PIO_PA,
-- A_OUT_EN => (others => '1'),
-- ARDY => open,
-- ASTBn => '0',
-- -- PORT B
-- B_IN => Z80PIO_PB,
-- B_OUT => open,
-- B_OUT_EN => (others => '0'),
-- BRDY => open,
-- BSTBn => '0'
-- );
-- Z80 PIO (version 1) used for keyboard, RAM and Video control.
--
PIO0 : entity work.z8420
port map (
@@ -346,39 +382,39 @@ begin
else '0';
LED_SHIFT_LOCK <= i8255_PA_O(5) when CONFIG(MZ80B) = '1' and i8255_PA_OEn(5) = '0'
else '0';
CMT_BUS_IN(AUTOPLAY) <= i8255_PA_O(6) when CONFIG(MZ2000) = '1' and i8255_PA_OEn(6) = '0'
else '0';
CMT_BUS_IN(AUTOREW) <= i8255_PA_O(5) when CONFIG(MZ2000) = '1' and i8255_PA_OEn(5) = '0'
else '0';
CMT_BUS_IN(AUTOPLAY) <= i8255_PA_O(6) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PA_OEn(6) = '0'
else '1';
CMT_BUS_IN(AUTOREW) <= i8255_PA_O(5) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PA_OEn(5) = '0'
else '1';
CMT_BUS_IN(STOP) <= i8255_PA_O(3) when i8255_PA_OEn(3) = '0'
else '0';
CMT_BUS_IN(PLAY) <= i8255_PA_O(2) when i8255_PA_OEn(2) = '0'
else '0';
CMT_BUS_IN(DIRECTION) <= i8255_PA_O(1) when CONFIG(MZ80B) = '1' and i8255_PA_OEn(1) = '0'
else '0';
CMT_BUS_IN(CMTFF) <= i8255_PA_O(1) when CONFIG(MZ2000) = '1' and i8255_PA_OEn(1) = '0'
else '0';
CMT_BUS_IN(CMTFF) <= i8255_PA_O(1) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PA_OEn(1) = '0'
else '1';
CMT_BUS_IN(REEL_MOTOR) <= i8255_PA_O(0) when CONFIG(MZ80B) = '1' and i8255_PA_OEn(0) = '0'
else '0';
CMT_BUS_IN(CMTREW) <= i8255_PA_O(0) when CONFIG(MZ2000) = '1' and i8255_PA_OEn(0) = '0'
else '0';
CMT_BUS_IN(CMTREW) <= i8255_PA_O(0) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PA_OEn(0) = '0'
else '1';
-- PPI Port B - Input connections.
--
i8255_PB_I(7) <= KEYB_BREAKDETECT;
i8255_PB_I(6) <= CMT_BUS_OUT(WRITEBIT); -- CMT Writing bit, MZ reading.
i8255_PB_I(5) <= CMT_BUS_OUT(TAPEREADY); -- Tape is loaded in deck when L (0).
i8255_PB_I(4) <= CMT_BUS_OUT(WRITEREADY); -- Prohibit Write/Record when L (0).
i8255_PB_I(3) <= CMT_BUS_OUT(ENDOFTAPE) when CONFIG(MZ2000) = '1' -- End of tape detected.
i8255_PB_I(6) <= CMT_BUS_OUT(WRITEBIT); -- CMT Writing bit, MZ reading.
i8255_PB_I(5) <= CMT_BUS_OUT(TAPEREADY); -- Tape is loaded in deck when L (0).
i8255_PB_I(4) <= CMT_BUS_OUT(WRITEREADY); -- Prohibit Write/Record when L (0).
i8255_PB_I(3) <= CMT_BUS_OUT(ENDOFTAPE) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') -- End of tape detected.
else '1';
i8255_PB_I(2 downto 1) <= (others => '1');
i8255_PB_I(0) <= VBLANK;
-- PPI allow readback of output signals.
--
i8255_PC_I(7) <= CMT_BUS_IN(READBIT); -- Data written to cassette.
i8255_PC_I(6) <= CMT_BUS_IN(WRITEENABLE); -- Cassette write enable (record).
i8255_PC_I(7) <= CMT_BUS_IN(READBIT); -- Data written to cassette.
i8255_PC_I(6) <= CMT_BUS_IN(WRITEENABLE); -- Cassette write enable (record).
i8255_PC_I(5) <= CMT_BUS_IN(SEEK) when CONFIG(MZ80B) = '1'
else
CMT_BUS_IN(KINH);
@@ -396,9 +432,9 @@ begin
else '0';
CMT_BUS_IN(SEEK) <= i8255_PC_O(5) when CONFIG(MZ80B) = '1' and i8255_PC_OEn(5) = '0'
else
i8255_PA_O(7) when CONFIG(MZ2000) = '1' and i8255_PA_OEn(7) = '0'
i8255_PA_O(7) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PA_OEn(7) = '0'
else '0';
CMT_BUS_IN(KINH) <= i8255_PC_O(5) when CONFIG(MZ2000) = '1' and i8255_PC_OEn(5) = '0'
CMT_BUS_IN(KINH) <= i8255_PC_O(5) when (CONFIG(MZ2000) = '1' or CONFIG(MZ2200) = '1') and i8255_PC_OEn(5) = '0'
else '0';
CMT_BUS_IN(EJECT) <= i8255_PC_O(4) when i8255_PC_OEn(4) = '0'
else '0';
@@ -537,6 +573,8 @@ begin
else '1';
CS_IO_Gni <= '0' when T80_IORQn = '0' and T80_A16(7 downto 2) = "111101" and T80_WRn = '0' -- IO Range for Graphics registers.
else '1';
CS_FDDn <= '0' when T80_IORQn = '0' and T80_A16(7 downto 3) = "11011" and T80_A16(3 downto 0) /= "1111" -- Floppy Range D8-DE.
else '1';
-- CS_IO_GFBni <= '0' when T80_IORQn = '0' and T80_A16(7 downto 3) = CONFIG(GRAMIOADDR) and T80_WRn = '0' -- IO Range for Graphics framebuffer register controlled by mctrl register.
-- else '1';
-- CS_IO_GRAMENABLEn <= '0' when CS_IO_GFBni = '0' and T80_A16(2 downto 0) = "100" -- IO Addr base+4 sets C000 -> FFFF map to Graphics RAM.
@@ -613,11 +651,17 @@ begin
-- Audio output. Choose between generated sound and CMT pulse audio.
--
AUDIO_L <= (others => SOUND) when CONFIG(AUDIOSRC) = '0' -- Sound Output Left
AUDIO_L <= SOUND&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' and CONFIG(MZ_B) = '1' -- Sound Output Left
else
(others => CMT_BUS_OUT(WRITEBIT));
AUDIO_R <= (others => SOUND) when CONFIG(AUDIOSRC) = '0' -- Sound Output Right
CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT) & X"000"
when CONFIG(AUDIOSRC) = '1' and CONFIG(MZ_B) = '1'
else
(others => CMT_BUS_OUT(READBIT));
(others => '0');
AUDIO_R <= SOUND&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' and CONFIG(MZ_B) = '1' -- Sound Output Right
else
CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT) & X"000"
when CONFIG(AUDIOSRC) = '1' and CONFIG(MZ_B) = '1'
else
(others => '0');
end rtl;

View File

@@ -100,6 +100,7 @@ entity mz80k_hw is
CS_VRAMn : out std_logic; -- VRAM Select
CS_PITn : out std_logic; -- 8253 write to mirror output onto the physical 8253 IC.
CS_MEM_Gn : out std_logic; -- Memory Peripherals Select
CS_FDDn : out std_logic; -- Floppy disk controller select.
CS_IO_Gn : out std_logic; -- I/O Peripherals Select
CS_GRAMn : out std_logic; -- GRAM Select
DATA_AVAILn : out std_logic; -- Data available to be read from module.
@@ -128,7 +129,7 @@ entity mz80k_hw is
IOP_CSn : in std_logic; -- Chip select from the I/O Processor decoding.
IOP_WRn : in std_logic; -- Write Enable.
IOP_RDn : in std_logic; -- Read Enable.
IOP_ADDR : in std_logic_vector(3 downto 0); -- Address bus.
IOP_ADDR : in std_logic_vector(5 downto 0); -- Address bus.
IOP_DOUT : in std_logic_vector(7 downto 0); -- Data into the mctrl unit.
IOP_DIN : out std_logic_vector(7 downto 0) -- Data out from the mctrl unit.
);
@@ -206,6 +207,10 @@ signal CS_RAMni : std_logic;
signal VGATEni : std_logic; -- Video Output Enable
signal T80_IWRn : std_logic;
signal T80_INTni : std_logic;
signal MUX_RDn : std_logic;
signal MUX_WRn : std_logic;
signal MUX_DOUT : std_logic_vector(7 downto 0);
signal MUX_ADDR : std_logic_vector(1 downto 0);
--
-- PPI
--
@@ -216,7 +221,9 @@ signal INTMSK : std_logic; -- EISU
--
signal DOPIT : std_logic_vector(7 downto 0);
signal SOUND_ENABLE : std_logic;
signal PIT_GATE0 : std_logic;
signal SOUND_PULSE_X2 : std_logic;
signal SOUND_PULSE_X1 : std_logic;
signal SOUND : std_logic;
signal INTX : std_logic;
--
@@ -227,6 +234,11 @@ signal Z80PIO_INTn : std_logic;
signal Z80PIO_PA : std_logic_vector(7 downto 0);
signal Z80PIO_PB : std_logic_vector(7 downto 0);
--
-- Joystick
--
signal MZ800_JOYSTB1n : std_logic;
signal MZ800_JOYSTB2n : std_logic;
--
-- SN76489 Programmable Sound Generator.
--
signal PSG_0_OUT : unsigned(13 downto 0); -- PSG mono channel on MZ-800, Left channel on MZ-1500,
@@ -259,6 +271,7 @@ signal SWIN : std_logic_vector(3 downto 0);
-- Debug
--
signal PULSECPU : std_logic;
signal TESTME : unsigned(15 downto 0) := "1100110000000000";
--
-- Components
@@ -353,12 +366,12 @@ begin
RESET => T80_RSTi,
CLK => CLKBUS(CKMASTER),
ENA => CLKBUS(CKENCPU), --'1',
ADDR => T80_ADDR(1 downto 0),
DI => T80_DO,
ADDR => MUX_ADDR,
DI => MUX_DOUT,
DO => DOPPI,
CSn => CS_8255n,
RDn => T80_RDn,
WRn => T80_WRn,
RDn => MUX_RDn,
WRn => MUX_WRn,
PA_I => i8255_PA_O,
PA_O => i8255_PA_O,
@@ -380,14 +393,14 @@ begin
RST => T80_RSTi,
CLK => CLKBUS(CKMASTER),
ENA => CLKBUS(CKENCPU),
A => T80_ADDR(1 downto 0),
DI => T80_DO,
A => MUX_ADDR,
DI => MUX_DOUT,
DO => DOPIT,
CSn => CS_8254n,
WRn => T80_WRn,
RDn => T80_RDn,
WRn => MUX_WRn,
RDn => MUX_RDn,
CLK0 => CLKBUS(CKSOUND),
GATE0 => SOUND_ENABLE,
GATE0 => PIT_GATE0,
OUT0 => SOUND_PULSE_X2,
CLK1 => CLKBUS(CKRTC),
GATE1 => '1',
@@ -400,28 +413,38 @@ begin
-- Z80 PIO on MZ800 used for printer, horizontal blanking interrupt and 8253 interrupt.
-- on MZ1500 used for printer and interrupts from the 8253 timer Channel 0/2.
--
PIO0 : entity work.z8420
PIO0 : entity work.z8420_v2
port map (
-- System
RSTn => T80_RSTn, -- Only Power On Reset
-- Z80 Bus Signals
CLK => CLKBUS(CKMASTER),
ENA => CLKBUS(CKENCPU),
BASEL => T80_ADDR(1),
CDSEL => T80_ADDR(0),
BASEL => MUX_ADDR(0),
CDSEL => not MUX_ADDR(1),
CE => CS_PIOn,
RDn => T80_RDn,
WRn => T80_WRn,
RDn => MUX_RDn,
WRn => MUX_WRn,
IORQn => T80_IORQn,
M1n => T80_M1n and T80_RSTn,
DI => T80_DO,
DI => MUX_DOUT,
DO => DOPIO,
IEI => '1',
IEO => open,
INTn => Z80PIO_INTn,
A => Z80PIO_PA, -- Printer control not currently used, only 8253 and horizontal blanking interrupts used.
B => Z80PIO_PB -- Printer output, not currently used.
-- PORT A
A_IN => Z80PIO_PA, -- Printer control not currently used, only 8253 and horizontal blanking interrupts used.
A_OUT => open,
A_OUT_EN => (others => '0'),
ARDY => open,
ASTBn => '0',
-- PORT B
B_IN => (others => '0'),
B_OUT => Z80PIO_PB, -- Printer output, not currently used.
B_OUT_EN => (others => '1'),
BRDY => open,
BSTBn => '0'
);
-- MZ-800 Mono PSG.
@@ -441,9 +464,9 @@ begin
clk_i => CLKBUS(CKMASTER), -- System clock
en_clk_psg_i => CLKBUS(CKENCPU), -- PSG clock enable
ce_n_i => CS_PSG0n, -- chip enable, active low
wr_n_i => T80_WRn, -- write enable, active low
wr_n_i => MUX_WRn, -- write enable, active low
ready_o => PSG_READY0n, -- low during I/O operations
data_i => T80_DO,
data_i => MUX_DOUT,
ch_a_o => open,
ch_b_o => open,
ch_c_o => open,
@@ -468,9 +491,9 @@ begin
clk_i => CLKBUS(CKMASTER), -- System clock
en_clk_psg_i => CLKBUS(CKENCPU), -- PSG clock enable
ce_n_i => CS_PSG1n, -- chip enable, active low
wr_n_i => T80_WRn, -- write enable, active low
wr_n_i => MUX_WRn, -- write enable, active low
ready_o => PSG_READY1n, -- low during I/O operations
data_i => T80_DO,
data_i => MUX_DOUT,
ch_a_o => open,
ch_b_o => open,
ch_c_o => open,
@@ -480,10 +503,50 @@ begin
);
-- I/O Processor bus.
IOP_DIN <= "01011100";
IOP_DIN <= DOPPI when IOP_ADDR(5 downto 2) = "0000" -- Read from 8255
else
DOPIT when IOP_ADDR(5 downto 2) = "0001" -- Read from 8254
else
DO367 when IOP_ADDR(5 downto 2) = "0010" -- Read from LS367
else
X"00" when IOP_ADDR(5 downto 2) = "0011" -- Read from MZ-800 Joystick port
else
DOPIO when IOP_ADDR(5 downto 2) = "0100" -- Read from MZ-800 Z80 PIO
else
std_logic_vector(TESTME(7 downto 0)) when IOP_ADDR(5 downto 2) = "1110"
else
std_logic_vector(TESTME(15 downto 8)) when IOP_ADDR(5 downto 2) = "1111"
else "01011100";
process( CLKBUS(CKMASTER), T80_RSTn )
begin
if T80_RSTn = '0' then
TESTME <= "0000000010101010";
elsif CLKBUS(CKMASTER)'event and CLKBUS(CKMASTER)='1' then
if Z80PIO_INTn = '0' then
TESTME(7 downto 0) <= unsigned(DOPIO);
TESTME(14 downto 8) <= TESTME(14 downto 8) + 1;
end if;
TESTME(15) <= SOUND_PULSE_X2;
end if;
end process;
-- Parent signals onto local wires.
--
MUX_RDn <= '0' when IOP_RDn = '0' and IOP_CSn = '0'
else
T80_RDn;
MUX_WRn <= '0' when IOP_WRn = '0' and IOP_CSn = '0'
else
T80_WRn;
MUX_DOUT <= IOP_DOUT when IOP_CSn = '0'
else
T80_DO;
MUX_ADDR <= IOP_ADDR(1 downto 0) when IOP_CSn = '0'
else
T80_ADDR(1 downto 0);
T80_BUSRQn <= '1';
T80_NMIn <= '1';
T80_WAITn <= '0' when MODE_MZ800 = '1' and PSG_READY0n = '0'
@@ -529,23 +592,27 @@ begin
else '1';
INTMSK <= i8255_PC_O(2) when i8255_PC_OEn(2) = '0'
else '1';
VGATEni <= i8255_PC_O(0) when i8255_PC_OEn(0) = '0'
VGATEni <= i8255_PC_O(0) when i8255_PC_OEn(0) = '0' and CONFIG(MZ_A) = '1'
else '1';
KEYB_SCAN <= i8255_PA_O(3 downto 0) when i8255_PA_OEn(3 downto 0) /= "1111"
else "0000"; -- Keyboard scan lines out.
KEYB_STALL <= '1'; --i8255_PA_O(4) when i8255_PA_OEn(4) = '0'
--else '0'; -- Keyboard Stall out.
i8255_PB_I <= KEYB_DATA; -- Keyboard scan data in.
MZ800_JOYSTB1n <= i8255_PA_O(4) when i8255_PA_OEn(4) = '0' -- Joystick strobe 1 on MZ-800
else '1';
MZ800_JOYSTB2n <= i8255_PA_O(5) when i8255_PA_OEn(5) = '0' -- Joystick strobe 2 on MZ-800
else '1';
--
-- PIO Signals.
--
Z80PIO_PA(0) <= '1'; -- Printer /RDA
Z80PIO_PA(0) <= '0'; -- Printer /RDA
Z80PIO_PA(1) <= '1'; -- Printer /STA
Z80PIO_PA(2) <= '0'; -- GND
Z80PIO_PA(3) <= '0'; -- GND
Z80PIO_PA(4) <= not SOUND_PULSE_X2; -- 8253 Interrupt.
Z80PIO_PA(5) <= VBLANK when CONFIG(MZ800) = '1' -- Manual indicates this should be the Horizontal Blanking yet schematic shows it connected to the vertical blanking.
Z80PIO_PA(5) <= VBLANK when CONFIG(MZ800) = '1' -- Manual indicates this should be the Horizontal Blanking yet schematic shows it connected to the vertical blanking.
else
INTX when CONFIG(MZ1500) = '1' -- Duplication of the 8253 Timer 2 interrupt but masked/enabled in the PIO rather than the 8255.
else
@@ -561,13 +628,15 @@ begin
else
X"00" when CS_JOYSTKn = '0' -- Read from MZ-800 Joystick port
else
DOPIO when CS_PIOn = '0' -- Read from MZ-800 Z80 PIO
DOPIO when CS_PIOn = '0' or (T80_M1n = '0' and T80_IORQn = '0' and CONFIG(MZ800) = '1') -- Read from MZ-800 Z80 PIO
else
(others=>'1');
-- Indicate when data is available for reading.
--
DATA_AVAILn <= '0' when T80_RDn = '0' and (CS_8255n ='0' or CS_8254n ='0' or CS_LS367n = '0' or CS_PIOn = '0' or CS_JOYSTKn = '0')
DATA_AVAILn <= '0' when MUX_RDn = '0' and (CS_8255n ='0' or CS_8254n ='0' or CS_LS367n = '0' or CS_PIOn = '0' or CS_JOYSTKn = '0')
else
'0' when T80_M1n = '0' and T80_IORQn = '0' and CONFIG(MZ800) = '1'
else '1';
-- The MZ-800 can change the mode between MZ-800 and MZ-700 by writing to the GD LSI. We thus setup the mode signals accordingly.
@@ -639,42 +708,70 @@ begin
CS_8255n <= '0' when CS_IO_DXn = '0' and T80_ADDR(3 downto 2) = "00" and MODE_MZ800 = '1' -- MZ800 8255 PPI
else
'0' when CS_Eni = '0' and T80_ADDR(11 downto 2) = "0000000000" and MODE_MZ800 = '0' -- 8255
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0000" -- External access by I/O processor.
else '1';
CS_8254n <= '0' when CS_IO_DXn = '0' and T80_ADDR(3 downto 2) = "01" and MODE_MZ800 = '1' -- MZ800 8254 PIT
else
'0' when CS_Eni = '0' and T80_ADDR(11 downto 2) = "0000000001" and MODE_MZ800 = '0' -- 8254
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0001" -- External access by I/O processor.
else '1';
CS_PITn <= '0' when CS_8254n = '0' or CS_LS367n = '0' -- 8254/LS367 mirrored externally.
else '1';
CS_LS367n <= '0' when CS_Eni = '0' and T80_ADDR(11 downto 2) = "0000000010" and MODE_MZ800 = '0' -- LS367
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0010" -- External access by I/O processor.
else '1';
CS_ESWPn <= '0' when CONFIG(MZ_A) = '1' and CS_Eni = '0' and T80_RDn = '0' and T80_ADDR(11 downto 5) = "0000000" -- ROM/RAM Swap
else '1';
CS_JOYSTKn <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 1) = "-00" and CONFIG(MZ800) = '1' -- MZ-800 Joystick ports.
CS_JOYSTKn <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 1) = "000" and CONFIG(MZ800) = '1' -- MZ-800 Joystick ports.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0011" -- External access by I/O processor.
else '1';
CS_PIOn <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 2) = "11" and CONFIG(MZ800) = '1' -- MZ-800 Z80 PIO Printer Interface.
else
'0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 2) = "11" and CONFIG(MZ1500) = '1' -- MZ-1500 Z80 PIO Printer Interface.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0100" -- External access by I/O processor.
else '1';
CS_FDDn <= '0' when CS_IO_DXn = '0' and T80_ADDR(3) = '1' and T80_ADDR(3 downto 0) /= "1111" -- Floppy range D8-DE
else '1';
CS_PSG0n <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 0) = "0010" and CONFIG(MZ800) = '1' -- MZ-800 Programmable Sound Generator.
else
'0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 0) = "0010" and CONFIG(MZ1500) = '1' -- MZ-1500 Programmable Sound Generator Left Channel.
else
'0' when CS_IO_EXn = '0' and T80_ADDR(3 downto 0) = "1001" and CONFIG(MZ1500) = '1' -- MZ-1500 Programmable Sound Generator Left Channel - simultaneous write to both channels.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0101" -- External access by I/O processor.
else '1';
CS_PSG1n <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 0) = "0011" and CONFIG(MZ1500) = '1' -- MZ-1500 Programmable Sound Generator Right Channel.
else
'0' when CS_IO_EXn = '0' and T80_ADDR(3 downto 0) = "1001" and CONFIG(MZ1500) = '1' -- MZ-1500 Programmable Sound Generator Right Channel - simultaneous write to both channels.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0110" -- External access by I/O processor.
else '1';
CS_GCRTCn <= '0' when CS_IO_CXn = '0' and T80_ADDR(3 downto 0) = "1111" and CONFIG(MZ800) = '1' -- MZ-800 CRT Control Register select.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "0111" -- External access by I/O processor.
else '1';
CS_GDMDn <= '0' when CS_IO_CXn = '0' and T80_ADDR(3 downto 0) = "1110" and CONFIG(MZ800) = '1' -- MZ-800 CRT Mode Register select.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "1000" -- External access by I/O processor.
else '1';
CS_GRFn <= '0' when CS_IO_CXn = '0' and T80_ADDR(3 downto 0) = "1101" and CONFIG(MZ800) = '1' -- MZ-800 CRT Read Format Register select.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "1001" -- External access by I/O processor.
else '1';
CS_GWFn <= '0' when CS_IO_CXn = '0' and T80_ADDR(3 downto 0) = "1100" and CONFIG(MZ800) = '1' -- MZ-800 CRT Write Format Register select.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "1010" -- External access by I/O processor.
else '1';
CS_GPALLETn <= '0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 0) = "0000" and CONFIG(MZ800) = '1' -- MZ-800 CRT Pallet Register select.
else
'0' when CS_IO_FXn = '0' and T80_ADDR(3 downto 0) = "0001" and CONFIG(MZ1500) = '1' -- MZ-1500 CRT Pallet Register select.
else
'0' when IOP_CSn = '0' and IOP_ADDR(5 downto 2) = "1011" -- External access by I/O processor.
else '1';
@@ -714,14 +811,14 @@ begin
or
( T80_ADDR(15 downto 11) = "11101" -- E800 -> EFFF User ROM memory.
and
(CONFIG(USERROM) and CONFIG(CURRENTMACHINE)) /= "00000000000" -- Active machine has the user rom enabled.
CONFIG(USERROM) = '1' -- Active machine has the user rom enabled.
and
(MZ_HIGH_RAM_ENABLE = '0' or (MZ_HIGH_RAM_ENABLE = '1' and MZ_HIGH_RAM_INHIBIT = '1')) -- High RAM is not enabled.
)
or
( T80_ADDR(15 downto 12) = "1111"
and
(CONFIG(FDCROM) and CONFIG(CURRENTMACHINE)) /= "00000000000" -- Active machine has the fdc rom enabled.
CONFIG(FDCROM) = '1' -- Active machine has the fdc rom enabled.
and
(MZ_HIGH_RAM_ENABLE = '0' or (MZ_HIGH_RAM_ENABLE = '1' and MZ_HIGH_RAM_INHIBIT = '1')) -- F000 -> FFFF FDC ROM memory.
)
@@ -752,8 +849,11 @@ begin
) and T80_MREQn = '0' and CONFIG(MZ800) = '1' and MODE_MZ700 = '1'
else '1';
--
CS_RAMni <= '1' when ( MZ_800_CGROM_ENABLE = '1' and T80_ADDR(15 downto 12)="0001" and T80_MREQn='0' and CONFIG(MZ800) = '1' and GDMD_REG(3) = '1' )
CS_RAMni <= '1' when ( MZ_800_CGROM_ENABLE = '1' and T80_ADDR(15 downto 12)="0001" and T80_MREQn='0' and CONFIG(MZ800) = '1' and GDMD_REG(3) = '1' ) -- Disable RAM when CGROM portion of BIOS ROM paged in.
else
'1' when ( MZ_800_CGRAM_ENABLE = '1' and T80_ADDR(15 downto 12)="1100" and T80_MREQn='0' and CONFIG(MZ800) = '1' and GDMD_REG(3) = '1' ) -- Disable RAM when CGRAM paged in.
else
-- MZ80K..MZ700
'0' when ( ( (T80_ADDR(15 downto 12)="0000")
and
( (CONFIG(MZ_A)='1' and MZ_MEMORY_SWAP='1' and CONFIG(RAMINSTALLED) = OPT_STDRAM) -- 0000 -> 0FFF MZ80A memory swapped.
@@ -799,6 +899,7 @@ begin
)
and T80_MREQn='0' and MODE_MZ800 = '0'
else
-- MZ800
'0' when (
( MZ_800_RAM_ENABLE(0) = '1' and T80_ADDR(15 downto 12)="0000")
or
@@ -828,9 +929,9 @@ begin
or
( MZ_800_RAM_ENABLE(13) = '1' and T80_ADDR(15 downto 12)="1101")
or
( MZ_800_INHIBIT(14) = '0' and MZ_800_RAM_ENABLE(14) = '1' and T80_ADDR(15 downto 12)="1110")
( MZ_800_RAM_ENABLE(14) = '1' and T80_ADDR(15 downto 12)="1110" and MZ_800_INHIBIT(14) = '0')
or
( MZ_800_INHIBIT(15) = '0' and MZ_800_RAM_ENABLE(14) = '1' and T80_ADDR(15 downto 12)="1111")
( MZ_800_RAM_ENABLE(15) = '1' and T80_ADDR(15 downto 12)="1111" and MZ_800_INHIBIT(15) = '0')
) and T80_MREQn='0' and MODE_MZ800 = '1'
else '1';
@@ -895,7 +996,7 @@ begin
-- MZ700 - Latch wether to enable RAM or ROM at 0000->0FFF.
--
process( T80_RSTi, CLKBUS(CKMASTER), T80_WRn, CS_IO_E0n, CS_IO_E2n, CS_IO_E4n ) begin
process( T80_RSTi, CLKBUS(CKMASTER), MUX_WRn, CS_IO_E0n, CS_IO_E2n, CS_IO_E4n ) begin
if(T80_RSTi = '1') then
MZ_LOW_RAM_ENABLE <= '0';
@@ -903,7 +1004,7 @@ begin
if CLKBUS(CKENCPU) = '1' then
if T80_WRn = '0' then
if MUX_WRn = '0' then
if(CS_IO_E0n = '0') then
MZ_LOW_RAM_ENABLE <= '1';
@@ -921,7 +1022,7 @@ begin
-- MZ700 - Latch wether to enable I/O or RAM at D000->FFFF.
--
process( T80_RSTi, CLKBUS(CKMASTER), T80_WRn, CS_IO_E1n, CS_IO_E3n, CS_IO_E4n, MZ_HIGH_RAM_INHIBIT ) begin
process( T80_RSTi, CLKBUS(CKMASTER), MUX_WRn, CS_IO_E1n, CS_IO_E3n, CS_IO_E4n, MZ_HIGH_RAM_INHIBIT ) begin
if(T80_RSTi = '1') then
MZ_HIGH_RAM_ENABLE <= '0';
MZ_INHIBIT_RESET <= '0';
@@ -930,7 +1031,7 @@ begin
if CLKBUS(CKENCPU) = '1' then
if T80_WRn = '0' then
if MUX_WRn = '0' then
if(CS_IO_E1n = '0' and MZ_HIGH_RAM_INHIBIT = '0') then
MZ_HIGH_RAM_ENABLE <= '1';
@@ -951,7 +1052,7 @@ begin
-- MZ700 - Latch wether to inhibit all functionality at D000->FFFF.
--
process( T80_RSTi, CLKBUS(CKMASTER), T80_WRn, CS_IO_E5n, CS_IO_E6n, MZ_INHIBIT_RESET ) begin
process( T80_RSTi, CLKBUS(CKMASTER), MUX_WRn, CS_IO_E5n, CS_IO_E6n, MZ_INHIBIT_RESET ) begin
if(T80_RSTi = '1') then
MZ_HIGH_RAM_INHIBIT <= '0';
@@ -959,7 +1060,7 @@ begin
if CLKBUS(CKENCPU) = '1' then
if T80_WRn = '0' then
if MUX_WRn = '0' then
if(CS_IO_E5n = '0') then
MZ_HIGH_RAM_INHIBIT <= '1';
@@ -976,11 +1077,9 @@ begin
-- a 16 bit map to indicate which is valid, a 1 indicates the 4K block for that device is active.
-- Priority: INHIBIT, ROM, CGROM, VRAM, RAM
--
process( T80_RSTi, CLKBUS, T80_RDn, T80_WRn, CS_IO_E0n, CS_IO_E1n, CS_IO_E2n, CS_IO_E3n, CS_IO_E4n, CS_IO_E5n, CS_IO_E6n, CS_GCRTCn, CS_GDMDn, CS_GRFn, CS_GWFn ) begin
process( T80_RSTi, CLKBUS, MUX_RDn, MUX_WRn, CS_IO_E0n, CS_IO_E1n, CS_IO_E2n, CS_IO_E3n, CS_IO_E4n, CS_IO_E5n, CS_IO_E6n, CS_GCRTCn, CS_GDMDn, CS_GRFn, CS_GWFn ) begin
if(T80_RSTi = '1') then
MZ_800_ROM_ENABLE <= (others => '0');
MZ_800_RAM_ENABLE <= (others => '0');
MZ_800_CGROM_ENABLE <= '0';
MZ_800_VRAM_ENABLE <= (others => '0');
MZ_800_INHIBIT <= (others => '0');
@@ -1017,7 +1116,7 @@ begin
if CLKBUS(CKENCPU) = '1' then
-- Write (OUT) latches.
if T80_WRn = '0' then
if MUX_WRn = '0' then
-- 0000:7FFF to DRAM
if CS_IO_E0n = '0' then
MZ_800_RAM_ENABLE(7 downto 0) <= "11111111";
@@ -1067,7 +1166,7 @@ begin
-- 8000:BFFF to VRAM depending upon option.
MZ_800_VRAM_ENABLE(9 downto 8) <= "11";
MZ_800_RAM_ENABLE(9 downto 8) <= "00";
if GDMD_REG(2) = '1' then
if GDMD_REG(3 downto 2) = "01" then
MZ_800_VRAM_ENABLE(11 downto 10) <= "11";
MZ_800_RAM_ENABLE(11 downto 10) <= "00";
else
@@ -1090,24 +1189,24 @@ begin
-- Graphics Display LSI CRT Control Register.
elsif CS_GCRTCn = '0' then
GCRTC_REG <= T80_DO;
GCRTC_REG <= MUX_DOUT;
-- Graphics Display LSI CRT Mode Register.
elsif CS_GDMDn = '0' then
GDMD_REG <= T80_DO;
GDMD_REG <= MUX_DOUT;
-- Graphics Display LSI Read Format Register.
elsif CS_GRFn = '0' then
GRF_REG <= T80_DO;
GRF_REG <= MUX_DOUT;
-- Graphics Display LSI Write Format Register.
elsif CS_GWFn = '0' then
GWF_REG <= T80_DO;
GWF_REG <= MUX_DOUT;
end if;
end if;
-- Read (IN) latches.
if T80_RDn = '0' then
if MUX_RDn = '0' then
if(CS_IO_E0n = '0') then
-- 1000:1FFF to CGROM
MZ_800_CGROM_ENABLE <= '1';
@@ -1120,7 +1219,7 @@ begin
-- 8000:BFFF to VRAM depending upon option.
MZ_800_VRAM_ENABLE(9 downto 8) <= "11";
MZ_800_RAM_ENABLE(9 downto 8) <= "00";
if GDMD_REG(2) = '1' then
if GDMD_REG(3 downto 2) = "01" then
MZ_800_VRAM_ENABLE(11 downto 10) <= "11";
MZ_800_RAM_ENABLE(11 downto 10) <= "00";
else
@@ -1188,39 +1287,72 @@ begin
--
-- Sound gate control
--
process( CLKBUS(CKMASTER), T80_WRn, CS_LS367n, T80_RSTn ) begin
process( CLKBUS(CKMASTER), MUX_WRn, CS_LS367n, T80_RSTn ) begin
if( T80_RSTn = '0' ) then
SOUND_ENABLE <= '0';
elsif CLKBUS(CKMASTER)'event and CLKBUS(CKMASTER) = '1' then
if CLKBUS(CKENPERIPH) = '1' and T80_WRn = '0' and CS_LS367n = '0' then
SOUND_ENABLE <= T80_DO(0);
if CONFIG(MZ800) = '1' then
if i8255_PC_OEn(0) = '0' then
SOUND_ENABLE <= i8255_PC_O(0);
end if;
if MODE_MZ800 = '1' then
PIT_GATE0 <= '1';
end if;
end if;
-- MZ-80A/MZ-700 sound control. MZ-800 in MZ-700 mode also has values updated.
if CLKBUS(CKENPERIPH) = '1' and MUX_WRn = '0' and CS_LS367n = '0' and (CONFIG(MZ_A) = '1' or MODE_MZ700 = '1') then
if CONFIG(MZ800) = '0' then
SOUND_ENABLE <= MUX_DOUT(0);
end if;
PIT_GATE0 <= MUX_DOUT(0);
end if;
end if;
end process;
-- On MZ80K..700 the 8253 Gate 0 control enables/disables the sound. On the MZ800 in 700 mode this is the same, in 800 mode
-- the timer is used as an interrupt event timer and the gate needs to be permanently enabled.
--
--PIT_GATE0 <= '1' when MODE_MZ800 = '1'
-- else
-- SOUND_ENABLE;
-- Audio output. Choose between generated sound and CMT pulse audio.
--
AUDIO_L <= PSG_0_OUT & "00" when (MODE_MZ800 = '1' or CONFIG(MZ1500) = '1') and CONFIG(AUDIOSRC) = '0' -- Left channel is the PSG output in MZ800/MZ1500 mode.
else
SOUND&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' -- Timer sound Output Left
'0'&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' and SOUND_ENABLE = '1' and CONFIG(MZ_K) = '1' -- Timer sound Output Left
else
(others => CMT_BUS_OUT(WRITEBIT));
CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT) & X"000"
when CONFIG(AUDIOSRC) = '1' and CONFIG(MZ_K) = '1'
else
(others => '0');
AUDIO_R <= PSG_1_OUT & "00" when CONFIG(MZ1500) = '1' and CONFIG(AUDIOSRC) = '0' -- Right channel PSG in MZ1500 mode.
else
SOUND&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' -- Timer sound Output Right
'0'&SOUND&SOUND&SOUND & X"000" when CONFIG(AUDIOSRC) = '0' and SOUND_ENABLE = '1' and CONFIG(MZ_K) = '1' -- Timer sound Output Right
else
(others => CMT_BUS_OUT(READBIT));
CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT)&CMT_BUS_OUT(WRITEBIT) & X"000"
when CONFIG(AUDIOSRC) = '1' and CONFIG(MZ_K) = '1'
else
(others => '0');
-- The signal coming out of the 8254 is not a square wave and twice the frequency. The addition of a flip-flop to divide the
-- frequency by 2 results in a square wave of the correct audio frequency.
-- The signal coming out of the 8254 on an MZ-80A is not a square wave and twice the frequency. The addition of a flip-flop to divide the
-- frequency by 2 results in a square wave of the correct audio frequency. Other models output a frequency wave.
process( SOUND_PULSE_X2 ) begin
if( SOUND_PULSE_X2'event and SOUND_PULSE_X2 = '1' ) then
SOUND <= not SOUND;
SOUND_PULSE_X1 <= not SOUND_PULSE_X1;
end if;
if CONFIG(MZ_A) = '1' then
SOUND <= SOUND_PULSE_X1;
else
SOUND <= SOUND_PULSE_X2;
end if;
end process;
-- MZ80 BLNK signal, enabled by VGATE being active and HBLANK pulsing. If HBLANK stops pulsing for more
-- than 32ms, then BLNK goes inactive.
--
@@ -1229,7 +1361,7 @@ begin
variable HBLANKLAST : std_logic;
begin
if T80_RSTn = '0' then
BLNKn <= '1';
BLNKn <= '1';
TCOUNT := (others=>'0');
elsif CLKBUS(CKMASTER)'event and CLKBUS(CKMASTER)='1' then

View File

@@ -0,0 +1,908 @@
--
-- SN76489 Complex Sound Generator
-- Matthew Hagerty
-- July 2020
-- https://dnotq.io
--
-- Released under the 3-Clause BSD License:
--
-- Copyright 2020 Matthew Hagerty (matthew <at> dnotq <dot> io)
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- 3. Neither the name of the copyright holder nor the names of its
-- contributors may be used to endorse or promote products derived from this
-- software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- A huge amount of effort has gone into making this core as accurate as
-- possible to the real IC, while at the same time making it usable in all
-- digital SoC designs, i.e. retro-computer and game systems, etc. Design
-- elements from the real IC were used and implemented when possible, with any
-- work-around or changes noted along with the reasons.
--
-- Synthesized and FPGA proven:
--
-- * Xilinx Spartan-6 LX16, SoC 21.477MHz system clock, 3.58MHz clock-enable.
--
--
-- References:
--
-- * The SN76489 datasheet
-- * Insight gained from the AY-3-8910/YM-2149 die-shot and reverse-engineered
-- schematics (similar audio chips from the same era).
-- * Real hardware (SN76489 in a ColecoVision game console).
-- * Chip quirks, use, and abuse details from friends and retro enthusiasts.
--
--
-- Generates:
--
-- * Unsigned 12-bit output for each channel.
-- * Unsigned 14-bit summation of the four channels.
-- * Signed 14-bit PCM summation of the four channels, with each channel
-- converted to -/+ zero-centered level or -/+ full-range level.
--
-- The tone counters are period-limited to prevent the very high frequency
-- outputs that the original IC is capable of producing. Frequencies above
-- 20KHz cause problems in all-digital systems with sampling rates around
-- 44.1KHz to 48KHz. The primary use of these high frequencies was as a
-- carrier for amplitude modulated (AM) audio. The high frequency would be
-- filtered out by external electronics, leaving only the low frequency audio.
--
-- When the tone counters are limited, the output square-wave is disabled, but
-- the amplitude can still be changed, which allows the A.M. technique to still
-- work in a digital Soc.
--
-- I/O requires at least two clock-enable cycles. This could be modified to
-- operate faster, i.e. based on the input-clock directly. All inputs are
-- registered at the system-clock rate.
--
-- Optionally simulates the original 32-clock (clock-enable) I/O cycle.
--
-- The SN76489 does not have an external reset and the original IC "wakes up"
-- generating a tone. This implementation sets the default output level to
-- full attenuation (silent output). If the original functionality is desired,
-- modify the channel period and level register initial values.
--
-- Basic I/O interface use:
--
-- Set-up data on data_i.
-- Set ce_n_i and wr_n_i low.
-- Observe ready_o and wait for it to become high.
-- Set wr_n_i high, if done writing to the chip set ce_n_i high.
--
-- Version history:
--
-- July 21 2020
-- V1.0. Release. SoC tested.
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity sn76489_audio is
generic -- 0 = normal I/O, 32 clocks per write
-- 1 = fast I/O, around 2 clocks per write
( FAST_IO_G : std_logic := '0'
-- Minimum allowable period count (see comments further
-- down for more information), recommended:
-- 6 18643.46Hz First audible count.
-- 17 6580.04Hz Counts at 16 are known to be used for
-- amplitude-modulation.
; MIN_PERIOD_CNT_G : integer := 6
);
port
( clk_i : in std_logic -- System clock
; en_clk_psg_i : in std_logic -- PSG clock enable
; ce_n_i : in std_logic -- chip enable, active low
; wr_n_i : in std_logic -- write enable, active low
; ready_o : out std_logic -- low during I/O operations
; data_i : in std_logic_vector(7 downto 0)
; ch_a_o : out unsigned(11 downto 0)
; ch_b_o : out unsigned(11 downto 0)
; ch_c_o : out unsigned(11 downto 0)
; noise_o : out unsigned(11 downto 0)
; mix_audio_o : out unsigned(13 downto 0)
; pcm14s_o : out unsigned(13 downto 0)
);
end sn76489_audio;
architecture rtl of sn76489_audio is
-- Registered I/O.
signal ce_n_r : std_logic := '1';
signal wr_n_r : std_logic := '1';
signal din_r : unsigned(7 downto 0) := x"00";
signal ready_r : std_logic := '1';
signal ready_x : std_logic;
-- I/O FSM.
type io_state_t is (IO_IDLE, IO_OP, IO_WAIT);
signal io_state_r : io_state_t := IO_IDLE;
signal io_state_x : io_state_t;
signal io_cnt_r : unsigned(4 downto 0) := (others => '0');
signal io_cnt_x : unsigned(4 downto 0);
signal en_reg_wr_s : std_logic;
-- Register file.
signal reg_addr_r : unsigned(2 downto 0) := (others => '0');
signal reg_sel_s : unsigned(2 downto 0);
signal ch_a_period_r : unsigned(9 downto 0) := (others => '0');
signal ch_a_period_x : unsigned(9 downto 0);
signal ch_a_level_r : unsigned(11 downto 0) := (others => '0');
signal ch_a_level_x : unsigned(11 downto 0);
signal ch_b_period_r : unsigned(9 downto 0) := (others => '0');
signal ch_b_period_x : unsigned(9 downto 0);
signal ch_b_level_r : unsigned(11 downto 0) := (others => '0');
signal ch_b_level_x : unsigned(11 downto 0);
signal ch_c_period_r : unsigned(9 downto 0) := (others => '0');
signal ch_c_period_x : unsigned(9 downto 0);
signal ch_c_level_r : unsigned(11 downto 0) := (others => '0');
signal ch_c_level_x : unsigned(11 downto 0);
signal noise_ctrl_r : std_logic := '0';
signal noise_ctrl_x : std_logic;
signal noise_shift_r : unsigned(1 downto 0) := (others => '0');
signal noise_shift_x : unsigned(1 downto 0);
signal noise_level_r : unsigned(11 downto 0) := (others => '0');
signal noise_level_x : unsigned(11 downto 0);
signal noise_rst_r : std_logic := '0';
signal noise_rst_x : std_logic;
-- Clock conditioning counters and enables.
signal clk_div16_r : unsigned(3 downto 0) := (others => '0');
signal clk_div16_x : unsigned(3 downto 0);
signal en_cnt_r : std_logic := '0';
signal en_cnt_x : std_logic;
-- Channel tone counters.
signal ch_a_cnt_r : unsigned(9 downto 0) := (others => '0');
signal ch_a_cnt_x : unsigned(9 downto 0);
signal flatline_a_s : std_logic;
signal tone_a_r : std_logic := '1';
signal tone_a_x : std_logic;
signal ch_b_cnt_r : unsigned(9 downto 0) := (others => '0');
signal ch_b_cnt_x : unsigned(9 downto 0);
signal flatline_b_s : std_logic;
signal tone_b_r : std_logic := '1';
signal tone_b_x : std_logic;
signal ch_c_cnt_r : unsigned(9 downto 0) := (others => '0');
signal ch_c_cnt_x : unsigned(9 downto 0);
signal flatline_c_s : std_logic;
signal tone_c_r : std_logic := '1';
signal tone_c_x : std_logic;
signal c_ff_r : std_logic := '1';
signal c_ff_x : std_logic;
-- Noise counter.
signal noise_cnt_r : unsigned(6 downto 0) := (others => '0');
signal noise_cnt_x : unsigned(6 downto 0);
signal noise_ff_r : std_logic := '1';
signal noise_ff_x : std_logic;
-- 15-bit Noise LFSR.
signal noise_lfsr_r : std_logic_vector(14 downto 0) := b"100_0000_0000_0000";
signal noise_lfsr_x : std_logic_vector(14 downto 0);
signal noise_fb_s : std_logic;
signal noise_s : std_logic;
-- Amplitude / Attenuation control.
signal level_a_s : unsigned(11 downto 0);
signal level_b_s : unsigned(11 downto 0);
signal level_c_s : unsigned(11 downto 0);
signal level_n_s : unsigned(11 downto 0);
-- DAC.
signal dac_a_r : unsigned(11 downto 0) := (others => '0');
signal dac_b_r : unsigned(11 downto 0) := (others => '0');
signal dac_c_r : unsigned(11 downto 0) := (others => '0');
signal dac_n_r : unsigned(11 downto 0) := (others => '0');
signal sum_audio_r : unsigned(13 downto 0) := (others => '0');
-- Digital to Analogue Output-level lookup table ROM.
--
-- The 4-bit level from the channel register is used as an index into a
-- calculated table of values that represent the equivalent voltage.
--
-- The output scale is amplitude logarithmic.
--
-- ln10 = Natural logarithm of 10, ~= 2.302585
-- amp = Amplitude in voltage, 0.2, 1.45, etc.
-- dB = decibel value in dB, -1.5, -3, etc.
--
-- dB = 20 * log(amp) / ln10
-- amp = 10 ^ (dB / 20)
--
-- -1.5dB = 0.8413951416
-- -2.0dB = 0.7943282347
-- -3.0dB = 0.7079457843
--
-- The datasheet defines 16 attenuation steps that are -2.0dB apart.
--
-- 1V reference values based on sixteen 2.0dB steps:
--
-- 1.0000, 0.7943, 0.6310, 0.5012, 0.3981, 0.3162, 0.2512, 0.1995,
-- 0.1585, 0.1259, 0.1000, 0.0794, 0.0631, 0.0501, 0.0398, 0.0316
--
-- A 7-bit number (0..128) can support a scaled version of the reference
-- list without having any duplicate values, but the difference is small at
-- the bottom-end. Duplicate values means several volume levels produce the
-- same output level, and is not accurate.
--
-- Using using a 12-bit output value means the four channels can be summed
-- into a 14-bit value without overflow, and leaves room for adjustments if
-- converting to something like 16-bit PCM. The 12-bit values also provide
-- a nicer curve, and are easier to initialize in VHDL.
--
-- The lowest volume level needs to go to 0 in a digital SoC to prevent
-- noise that would be filtered in a real system with external electronics.
signal dac_level_s : unsigned(11 downto 0);
type dacrom_type is array (0 to 15) of unsigned(11 downto 0);
signal dacrom_ar : dacrom_type := (
-- 4095,3253,2584,2052,1630,1295,1029, 817,
-- 649, 516, 409, 325, 258, 205, 163, 129 (forced to 0)
x"FFF",x"CB5",x"A18",x"804",x"65E",x"50F",x"405",x"331",
x"289",x"204",x"199",x"145",x"102",x"0CD",x"0A3",x"000");
-- PCM signed 14-bit.
signal sign_a_r : unsigned(11 downto 0) := (others => '0');
signal sign_a_x : unsigned(11 downto 0);
signal sign_b_r : unsigned(11 downto 0) := (others => '0');
signal sign_b_x : unsigned(11 downto 0);
signal sign_c_r : unsigned(11 downto 0) := (others => '0');
signal sign_c_x : unsigned(11 downto 0);
signal sign_n_r : unsigned(11 downto 0) := (others => '0');
signal sign_n_x : unsigned(11 downto 0);
signal pcm14s_r : unsigned(13 downto 0) := (others => '0');
begin
-- Register the input data at the full clock rate.
process ( clk_i ) begin
if rising_edge(clk_i) then
ce_n_r <= ce_n_i;
wr_n_r <= wr_n_i;
din_r <= unsigned(data_i);
-- Ready is very fast so the external CPU will see the signal
-- in time to extend the I/O operation.
ready_r <= ready_x;
end if;
end process;
-- Registered output.
ready_o <= ready_r;
-- -----------------------------------------------------------------------
--
-- External bus cycle FSM.
--
-- The SN76489 can only be written (registers cannot be read back), and
-- only when the chip is selected.
--
-- A write operation takes 32 clock cycles. A fast-IO mode is available
-- if legacy timing is not important.
bus_io : process
( ce_n_r, wr_n_r
, io_state_r, io_cnt_r, ready_r
) begin
io_state_x <= io_state_r;
io_cnt_x <= io_cnt_r;
ready_x <= ready_r;
en_reg_wr_s <= '0';
case io_state_r is
when IO_IDLE =>
-- Wait for CE_n and WR_n.
if ce_n_r = '0' and wr_n_r = '0' then
io_state_x <= IO_OP;
ready_x <= '0';
if FAST_IO_G = '1' then
-- No delay.
io_cnt_x <= (others => '0');
else
-- The real IC takes 32 cycles for an I/O operation.
io_cnt_x <= to_unsigned(31, io_cnt_x'length);
end if;
end if;
when IO_OP =>
if io_cnt_r = 0 then
io_state_x <= IO_WAIT;
en_reg_wr_s <= '1';
ready_x <= '1';
else
io_cnt_x <= io_cnt_r - 1;
end if;
when IO_WAIT =>
-- The CPU must end the write-cycle; fine if CE_n is still asserted.
if wr_n_r = '1' then
io_state_x <= IO_IDLE;
end if;
end case;
end process;
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
io_state_r <= io_state_x;
io_cnt_r <= io_cnt_x;
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Registers. Setting the channel tone period requires two writes to set
-- the full 10-bit value. Bit numbering is n..0, which is *BACKWARDS* from
-- TI's bit numbering during this era.
--
-- 7654 3210
-- R0 1000 PPPP Channel A tone period 3..0.
-- 0-PP PPPP Channel A tone period 9..4.
-- R1 1001 AAAA Channel A attenuation.
-- R2 1010 PPPP Channel B tone period 3..0.
-- 0-PP PPPP Channel B tone period 9..4.
-- R3 1011 AAAA Channel B attenuation.
-- R4 1100 PPPP Channel C tone period 3..0.
-- 0-PP PPPP Channel C tone period 9..4.
-- R5 1101 AAAA Channel C attenuation.
-- R6 1110 -F-- Noise feedback, 0=periodic, 1=white noise
-- 1110 --SS Noise shift rate, 00=N/512, 01=N/1024, 10=N/2048, 11=Channel C
-- R7 1111 AAAA Noise attenuation.
-- The register last written is latched, so any bytes written with a '0' in
-- the MS-bit will go to the same register.
-- The output-level is converted to the equivalent DAC value and stored as
-- as the look-up result, rather than the 4-bit level index. This allows
-- sharing of the ROM lookup table, and uses less FPGA resources.
dac_level_s <= dacrom_ar(to_integer(unsigned(din_r(3 downto 0))));
register_file : process
( din_r, reg_addr_r, reg_sel_s, dac_level_s, en_reg_wr_s
, ch_a_period_r, ch_a_level_r
, ch_b_period_r, ch_b_level_r
, ch_c_period_r, ch_c_level_r
, noise_ctrl_r, noise_level_r, noise_shift_r
) begin
-- Register writes go to the specified register, data writes go to the
-- previously written register.
if din_r(7) = '0' then
reg_sel_s <= reg_addr_r;
else
reg_sel_s <= din_r(6 downto 4);
end if;
ch_a_period_x <= ch_a_period_r;
ch_a_level_x <= ch_a_level_r;
ch_b_period_x <= ch_b_period_r;
ch_b_level_x <= ch_b_level_r;
ch_c_period_x <= ch_c_period_r;
ch_c_level_x <= ch_c_level_r;
noise_ctrl_x <= noise_ctrl_r;
noise_shift_x <= noise_shift_r;
noise_level_x <= noise_level_r;
noise_rst_x <= '0';
case reg_sel_s is
when "000" =>
if din_r(7) = '0' then
ch_a_period_x <= din_r(5 downto 0) & ch_a_period_r(3 downto 0);
else
ch_a_period_x <= ch_a_period_r(9 downto 4) & din_r(3 downto 0);
end if;
when "001" =>
ch_a_level_x <= dac_level_s;
when "010" =>
if din_r(7) = '0' then
ch_b_period_x <= din_r(5 downto 0) & ch_b_period_r(3 downto 0);
else
ch_b_period_x <= ch_b_period_r(9 downto 4) & din_r(3 downto 0);
end if;
when "011" =>
ch_b_level_x <= dac_level_s;
when "100" =>
if din_r(7) = '0' then
ch_c_period_x <= din_r(5 downto 0) & ch_c_period_r(3 downto 0);
else
ch_c_period_x <= ch_c_period_r(9 downto 4) & din_r(3 downto 0);
end if;
when "101" =>
ch_c_level_x <= dac_level_s;
when "110" =>
noise_ctrl_x <= din_r(2);
noise_shift_x <= din_r(1 downto 0);
-- Writing to the noise control register resets the LFSR to its
-- initialization value.
noise_rst_x <= en_reg_wr_s;
-- "111"
when others =>
noise_level_x <= dac_level_s;
null;
end case;
end process;
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
noise_rst_r <= noise_rst_x;
if en_reg_wr_s = '1' then
-- Latch the register when the write specifies a register.
reg_addr_r <= reg_sel_s;
ch_a_period_r <= ch_a_period_x;
ch_a_level_r <= ch_a_level_x;
ch_b_period_r <= ch_b_period_x;
ch_b_level_r <= ch_b_level_x;
ch_c_period_r <= ch_c_period_x;
ch_c_level_r <= ch_c_level_x;
noise_ctrl_r <= noise_ctrl_x;
noise_shift_r <= noise_shift_x;
noise_level_r <= noise_level_x;
end if;
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Clock conditioning. Reduce the input clock to provide the divide by
-- sixteen clock-phases.
--
clk_div16_x <= clk_div16_r + 1;
en_cnt_x <= '1' when clk_div16_r = 0 else '0';
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
clk_div16_r <= clk_div16_x;
en_cnt_r <= en_cnt_x;
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Channel tone counters. The counters *always* count.
--
-- The counters count *down* and load the period when they reach zero. The
-- zero-check-and-load are all part of the same cycle. An implementation in
-- C might look like this:
--
-- {
-- if ( counter == 0 ) {
-- tone = !tone;
-- counter = period;
-- }
--
-- counter--;
-- }
--
-- With the period work-around described below:
--
-- {
-- if ( counter == 0 )
-- {
-- if ( period > 0 and period < 6 ) {
-- tone = 1;
-- } else {
-- tone = !tone;
-- }
--
-- counter = period;
-- }
--
-- counter--;
-- }
--
-- This also demonstrates why changing the tone period will not take effect
-- until the next cycle of the counter. Interestingly, the same counter is
-- used in the AY-3-8910 and YM-2149, only slightly modified to count up
-- (actually, in silicon both up and down counters are present
-- simultaneously) and reset on a >= period condition.
--
-- Amplitude Modulation.
--
-- With a typical 3.5MHz to 4.0MHz (max) input clock, the main-clock divide
-- by sixteen produces a 223.72KHz clock into the tone counters. With small
-- period counts, frequencies *well over* the human hearing range can be
-- produced.
--
-- The problem with the frequencies over 20KHz is, in a digital SoC the high
-- frequencies do not filter out like they do when the output is connected
-- to an external low-pass filter and inductive load (speaker), like they
-- are with the real IC.
--
-- In an all digital system with digital audio, the generated frequencies
-- should never be more than the Nyquist frequency (twice the sample rate).
-- Typical sample rates are 44.1KHz or 48KHz, so any frequency over 20KHz
-- should not be output (and is not audible to a human anyway).
--
-- The work-around here is to flat-line the toggle flip-flop for any tone
-- counter with a period that produces a frequency over the Nyquist rate.
-- This change still allows the technique of modulating the output with
-- rapid volume level changes.
--
-- Based on a typical PSG clock of 3.58MHz for a Z80-based system, the
-- period counts that cause frequencies above 20KHz are:
--
-- f = CLK / (32 * Count)
--
-- Clk 3,579,545Hz 2.793651ns
--
-- Cnt Frequency Period
-- 1 111860.78Hz 8.9396us
-- 2 55930.39Hz 17.8793us
-- 3 37286.92Hz 26.8190us
-- 4 27965.19Hz 35.7587us
-- 5 22372.15Hz 44.6984us
-- ---------------------------
-- 6 18643.46Hz 53.6381us First audible count.
-- 7 15980.11Hz 62.5777us
-- 8 13982.59Hz 71.5174us
-- 9 12428.97Hz 80.4571us
-- 10 11186.07Hz 89.3968us
-- 11 10169.16Hz 98.3365us
-- 12 9321.73Hz 107.2762us
-- 13 8604.67Hz 116.2158us
-- 14 7990.05Hz 125.1555us
-- 15 7457.38Hz 134.0952us
-- 16 6991.29Hz 143.0349us Used by some software for level-modulation.
-- ---------------------------
-- 17 6580.04Hz 151.9746us
-- ...
-- 0 109.23Hz 9.1542ms A count of zero is the maximum period.
--
-- While a count of 6 is technically the Nyquist cut-off, some game software
-- is known to use a period of 16 as the base frequency for the amplitude
-- modulation hack. While audible, the 7KHz tone is not very musical, and
-- causes a harsh (if not painful) undertone to the sound being created with
-- the amplitude modulation.
--
-- Suffice to say, setting the flat-line cut-off to 16 should not affect the
-- audio for most software in any negative way, but can help some software
-- sound better.
-- A channel counter and tone flip-flop.
ch_a_cnt_x <=
-- Load uses count-enable next-state look-ahead when counter is 0.
ch_a_period_r when (en_cnt_x = '1' and ch_a_cnt_r = 0) else
-- Counting uses the current-state.
ch_a_cnt_r - 1 when en_cnt_r = '1' else
ch_a_cnt_r;
flatline_a_s <=
'1' when ch_a_period_r > 0 and ch_a_period_r < MIN_PERIOD_CNT_G else
'0';
tone_a_x <=
-- Flat-line the output for counts that produce frequencies > 20KHz.
'1' when flatline_a_s = '1' else
-- Toggle channel tone flip-flop when the counter reaches 0.
-- Same look-ahead condition as the counter-load.
not tone_a_r when (en_cnt_x = '1' and ch_a_cnt_r = 0) else
tone_a_r;
-- B channel counter and tone flip-flop.
ch_b_cnt_x <=
ch_b_period_r when (en_cnt_x = '1' and ch_b_cnt_r = 0) else
ch_b_cnt_r - 1 when en_cnt_r = '1' else
ch_b_cnt_r;
flatline_b_s <=
'1' when ch_b_period_r > 0 and ch_b_period_r < MIN_PERIOD_CNT_G else
'0';
tone_b_x <=
'1' when flatline_b_s = '1' else
not tone_b_r when (en_cnt_x = '1' and ch_b_cnt_r = 0) else
tone_b_r;
-- C channel counter and tone flip-flop.
ch_c_cnt_x <=
ch_c_period_r when (en_cnt_x = '1' and ch_c_cnt_r = 0) else
ch_c_cnt_r - 1 when en_cnt_r = '1' else
ch_c_cnt_r;
flatline_c_s <=
'1' when ch_c_period_r > 0 and ch_c_period_r < MIN_PERIOD_CNT_G else
'0';
tone_c_x <= flatline_c_s or c_ff_r;
-- The work-around to limit high frequency outputs interferes with Channel-C
-- being able to clock the noise shift register. This is a work-around to
-- that work-around, to always have an output from Channel-C that can be
-- used to clock the noise shift register.
c_ff_x <=
not c_ff_r when (en_cnt_x = '1' and ch_c_cnt_r = 0) else
c_ff_r;
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
ch_a_cnt_r <= ch_a_cnt_x;
tone_a_r <= tone_a_x;
ch_b_cnt_r <= ch_b_cnt_x;
tone_b_r <= tone_b_x;
ch_c_cnt_r <= ch_c_cnt_x;
tone_c_r <= tone_c_x;
c_ff_r <= c_ff_x;
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Noise period counter. A continuous counter to further divide the input
-- clock by 32, 64, or 128. The output goes to a selector, controlled by the
-- noise register, to choose one of the three rates, or the output flip-flop
-- of channel C, to drive the LFSR.
noise_cnt_x <=
noise_cnt_r + 1 when en_cnt_r = '1' else
noise_cnt_r;
with noise_shift_r select
noise_ff_x <= -- N = PSG input clock, typical 3.58MHz.
noise_cnt_r(4) when "00", -- N / 512 = approx 6991.2988Hz 143.034us
noise_cnt_r(5) when "01", -- N / 1024 = approx 3495.6494Hz 286.069us
noise_cnt_r(6) when "10", -- N / 2048 = approx 1747.8247Hz 572.139us
c_ff_r when others; -- "11" -- Channel C tone as the clock.
-- The noise can be periodic or white depending on the feedback-bit in the
-- noise control register. 0 = periodic, which just disables the XOR of the
-- LFSR and loops the single initialization bit through the shift register.
--
-- Noise 15-bit right-shift LFSR with taps at 0 and 1, LS-bit is the output.
-- Reset loads the LFSR with 0x4000 to prevent lock-up. The same pattern
-- can be obtained with a left-shift, taps at 13 and 14, MS-bit output.
--
-- Accurate implementation in C, no branching:
--
-- uint32_t lfsr;
-- uint8_t noise_bit;
--
-- // A 15-input NOR gate ensures init with 100_0000_0000_0000
-- lfsr = (1 << 14);
--
-- // Taps at 0 and 1, mask result.
-- int32_t fb = ((lfsr >> 0) ^ (lfsr >> 1)) & 1;
--
-- // Right-shift, feedback bit to the most-significant bit.
-- lfsr = (lfsr >> 1) | (fb << 14);
--
-- noise_bit = (lfsr & 1);
--
noise_fb_s <=
(noise_ctrl_r and noise_lfsr_r(1)) xor noise_lfsr_r(0);
noise_lfsr_x <= noise_fb_s & noise_lfsr_r(14 downto 1);
noise_s <= noise_lfsr_r(0);
process
( clk_i, en_clk_psg_i, noise_rst_r
) begin
if rising_edge(clk_i) then
-- ** NOTE: This reset is active high.
if noise_rst_r = '1' then
noise_cnt_r <= (others => '0');
noise_ff_r <= '1';
noise_lfsr_r <= b"100_0000_0000_0000";
elsif en_clk_psg_i = '1' then
noise_cnt_r <= noise_cnt_x;
noise_ff_r <= noise_ff_x;
-- Look-ahead rising-edge detect the noise flip-flop.
if noise_ff_r = '0' and noise_ff_x = '1' then
noise_lfsr_r <= noise_lfsr_x;
end if;
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Amplitude / Attenuation control. The amplitude of each channel is
-- controlled by the channel's 4-bit attenuation register.
--
-- The "level" in the SN76489 is an amount of attenuation, and the channel's
-- level has already been converted into its DAC level. When the tone
-- flip-flop is '0', the most attenuation (minimum DAC level) is output.
level_a_s <=
(others => '0') when tone_a_r = '0' else
ch_a_level_r;
level_b_s <=
(others => '0') when tone_b_r = '0' else
ch_b_level_r;
level_c_s <=
(others => '0') when tone_c_r = '0' else
ch_c_level_r;
level_n_s <=
(others => '0') when noise_s = '0' else
noise_level_r;
-- -----------------------------------------------------------------------
--
-- Output registers and unsigned summation. If the level had not already
-- been converted to the output level, this would also be the DAC section.
--
ch_a_o <= dac_a_r;
ch_b_o <= dac_b_r;
ch_c_o <= dac_c_r;
noise_o <= dac_n_r;
mix_audio_o <= sum_audio_r;
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
dac_a_r <= level_a_s;
dac_b_r <= level_b_s;
dac_c_r <= level_c_s;
dac_n_r <= level_n_s;
-- Sum the audio channels.
sum_audio_r <= ("00" & level_a_s) + ("00" & level_b_s) +
("00" & level_c_s) + ("00" & level_n_s);
end if;
end if;
end process;
-- -----------------------------------------------------------------------
--
-- Signed zero-centered 14-bit PCM.
--
-- Make a -/+ level value depending on the tone state. When the flat-line
-- work-around for frequencies over the Nyquist rate is in effect, adjust
-- the unsigned level-range to a signed-level range.
--
-- signed_level =
-- level - (range / 2) when flat-line
--
-- otherwise, -/+ zero-centered square-wave:
--
-- signed_level =
-- -(level / 2) when tone == 0
-- (level / 2) when tone == 1
sign_a_x <=
ch_a_level_r - x"800" when flatline_a_s = '1' else
("1" & (not ch_a_level_r(11 downto 1))) + 1 when tone_a_r = '0' else
("0" & ch_a_level_r(11 downto 1));
sign_b_x <=
ch_b_level_r - x"800" when flatline_b_s = '1' else
("1" & (not ch_b_level_r(11 downto 1))) + 1 when tone_b_r = '0' else
("0" & ch_b_level_r(11 downto 1));
sign_c_x <=
ch_c_level_r - x"800" when flatline_c_s = '1' else
("1" & (not ch_c_level_r(11 downto 1))) + 1 when tone_c_r = '0' else
("0" & ch_c_level_r(11 downto 1));
sign_n_x <=
("1" & (not noise_level_r(11 downto 1))) + 1 when noise_s = '0' else
("0" & noise_level_r(11 downto 1));
pcm14s_o <= pcm14s_r;
process
( clk_i, en_clk_psg_i
) begin
if rising_edge(clk_i) then
if en_clk_psg_i = '1' then
sign_a_r <= sign_a_x;
sign_b_r <= sign_b_x;
sign_c_r <= sign_c_x;
sign_n_r <= sign_n_x;
-- Sum to signed 14-bit and left-align to signed 16-bit.
pcm14s_r <=
(sign_a_r(11) & sign_a_r(11) & sign_a_r) +
(sign_b_r(11) & sign_b_r(11) & sign_b_r) +
(sign_c_r(11) & sign_c_r(11) & sign_c_r) +
(sign_n_r(11) & sign_n_r(11) & sign_n_r);
end if;
end if;
end process;
end rtl;

View File

@@ -67,6 +67,8 @@ entity snd is
SND_ADDR : out std_logic_vector(15 downto 0);
SND_DOUT : out std_logic_vector(7 downto 0);
SND_DIN : in std_logic_vector(7 downto 0);
SND_INIT_HW : in std_logic;
SND_INIT_DONEn : out std_logic;
-- Arbiter control signals
SND_GRANTn : in std_logic;
@@ -121,6 +123,7 @@ architecture RTL of snd is
signal SND_FIFO_WEN : std_logic;
signal SND_FIFO_WR : natural range 0 to CACHE_SIZE;
signal SND_FIFO_RD : natural range 0 to CACHE_SIZE;
signal SND_INITi : std_logic;
signal MB_STATE : integer range 0 to 3;
-- Digital to Analogue converter and mixer.
@@ -166,6 +169,7 @@ begin
Z80_WR_LASTn <= '1';
SND_DATA <= (others => '0');
SND_RD_DATA <= (others => '0');
SND_INITi <= SND_INIT_HW;
elsif rising_edge(CLKBUS(CKMASTER)) then
@@ -315,8 +319,8 @@ begin
AudioLeft := (others => '0');
AudioRight := (others => '0');
else
AudioLeft := shift_right(AudioMixLeft, to_integer(unsigned(not CONFIG(AUDIOVOL))));
AudioRight := shift_right(AudioMixRight, to_integer(unsigned(not CONFIG(AUDIOVOL))));
AudioLeft := AudioMixLeft / (to_integer(unsigned(not CONFIG(AUDIOVOL)))+1); --shift_right(AudioMixLeft, to_integer(unsigned(not CONFIG(AUDIOVOL))));
AudioRight := AudioMixRight / (to_integer(unsigned(not CONFIG(AUDIOVOL)))+1); --shift_right(AudioMixRight, to_integer(unsigned(not CONFIG(AUDIOVOL))));
end if;
-- Assign computed digital sound value to DAC outputs.
@@ -330,14 +334,14 @@ begin
-- to ascertain a required voltage level.
DACL : sigma_delta_dac
generic map (
MSBI => 15, -- Highest bit number.
MSBI => 23, -- Highest bit number.
INV => 1 -- Invert
)
port map
(
CLK => CLKBUS(CKMASTER),
RESET => not RSTn,
DACin => SND_DAC_LEFT,
DACin => SND_DAC_LEFT & X"00",
DACout => SND_AUDIO_LEFT
);
@@ -346,14 +350,14 @@ begin
-- to ascertain a required voltage level.
DACR : sigma_delta_dac
generic map (
MSBI => 15, -- Highest bit number.
MSBI => 23, -- Highest bit number.
INV => 1 -- Invert
)
port map
(
CLK => CLKBUS(CKMASTER),
RESET => not RSTn,
DACin => SND_DAC_RIGHT,
DACin => SND_DAC_RIGHT & X"00",
DACout => SND_AUDIO_RIGHT
);
@@ -364,5 +368,6 @@ begin
SND_IORQn <= SND_IORQni;
SND_M1n <= SND_M1ni;
SND_BUSYn <= SND_BUSYni;
SND_INIT_DONEn <= SND_INITi;
end RTL;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
--
-- Interrupt_v2.vhd
--
-- Z80 Daisy-Chain Interrupt Logic for FPGA
--
-- Nibbles Lab. 2013-2014
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Interrupt_v2 is
Port (
-- System Signal
RESET : in std_logic;
-- CPU Signals
DI : in std_logic_vector(7 downto 0);
IORQn : in std_logic; -- same as Z80
RDn : in std_logic; -- same as Z80
M1n : in std_logic; -- same as Z80
IEI : in std_logic; -- same as Z80
IEO : out std_logic; -- same as Z80
INTOn : out std_logic;
-- Control Signals
VECTEN : out std_logic;
INTI : in std_logic;
INTEN : in std_logic
);
end Interrupt_v2;
architecture Behavioral of Interrupt_v2 is
-----------------------------------------------------------------------------------
-- Signals
-----------------------------------------------------------------------------------
signal IREQ : std_logic;
signal IRES : std_logic;
signal INTR : std_logic;
signal IAUTH : std_logic;
signal AUTHRES : std_logic;
signal IED1 : std_logic;
signal IED2 : std_logic;
signal ICB : std_logic;
signal I4D : std_logic;
signal FETCH : std_logic;
signal INTA : std_logic;
signal IENB : std_logic;
signal iINT : std_logic;
signal iIEO : std_logic;
begin
--
-- External signals
--
INTOn <= iINT;
IEO <= iIEO;
--
-- Internal signals
--
iINT <= '0' when IEI='1' and IREQ='1' and IAUTH='0' else '1';
iIEO <= not (((not IED1) and IREQ) or IAUTH or (not IEI));
INTA <= ((not M1n ) and (not IORQn ) and IEI);
AUTHRES <= RESET or (IEI and IED2 and I4D);
FETCH <= M1n or RDn ;
IRES <= RESET or INTA;
INTR <= M1n and (INTI and INTEN);
VECTEN <= '1' when INTA='1' and IEI='1' and IAUTH='1' else '0';
--
-- Keep Interrupt Request
--
process( IRES, INTR ) begin
if IRES='1' then
IREQ <= '0';
elsif INTR'event and INTR='1' then
IREQ <= '1';
end if;
end process;
--
-- Interrupt Authentication
--
process( AUTHRES, INTA ) begin
if AUTHRES='1' then
IAUTH <= '0';
elsif INTA'event and INTA='1' then
IAUTH <= IREQ;
end if;
end process;
--
-- Fetch 'RETI'
--
process( RESET, FETCH ) begin
if RESET='1' then
IED1 <= '0';
IED2 <= '0';
ICB <= '0';
I4D <= '0';
elsif FETCH'event and FETCH='1' then
IED2 <= IED1;
if DI=X"ED" and ICB='0' then
IED1 <= '1';
else
IED1 <= '0';
end if;
if DI=X"CB" then
ICB <= '1';
else
ICB <= '0';
end if;
if DI=X"4D" then
I4D <= IEI;
else
I4D <= '0';
end if;
end if;
end process;
end Behavioral;

View File

@@ -0,0 +1,379 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: z8420_v2.vhd
-- Created: Original design, 2005-2014, Updates Dec 2021.
-- Author(s): Nibbles Lab, Philip Smart
-- Description: Sharp MZ series PWM Tape Interface.
-- This module interfaces the emulation to physical sound generation hardware. On the
-- MZ-80K series this is generally an 8253 timer, on the MZ-80B series this is a digital
-- bit except the MZ-800 which uses an additional single PSG, the MZ-1500 which uses
-- twin PSG's and the MZ-2500 which uses a Yamaha PSG.
-- This module either outputs sound by controlling underlying host hardware or it
-- mixes a digital sound representation to be output by the FPGA for use with the
-- host hardware.
-- Credits:
-- Copyright: Nibbles Lab. 2005-2014 - Original work.
-- (c) 2018-22 Philip Smart <philip.smart@net2net.org> - Enhancements
--
-- History: Oct 2021 - Initial write of the module.
-- Dec 2021 - Added Audio inputs and Sigma Delta DAC's to create PWM output.
-- Hardware used is selected via the AUDIOHW option, either writing
-- to the underlying host hardware or using the onboard FPGA hardware.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
--
-- z8420_v2.vhd
--
-- Zilog Z80PIO partiality compatible module
-- for MZ-80B on FPGA
--
-- Port A : Output, mode 0 only
-- Port B : Input, mode 0 only
--
-- Nibbles Lab. 2005-2014
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity z8420_v2 is
Port (
-- System
RSTn : in std_logic; -- Only Power On Reset
-- Z80 Bus Signals
CLK : in std_logic;
ENA : in std_logic;
BASEL : in std_logic;
CDSEL : in std_logic;
CE : in std_logic;
RDn : in std_logic;
WRn : in std_logic;
IORQn : in std_logic;
M1n : in std_logic;
DI : in std_logic_vector(7 downto 0);
DO : out std_logic_vector(7 downto 0);
IEI : in std_logic;
IEO : out std_logic;
INTn : out std_logic;
-- Port
A_IN : in std_logic_vector(7 downto 0);
A_OUT : out std_logic_vector(7 downto 0);
A_OUT_EN : in std_logic_vector(7 downto 0);
ARDY : out std_logic;
ASTBn : in std_logic;
B_IN : in std_logic_vector(7 downto 0);
B_OUT : out std_logic_vector(7 downto 0);
B_OUT_EN : in std_logic_vector(7 downto 0);
BRDY : out std_logic;
BSTBn : in std_logic
);
end z8420_v2;
architecture Behavioral of z8420_v2 is
--
-- Signal conditioners.
--
signal WR_LAST : std_logic_vector(1 downto 0);
--
-- Port Selecter
--
signal SELAD : std_logic;
signal SELBD : std_logic;
signal SELAC : std_logic;
signal SELBC : std_logic;
--
-- Port Register
--
signal AREG : std_logic_vector(7 downto 0); -- Output Register (Port A)
signal DIRA : std_logic_vector(7 downto 0); -- Data Direction (Port A)
signal DDWA : std_logic; -- Prepare for Data Direction (Port A)
signal IMWA : std_logic_vector(7 downto 0); -- Interrupt Mask Word (Port A)
signal MFA : std_logic; -- Mask Follows (Port A)
signal VECTA : std_logic_vector(7 downto 0); -- Interrupt Vector (Port A)
signal MODEA : std_logic_vector(1 downto 0); -- Mode Word (Port A)
signal HLA : std_logic; -- High/Low (Port A)
signal AOA : std_logic; -- AND/OR (Port A)
signal BREG : std_logic_vector(7 downto 0); -- Output Register (Port B)
signal DIRB : std_logic_vector(7 downto 0); -- Data Direction (Port B)
signal DDWB : std_logic; -- Prepare for Data Direction (Port B)
signal IMWB : std_logic_vector(7 downto 0); -- Interrupt Mask Word (Port B)
signal MFB : std_logic; -- Mask Follows (Port B)
signal VECTB : std_logic_vector(7 downto 0); -- Interrupt Vector (Port B)
signal MODEB : std_logic_vector(1 downto 0); -- Mode Word (Port B)
signal HLB : std_logic; -- High/Low (Port B)
signal AOB : std_logic; -- AND/OR (Port B)
--
-- Interrupt
--
signal VECTENA : std_logic;
signal EIA : std_logic; -- Interrupt Enable (Port A)
signal MINTA : std_logic_vector(7 downto 0);
signal INTA : std_logic;
signal INT0n : std_logic;
signal VECTENB : std_logic;
signal EIB : std_logic; -- Interrupt Enable (Port B)
signal MINTB : std_logic_vector(7 downto 0);
signal INTB : std_logic;
signal INT1n : std_logic;
--
-- Components
--
component Interrupt is
Port (
-- System Signal
RESET : in std_logic;
-- CPU Signals
DI : in std_logic_vector(7 downto 0);
IORQn : in std_logic; -- same as Z80
RDn : in std_logic; -- same as Z80
M1n : in std_logic; -- same as Z80
IEI : in std_logic; -- same as Z80
IEO : out std_logic; -- same as Z80
INTOn : out std_logic;
-- Control Signals
VECTEN : out std_logic;
INTI : in std_logic;
INTEN : in std_logic
);
end component;
begin
--
-- Instantiation
--
INT0 : Interrupt port map (
-- System Signal
RESET => not RSTn,
-- CPU Signals
DI => DI,
IORQn => IORQn,
RDn => RDn,
M1n => M1n,
IEI => IEI,
IEO => IEO,
INTOn => INT0n,
-- Control Signals
VECTEN => VECTENA,
INTI => INTA,
INTEN => EIA
);
INT1 : Interrupt port map (
-- System Signal
RESET => not RSTn ,
-- CPU Signals
DI => DI,
IORQn => IORQn,
RDn => RDn,
M1n => M1n,
IEI => IEI,
IEO => IEO,
INTOn => INT1n,
-- Control Signals
VECTEN => VECTENB,
INTI => INTB,
INTEN => EIB
);
--
-- Port select for Output
--
SELAD <= '1' when BASEL='0' and CDSEL='0' else '0';
SELBD <= '1' when BASEL='1' and CDSEL='0' else '0';
SELAC <= '1' when BASEL='0' and CDSEL='1' else '0';
SELBC <= '1' when BASEL='1' and CDSEL='1' else '0';
--
-- Control Register programming.
--
process( RSTn , CLK, ENA ) begin
if RSTn ='0' then
AREG <= (others=>'0');
MODEA <= "01";
DDWA <= '0';
MFA <= '0';
EIA <= '0';
BREG <=(others=>'0');
MODEB <= "01";
DDWB <= '0';
MFB <= '0';
EIB <= '0';
elsif CLK'event and CLK='0' then
if ENA = '1' then
-- Writes clock cycle can be greater than 1 clock so limit to the falling edge.
WR_LAST <= WR_LAST(0) & WRn;
if CE='0' and WRn ='0' and WR_LAST = "11" then
if SELAD='1' then
AREG <= DI;
end if;
if SELBD='1' then
BREG <= DI;
end if;
-- Port A
if SELAC='1' then
-- If the define data word is set, store the byte as the direction map of each bit.
if DDWA='1' then
DIRA <= DI;
DDWA <= '0';
-- If the MASK setting bit is set, store the byte as the interrupt mask.
elsif MFA='1' then
IMWA <= DI;
MFA <= '0';
-- Set the interrupt Vector.
elsif DI(0)='0' then
VECTA <= DI;
-- Set the mode.
elsif DI(3 downto 0)="1111" then
MODEA <= DI(7 downto 6);
DDWA <= DI(7) and DI(6); -- Bidirectional mode requires a follow up byte to define bit direction.
-- Set the interrupt control word. AOA, HLA and MFA only applicable in Mode 3.
elsif DI(3 downto 0)="0111" then
MFA <= DI(4); -- Setting of this bit requires a follow on MASK to be sent.
HLA <= DI(5);
AOA <= DI(6);
EIA <= DI(7);
-- Enable/Disable interrupt, D7 = 0 = Disable, = 1 = Enable.
elsif DI(3 downto 0)="0011" then
EIA <= DI(7);
end if;
end if;
-- Port B
if SELBC='1' then
-- If the define data word is set, store the byte as the direction map of each bit.
if DDWB='1' then
DIRB <= DI;
DDWB <= '0';
-- If the MASK setting bit is set, store the byte as the interrupt mask.
elsif MFB='1' then
IMWB <= DI;
MFB <= '0';
-- Set the interrupt Vector.
elsif DI(0)='0' then
VECTB <= DI;
-- Set the mode.
elsif DI(3 downto 0)="1111" then
MODEB <= DI(7 downto 6);
DDWB <= DI(7) and DI(6);
-- Set the interrupt control word. AOA, HLA and MFA only applicable in Mode 3.
elsif DI(3 downto 0)="0111" then
MFB <= DI(4);
HLB <= DI(5);
AOB <= DI(6);
EIB <= DI(7);
-- Enable/Disable interrupt, D7 = 0 = Disable, = 1 = Enable.
elsif DI(3 downto 0)="0011" then
EIB <= DI(7);
end if;
end if;
end if;
end if;
end if;
end process;
--
-- Input select
--
DO <= VECTA when VECTENA='1'
else
VECTB when VECTENB='1'
else
A_IN when RDn = '0' and CE = '0' and SELAD = '1' and MODEA = "01"
else
A_IN when RDn = '0' and CE = '0' and SELAD = '1' and MODEA = "10" and ASTBn = '1'
else
A_IN and DIRA when RDn = '0' and CE = '0' and SELAD = '1' and MODEA = "11"
else
AREG when RDn = '0' and CE = '0' and SELAD = '1' and MODEA = "00"
else
B_IN when RDn = '0' and CE = '0' and SELBD = '1' and MODEB = "01"
else
B_IN when RDn = '0' and CE = '0' and SELBD = '1' and MODEB = "10" and ASTBn = '1'
else
B_IN and DIRB when RDn = '0' and CE = '0' and SELBD = '1' and MODEB = "11"
else
BREG when RDn = '0' and CE = '0' and SELBD = '1' and MODEB = "00"
else
VECTA when RDn = '0' and CE = '0' and SELAC = '1'
else
MFA & HLA & AOA & EIA & MODEA & '0' & A_IN(4) when RDn = '0' and CE = '0' and SELBC = '1'
else (others => '0');
-- Tristate outputs dependent upon mode and configuration.
OUTA : for I in 0 to 7 generate
A_OUT(I) <= AREG(I) when MODEA = "00" and A_OUT_EN(I) = '1'
else
AREG(I) when MODEA = "10" and A_OUT_EN(I) = '1' and ASTBn = '0'
else
AREG(I) when DIRA(I) = '0' and A_OUT_EN(I) = '1'
else 'Z';
end generate OUTA;
OUTB : for I in 0 to 7 generate
B_OUT(I) <= BREG(I) when MODEB = "00" and B_OUT_EN(I) = '1'
else
BREG(I) when MODEB = "10" and B_OUT_EN(I) = '1' and BSTBn = '0'
else
BREG(I) when DIRB(I) = '0' and B_OUT_EN(I) = '1'
else 'Z';
end generate OUTB;
--
-- Interrupt masking, Mode 3.
--
INTMASK : for I in 0 to 7 generate
-- MINTA(I) <= '1' when (A_IN(I) = HLA) and (IMWA(I) = '0') and AOA='0'
-- else
-- '1' when (A_IN(I) = HLA) or IMWA(I);
MINTA(I) <= (A_IN(I) xnor HLA) and (not IMWA(I)) when AOA='0'
else
(A_IN(I) xnor HLA) or IMWA(I);
MINTB(I) <= (B_IN(I) xnor HLB) and (not IMWB(I)) when AOB='0'
else
(B_IN(I) xnor HLB) or IMWB(I);
end generate INTMASK;
--
-- Interrupt select
--
--INTA <= '1' when A_IN(4) = '0' else '0'; --MINTA(7) or MINTA(6) or MINTA(5) or MINTA(4) or MINTA(3) or MINTA(2) or MINTA(1) or MINTA(0) when AOA='0'
INTA <= MINTA(7) or MINTA(6) or MINTA(5) or MINTA(4) or MINTA(3) or MINTA(2) or MINTA(1) or MINTA(0) when AOA='0'
else
MINTA(7) and MINTA(6) and MINTA(5) and MINTA(4) and MINTA(3) and MINTA(2) and MINTA(1) and MINTA(0);
INTB <= MINTB(7) or MINTB(6) or MINTB(5) or MINTB(4) or MINTB(3) or MINTB(2) or MINTB(1) or MINTB(0) when AOB='0'
else
MINTB(7) and MINTB(6) and MINTB(5) and MINTB(4) and MINTB(3) and MINTB(2) and MINTB(1) and MINTB(0);
-- Combine interrupts, any negative state goes through to the CPU.
INTn <= INT0n and INT1n;
end Behavioral;

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -184,15 +184,28 @@ fi
# Create necessary directories on the SD card and clean them out.
#for dir in TZFS MZF MZ80K MZ80A MZ80B MZ700 MZ800 MZ1500 MZ2000 CPM MSBAS MSCAS
for dir in TZFS MZF MZ80K MZ80A MZ80B MZ80C MZ700 MZ800 MZ1200 MZ1500 MZ2000 CPM MSBAS MSCAS BASIC
for dir in BASIC MSBAS MSCAS CPM DSK MZF TZFS
do
# Clean out the directories to avoid old files being used.
if [[ "${media}x" != "x" ]]; then
mkdir -p $media/${dir}/;
if [ -d $media/${dir} ]; then
rm -f $media/${dir}/*;
rm -fr $media/${dir}/*;
fi
fi
if [[ "${dir}" = "DSK" ]] || [[ "${dir}" = "MZF" ]]; then
for subdir in MZ80K MZ80C MZ1200 MZ80A MZ700 MZ700-2 MZ1500 MZ800 MZ80B MZ2000 MZ2200 MZ2500
do
# Clean out the directories to avoid old files being used.
if [[ "${media}x" != "x" ]]; then
mkdir -p $media/${dir}/${subdir}/;
if [ -d $media/${dir}/${subdir} ]; then
rm -f $media/${dir}/${subdir}/*;
fi
fi
done
fi
done
# Manually copy required files.
@@ -228,18 +241,34 @@ cp -fup ${rootdir}/${softwaredir}/roms/mz2000_cgrom.rom $media/TZ
cp -fup ${rootdir}/${softwaredir}/roms/cpm22.bin $media/CPM/;
cp -fup ${rootdir}/${softwaredir}/CPM/SDC16M/RAW/* $media/CPM/;
cp -fup ${rootdir}/${softwaredir}/MZF/Common/* $media/MZF/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80K/* $media/MZ80K/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80A/* $media/MZ80A/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80B/* $media/MZ80B/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80C/* $media/MZ80C/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-700/* $media/MZ700/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-800/* $media/MZ800/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-1200/* $media/MZ1200/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-1500/* $media/MZ1500/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-2000/* $media/MZ2000/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80K/* $media/MZF/MZ80K/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80A/* $media/MZF/MZ80A/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80B/* $media/MZF/MZ80B/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-80C/* $media/MZF/MZ80C/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-700/* $media/MZF/MZ700/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-700-2/* $media/MZF/MZ700-2/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-800/* $media/MZF/MZ800/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-1200/* $media/MZF/MZ1200/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-1500/* $media/MZF/MZ1500/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-2000/* $media/MZF/MZ2000/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-2200/* $media/MZF/MZ2200/;
cp -fup ${rootdir}/${softwaredir}/MZF/MZ-2500/* $media/MZF/MZ2500/;
cp -fup ${rootdir}/${softwaredir}/BAS/* $media/MSBAS/;
cp -fup ${rootdir}/${softwaredir}/CAS/* $media/MSCAS/
cp -fup ${rootdir}/${softwaredir}/Basic/* $media/BASIC/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-80K/* $media/DSK/MZ80K/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-80A/* $media/DSK/MZ80A/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-80B/* $media/DSK/MZ80B/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-80C/* $media/DSK/MZ80C/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-700/* $media/DSK/MZ700/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-800/* $media/DSK/MZ800/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-1200/* $media/DSK/MZ1200/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-1500/* $media/DSK/MZ1500/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-2000/* $media/DSK/MZ2000/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-2200/* $media/DSK/MZ2200/;
cp -fup ${rootdir}/${softwaredir}/DSK/MZ-2500/* $media/DSK/MZ2500/;
echo "Done, TZFS, CPM and host programs copied to SD card."