From cf9b4d4209c47dfe1ce5a303d0fc2ce609b58db2 Mon Sep 17 00:00:00 2001 From: Philip Smart Date: Sun, 23 Jan 2022 14:11:13 +0000 Subject: [PATCH] Updates due to WD1793 development --- CPLD/SW700/v1.3/MZ700/build/emuMZ_ClockII.qip | 0 CPLD/SW700/v1.3/MZ80A/build/emuMZ_Clock.qip | 0 .../v1.3/VideoController/VideoController.vhd | 167 +-- FPGA/SW700/v1.3/coreMZ_emuMZ.vhd | 165 ++- FPGA/SW700/v1.3/coreMZ_emuMZ_constraints.sdc | 181 +++ FPGA/SW700/v1.3/emuMZ/arbiter.vhd | 14 +- FPGA/SW700/v1.3/emuMZ/clkgen.vhd | 54 +- FPGA/SW700/v1.3/emuMZ/cmt.vhd | 159 ++- FPGA/SW700/v1.3/emuMZ/cmt_hw.vhd | 2 + FPGA/SW700/v1.3/emuMZ/emuMZ.qip | 4 + FPGA/SW700/v1.3/emuMZ/fdd.vhd | 356 +++++ FPGA/SW700/v1.3/emuMZ/iointr.vhd | 57 +- FPGA/SW700/v1.3/emuMZ/keymatrix_mz700.vhd | 50 +- FPGA/SW700/v1.3/emuMZ/mctrl.vhd | 363 +++--- FPGA/SW700/v1.3/emuMZ/mz80b_hw.vhd | 86 +- FPGA/SW700/v1.3/emuMZ/mz80k_hw.vhd | 258 +++- FPGA/SW700/v1.3/emuMZ/sn76489_audio.vhd | 908 +++++++++++++ FPGA/SW700/v1.3/emuMZ/snd.vhd | 17 +- FPGA/SW700/v1.3/emuMZ/wd1793/wd1793.vhd | 1153 +++++++++++++++++ FPGA/SW700/v1.3/emuMZ/z8420/Interrupt_v2.vhd | 125 ++ FPGA/SW700/v1.3/emuMZ/z8420/z8420_v2.vhd | 379 ++++++ software/asm/mz2000_ipl.asm | 750 +++++------ software/roms/mz1500_ipl.rom | Bin 4096 -> 10240 bytes software/roms/mz2000_ipl_original.rom | Bin 2048 -> 2048 bytes software/roms/mz2000_ipl_tzpu.rom | Bin 4096 -> 4096 bytes software/tools/copytosd.sh | 51 +- 26 files changed, 4475 insertions(+), 824 deletions(-) create mode 100644 CPLD/SW700/v1.3/MZ700/build/emuMZ_ClockII.qip create mode 100644 CPLD/SW700/v1.3/MZ80A/build/emuMZ_Clock.qip create mode 100644 FPGA/SW700/v1.3/coreMZ_emuMZ_constraints.sdc create mode 100644 FPGA/SW700/v1.3/emuMZ/fdd.vhd create mode 100644 FPGA/SW700/v1.3/emuMZ/sn76489_audio.vhd create mode 100644 FPGA/SW700/v1.3/emuMZ/wd1793/wd1793.vhd create mode 100644 FPGA/SW700/v1.3/emuMZ/z8420/Interrupt_v2.vhd create mode 100644 FPGA/SW700/v1.3/emuMZ/z8420/z8420_v2.vhd diff --git a/CPLD/SW700/v1.3/MZ700/build/emuMZ_ClockII.qip b/CPLD/SW700/v1.3/MZ700/build/emuMZ_ClockII.qip new file mode 100644 index 0000000..e69de29 diff --git a/CPLD/SW700/v1.3/MZ80A/build/emuMZ_Clock.qip b/CPLD/SW700/v1.3/MZ80A/build/emuMZ_Clock.qip new file mode 100644 index 0000000..e69de29 diff --git a/FPGA/SW700/v1.3/VideoController/VideoController.vhd b/FPGA/SW700/v1.3/VideoController/VideoController.vhd index d559dc3..c2b7ed1 100644 --- a/FPGA/SW700/v1.3/VideoController/VideoController.vhd +++ b/FPGA/SW700/v1.3/VideoController/VideoController.vhd @@ -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= 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= 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= 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= sets the Red bit mask (1 bit = 1 pixel, 8 pixels per byte). -- 0xBB= 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. -- diff --git a/FPGA/SW700/v1.3/coreMZ_emuMZ.vhd b/FPGA/SW700/v1.3/coreMZ_emuMZ.vhd index 3a70bb5..61f7714 100644 --- a/FPGA/SW700/v1.3/coreMZ_emuMZ.vhd +++ b/FPGA/SW700/v1.3/coreMZ_emuMZ.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/coreMZ_emuMZ_constraints.sdc b/FPGA/SW700/v1.3/coreMZ_emuMZ_constraints.sdc new file mode 100644 index 0000000..4c24083 --- /dev/null +++ b/FPGA/SW700/v1.3/coreMZ_emuMZ_constraints.sdc @@ -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 +#************************************************************** diff --git a/FPGA/SW700/v1.3/emuMZ/arbiter.vhd b/FPGA/SW700/v1.3/emuMZ/arbiter.vhd index 041f9bb..22526ab 100644 --- a/FPGA/SW700/v1.3/emuMZ/arbiter.vhd +++ b/FPGA/SW700/v1.3/emuMZ/arbiter.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/clkgen.vhd b/FPGA/SW700/v1.3/emuMZ/clkgen.vhd index bae8337..a10c5d2 100644 --- a/FPGA/SW700/v1.3/emuMZ/clkgen.vhd +++ b/FPGA/SW700/v1.3/emuMZ/clkgen.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/cmt.vhd b/FPGA/SW700/v1.3/emuMZ/cmt.vhd index ca80c74..616fede 100644 --- a/FPGA/SW700/v1.3/emuMZ/cmt.vhd +++ b/FPGA/SW700/v1.3/emuMZ/cmt.vhd @@ -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); diff --git a/FPGA/SW700/v1.3/emuMZ/cmt_hw.vhd b/FPGA/SW700/v1.3/emuMZ/cmt_hw.vhd index 2bb0ea8..95f5f8f 100644 --- a/FPGA/SW700/v1.3/emuMZ/cmt_hw.vhd +++ b/FPGA/SW700/v1.3/emuMZ/cmt_hw.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/emuMZ.qip b/FPGA/SW700/v1.3/emuMZ/emuMZ.qip index e4ecfa7..7548f8a 100644 --- a/FPGA/SW700/v1.3/emuMZ/emuMZ.qip +++ b/FPGA/SW700/v1.3/emuMZ/emuMZ.qip @@ -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] diff --git a/FPGA/SW700/v1.3/emuMZ/fdd.vhd b/FPGA/SW700/v1.3/emuMZ/fdd.vhd new file mode 100644 index 0000000..7326048 --- /dev/null +++ b/FPGA/SW700/v1.3/emuMZ/fdd.vhd @@ -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 +-- +-- 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 . +--------------------------------------------------------------------------------------------------------- + +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; diff --git a/FPGA/SW700/v1.3/emuMZ/iointr.vhd b/FPGA/SW700/v1.3/emuMZ/iointr.vhd index a70039e..f0284c9 100644 --- a/FPGA/SW700/v1.3/emuMZ/iointr.vhd +++ b/FPGA/SW700/v1.3/emuMZ/iointr.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/keymatrix_mz700.vhd b/FPGA/SW700/v1.3/emuMZ/keymatrix_mz700.vhd index ff5ca41..cff1be4 100644 --- a/FPGA/SW700/v1.3/emuMZ/keymatrix_mz700.vhd +++ b/FPGA/SW700/v1.3/emuMZ/keymatrix_mz700.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/mctrl.vhd b/FPGA/SW700/v1.3/emuMZ/mctrl.vhd index 7ed8750..e1a6a5f 100644 --- a/FPGA/SW700/v1.3/emuMZ/mctrl.vhd +++ b/FPGA/SW700/v1.3/emuMZ/mctrl.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/mz80b_hw.vhd b/FPGA/SW700/v1.3/emuMZ/mz80b_hw.vhd index 806729e..fe3fd9f 100644 --- a/FPGA/SW700/v1.3/emuMZ/mz80b_hw.vhd +++ b/FPGA/SW700/v1.3/emuMZ/mz80b_hw.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/mz80k_hw.vhd b/FPGA/SW700/v1.3/emuMZ/mz80k_hw.vhd index 147462e..84fb5ab 100644 --- a/FPGA/SW700/v1.3/emuMZ/mz80k_hw.vhd +++ b/FPGA/SW700/v1.3/emuMZ/mz80k_hw.vhd @@ -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 diff --git a/FPGA/SW700/v1.3/emuMZ/sn76489_audio.vhd b/FPGA/SW700/v1.3/emuMZ/sn76489_audio.vhd new file mode 100644 index 0000000..218f6d6 --- /dev/null +++ b/FPGA/SW700/v1.3/emuMZ/sn76489_audio.vhd @@ -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 dnotq 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; diff --git a/FPGA/SW700/v1.3/emuMZ/snd.vhd b/FPGA/SW700/v1.3/emuMZ/snd.vhd index 69d9091..6fbe0c6 100644 --- a/FPGA/SW700/v1.3/emuMZ/snd.vhd +++ b/FPGA/SW700/v1.3/emuMZ/snd.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/wd1793/wd1793.vhd b/FPGA/SW700/v1.3/emuMZ/wd1793/wd1793.vhd new file mode 100644 index 0000000..d941dbc --- /dev/null +++ b/FPGA/SW700/v1.3/emuMZ/wd1793/wd1793.vhd @@ -0,0 +1,1153 @@ +--------------------------------------------------------------------------------------------------------- +-- +-- Name: wd1793.vhd +-- Created: Jan 2022 +-- Author(s): Philip Smart +-- Description: Sharp MZ Series FPGA core. +-- +-- This module provides the basic functionality to instantiate a Western Digital +-- WD177X series controller. This is used by all of the emulations to allow for +-- floopy disk access. +-- To operate in limited memory, only sectors are cached, any sector not in +-- memory first sees a flush of any dirty sector by the I/O processor to SD card +-- and then the new sector is fetched and written into cache. +-- +-- Credits: Based on the verilog designs from Viacheslav Slavinsky +-- Copyright: (c) 2018-22 Philip Smart +-- (c) Copyright 2007,2008 Viacheslav Slavinsky +-- +-- History: Jan 2022 - Initial creation. +-- +--------------------------------------------------------------------------------------------------------- +-- This source file is free software: you can redistribute it and-or modify +-- it under the terms of the GNU General Public License as published +-- by the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This source file is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +--------------------------------------------------------------------------------------------------------- +library ieee; +library altera; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.coreMZ_pkg.all; +use altera.altera_syn_attributes.all; + + +entity wd1793 is + generic ( + MAX_SECTOR_SIZE : integer := 2048; -- Maximum sector size, used for sizing cache and signals. + ROTATIONAL_CNT_300RPM : integer := 1600000; -- Count of host clock periods representing one disk rotation. 300rpm = 0.2s. Default = 8MHz original FDC clock. + ROTATIONAL_CNT_360RPM : integer := 1333333; -- Count of host clock periods representing one disk rotation. 360rpm = 0.166s. Default = 8MHz original FDC clock. + DELAY_CNT_30MS : integer := 240000 -- For the WD1770/1773 this should be the count for 30ms, for WD1772 this should be the count for 15ms. + ); + port ( + -- WD177X-00 CPU Interface + CLK : in std_logic; -- System or FPGA clock. + CLKEN : in std_logic; -- Clock enable running at original 8MHz FDC timing frequency. + MRn : in std_logic; -- Async reset + CSn : in std_logic; -- Device enable. + RDn : in std_logic; -- I/O read + WRn : in std_logic; -- I/O write + ADDR : in std_logic_vector(1 downto 0); -- I/O port addr + DIN : in std_logic_vector(7 downto 0); -- I/O data in + DOUT : out std_logic_vector(7 downto 0); -- I/O data out + DRQ : out std_logic; -- DMA request + INTRQ : out std_logic; -- Interrupt request for data transfer or service. + BUSY : out std_logic; -- Controller busy with previous command. + RDY : in std_logic; -- Disk loaded (1). + WPRT : in std_logic; -- Write protect (1) + POLARITY : in std_logic; -- Disk data polarity. Invert data stream to match requirements. + DDEN : in std_logic; -- Double Density enable. + SIDE : in std_logic; -- Disk side. + + -- Disk parameters, valid and latched prior to a disk command. + DISK_TYPE : in std_logic_vector(3 downto 0); -- Type of disk and disk structure. + + -- I/O Processor disk service interface. + IOP_READY : in std_logic; -- IO Processor ready to service floppy requests. + IOP_SECTOR_REQ : out std_logic; -- Request for I/O processor for a sector. + IOP_SECTOR_DIR : out std_logic; -- Direction of sector, 0 = sector is written into cache (IOP->WD1793), 1 = sector is read from cache (WD1793->IOP). + IOP_SECTOR_NO : out std_logic_vector(7 downto 0); -- Sectoj number required. + IOP_TRACK_NO : out std_logic_vector(7 downto 0); -- Track number of required sector. + IOP_SIDE : out std_logic; -- Disk side of required sector. + + -- 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(11 downto 0); -- Address bus, width of sector cache + internal register access addresses. + 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 entity; + +architecture rtl of wd1793 is + + -- Western Digital Controller command / status information for WD1793/WD177X series. + -- + -- Commands: + -- Type Command b7 b6 b5 b4 b3 b2 b1 b0 + -- ---- ------- -- -- -- -- -- -- -- -- + -- I Restore 0 0 0 0 h V r1 r0 + -- I Seek 0 0 0 1 h V r1 r0 + -- I Step 0 0 1 T h V r1 r0 + -- I Step-In 0 1 0 T h V r1 r0 + -- I Step-Out 0 1 1 T h V r1 r0 + -- II Read Sector 1 0 0 m S E C 0 + -- II Write Sector 1 0 1 m S E C a0 + -- III Read Address 1 1 0 0 0 E 0 0 + -- III Read Track 1 1 1 0 0 E 0 0 + -- III Write Track 1 1 1 1 0 E 0 0 + -- IV Force Interrupt 1 1 0 1 i3 i2 i1 i0 + -- + -- Flags: + -- r1 r0 Stepping Motor Rate + -- 0 0 6 ms + -- 0 1 12 ms + -- 1 0 20 ms + -- 1 1 30 ms + -- V Track Number Verify Flag (0: no verify, 1: verify on dest track) + -- h Head Load Flag (1: load head at beginning, 0: unload head) + -- T Track Update Flag (0: no update, 1: update Track Register) + -- a0 Data Address Mark (0: FB, 1: F8 (deleted DAM)) + -- C Side Compare Flag (0: disable side compare, 1: enable side comp) + -- E 15 ms delay (0: no 15ms delay, 1: 15 ms delay) + -- S Side Compare Flag (0: compare for side 0, 1: compare for side 1) + -- m Multiple Record Flag (0: single record, 1: multiple records) + -- i3 i2 i1 i0 Interrupt Condition Flags + -- i3-i0 = 0 Terminate with no interrupt (INTRQ) + -- i3 = 1 Immediate interrupt, requires a reset + -- i2 = 1 Index pulse + -- i1 = 1 Ready to not ready transition + -- i0 = 1 Not ready to ready transition + -- + -- Status Register Summary: + -- Bit All type I commands Read Address Read Sector Read Track Write Sector Write Track + -- --- ------------------- ------------ ----------- ---------- ------------ ----------- + -- 7 Not Ready Not Ready Not Ready Not Ready Not Ready Not Ready + -- 6 Write Protect 0 0 0 Write Protect Write Protect + -- 5 Head Loaded 0 Record Type 0 Write Fault Write Fault + -- 4 Seek Error RNF RNF 0 RNF 0 + -- 3 CRC Error CRC Error CRC Error 0 CRC Error 0 + -- 2 Track 0 Lost Data Lost Data Lost Data Lost Data Lost Data + -- 1 Index DRQ DRQ DRQ DRQ DRQ + -- 0 Busy Busy Busy Busy Busy Busy + + -- Type I commands: + -- Bit Name Description + -- --- ---- ----------- + -- 7 Not Ready This bit when set indicates the drive is not ready. When reset it indicates that the drive is ready. This bit is an inverted copy of the Ready input and logically ‘ored’ with MR. + -- 6 Protected When set, indicates Write Protect is activated. This bit is an inverted copy of /WRPT input. + -- 5 Head Loaded When set, it indicates the head is loaded an engaged. This bit is a logical “and” of HLD and HLT signals. + -- 4 Seek Error When set, the desired track was not verified. This bit is reset 0 when updated. + -- 3 CRC Error CRC encountered in ID field. + -- 2 Track 0 When set, indicates Read/Write head i positioned to Track 0. This bit is an inverted copy of the /TR00 input. + -- 1 Index When set, indicates index mark detected from drive. This bit is an inverted copy of the /IP input. + -- 0 Busy When set command is in progress. When reset no command is in progress. + + -- Type II & III commands: + -- Bit Name Description + -- --- ---- ----------- + -- 7 Not Ready This bit when set indicates the drive is not ready. When reset it indicates that the drive is ready. This bit is an inverted copy of the Ready input and logically ‘ored’ with MR. + -- 6 Protected On Read Record or Read Track, not used. On any write: it indicates a Write Protect. This bit is reset when updated. + -- 5 Record Type / Write Fault On Read Record: it indicates the record-type code from data field address mark (1: Deleted Data Mark, 0: Data Mark). On any write: it indicates a Write Fault. This bit is reset when updated. + -- 4 Record Not Found (RNF) When set, it indicates the desired track, sector, or side were not found. This bit is reset when updated. + -- 3 CRC Error if b4 is set, an error is found in one or more ID fields otherwise it indicates error in data field. This bit is reset when updated. + -- 2 Lost Data When set, it indicates the computer did not respond to DRQ in one byte time. This bit is reset to zero when updated. + -- 1 Data Request This bit is a copy of the DRQ output. When set, it indicates the DR is full on a Read operation or the DR is empty on a Write operation. This bit is reset to zero when updated. + -- 0 Busy When set, command is under execution. When reset, no command is under execution. + + -- Find the number of bits required to represent an integer. + function log2ceil(arg : positive) return natural is + variable tmp : positive := 1; + variable log : natural := 0; + begin + if arg = 1 then + return 0; + end if; + + while arg > tmp loop + tmp := tmp * 2; + log := log + 1; + end loop; + return log; + end function; + + -- Configuration constants + constant MAX_SECTOR_BITS : integer := log2ceil(MAX_SECTOR_SIZE); + + -- Register addresses + constant A_COMMAND : integer := 0; + constant A_STATUS : integer := 0; + constant A_TRACK : integer := 1; + constant A_SECTOR : integer := 2; + constant A_DATA : integer := 3; + + -- Ranges. + subtype BYTE_RANGE is natural range 7 downto 0; -- Number of bits in a byte. + + -- States + type IO_STATES_ENUM is + ( + STATE_IDLE, -- 00 + + STATE_SEARCH, -- 01 + STATE_SEARCH_1, -- 02 + STATE_SEARCH_2, -- 03 + + STATE_CALC_ID_CRC, -- 04 + STATE_CALC_ID_CRC_1, -- 05 + + STATE_WAIT_READ, -- 06 + STATE_WAIT_READ_1, -- 07 + STATE_WAIT_READ_2, -- 08 + + STATE_READ, -- 09 + STATE_READ_1, -- 0A + STATE_READ_2, -- 0B + STATE_READ_3, -- 0C + + STATE_WAIT_WRITE, -- 0D + STATE_WAIT_WRITE_1, -- 0E + STATE_WAIT_WRITE_2, -- 0F + + STATE_WRITE, -- 10 + STATE_WRITE_1, -- 11 + STATE_WRITE_2, -- 12 + + STATE_ABORT, -- 13 + STATE_WAIT, -- 14 + STATE_WAIT_2, -- 15 + STATE_ENDCOMMAND -- 16 + ); + + -- IC host accessible registers. + signal REG_TRACKi : std_logic_vector(BYTE_RANGE); + signal REG_SECTORi : std_logic_vector(BYTE_RANGE); + signal REG_DATAi : std_logic_vector(BYTE_RANGE); + signal REG_STATUSi : std_logic_vector(BYTE_RANGE); + + -- Sector control and manipulation. + signal SECTORS_PER_TRACKi : unsigned(BYTE_RANGE); + signal SECTOR_SIZEi : unsigned(MAX_SECTOR_BITS-1 downto 0); + signal SECTOR_REQUESTi : std_logic; + signal SECTOR_CACHE_ADDRi : unsigned(MAX_SECTOR_BITS-1 downto 0); + signal SECTOR_CACHE_DINi : std_logic_vector(BYTE_RANGE); + signal SECTOR_CACHE_WRENi : std_logic; + signal SECTOR_CACHE_DOUTi : std_logic_vector(BYTE_RANGE); + signal SECTOR_SIZE_CODE : unsigned(1 downto 0); + signal SECTOR_CACHE_WRITEi : std_logic; + signal SECTOR_CACHE_READi : std_logic; + signal SECTOR_CURRENTi : unsigned(BYTE_RANGE); + + -- Track control and manipulation. + signal TRACK_CURRENTi : unsigned(BYTE_RANGE); -- "real" heads position + signal TRACK_NEXTi : unsigned(BYTE_RANGE); + signal TRACK_ZEROi : std_logic; -- Track at position 0. + + -- Command flags, used to enable and direct actions. Either set/reset by host commands or indirectly through command actions. + signal CMD_MULTISECTOR : std_logic; + signal CMD_FORMAT : std_logic; + signal CMD_DISK_RW : std_logic; + signal CMD_STEP_DIRECTION : std_logic; -- last step direction + signal CMD_30MS_DELAY : std_logic; -- 15ms delay prior to command processing. + signal CMD_LAST : std_logic_vector(BYTE_RANGE); -- Last executed command. + + -- Status flags, used for host status query. + signal FLAG_READY : std_logic; -- Controller is ready to process next command. + signal FLAG_READONLY : std_logic; + signal FLAG_CRCERR : std_logic; + signal FLAG_SEEKERR : std_logic; + signal FLAG_INDEX : std_logic; -- mode 1 + signal FLAG_LOSTDATA : std_logic; + signal FLAG_WRFAULT : std_logic; -- mode 2,3 + signal FLAG_HEAD_LOADED : std_logic; + signal FLAG_DATAREAD : std_logic; -- Flag to indicate when data has been read by the host. + signal FLAG_DATAWRITE : std_logic; -- Flag to indicate when data has been written by the host. + + -- DRQ/BUSY are intertwined. + signal DRQ_BUSYi : std_logic_vector(1 downto 0); + signal DRQi : std_logic; + signal BUSYi : std_logic; + signal INTRQi : std_logic; + + -- Disk Control signals. + signal ROTATION_SPEED_300RPMi : std_logic; -- Flag to indicate rotational speed of disk drive. + signal INDEX_PULSE : std_logic; -- Rotational pulse, set after a period of time and cleared to indicate passing of the index hole on the floppy disk. + + -- Read Address command register stack. This comman expects multiple status bytes to be read out from the device so + -- it is realised via a loadable array which is then fed to the host when requested. + type READADDR_TYPE is array(natural range 0 to 9) of std_logic_vector(BYTE_RANGE); + signal READADDR_DOUTi : READADDR_TYPE; + signal DOUTi : std_logic_vector(BYTE_RANGE); + signal ADDRi : std_logic_vector(1 downto 0); + + -- Data transfer variables. + signal XFER_BYTESi : unsigned(MAX_SECTOR_BITS-1 downto 0); -- Number of bytes to transfer in current read/write operation. + signal XFER_BYTES_NEXTi : unsigned(MAX_SECTOR_BITS-1 downto 0); -- Next value of the number of bytes to transfer, lookahead. + + -- Finite State Machine control. + signal IO_FSM_STATE : IO_STATES_ENUM := STATE_IDLE; + + -- Host interface signals. + signal READ_CYCLE : std_logic; -- Host interface Read Cycle in progress. + signal WRITE_CYCLE : std_logic; -- Host interface Write Cycle in progress. + + -- Watchdog + signal WATCHDOG_SETi : std_logic; + signal WATCHDOG_BARKi : std_logic; + signal WATCHDOG_TIMERi : unsigned(15 downto 0); + + -- Timers. + signal TIMER_READi : unsigned(3 downto 0); + signal TIMER_WAITi : integer; + signal TIMER_READY : integer; + + -- I/O Processor signals. + signal IOP_WRENi : std_logic; + signal IOP_CACHE_DINi : std_logic_vector(BYTE_RANGE); + + -- CRC16 signals. + constant CRC_INIT_VALUE : std_logic_vector(15 downto 0) := X"1021"; + signal CRC_CLR : std_logic; + signal CRC_DATA : std_logic_vector(BYTE_RANGE); + signal CRC_ADD : std_logic; + signal CRC_OUT : std_logic_vector(15 downto 0); + signal CRC_CNT : integer range 0 to 9; + signal LFSR : std_logic_vector(15 downto 0) := CRC_INIT_VALUE; + signal LFSN : std_logic_vector(15 downto 0); + +begin + + -- Dual port RAM to act as a sector buffer between the I/O processor read/writing to/from SD and the host accessing the floppy drive. + SECTORBUF : entity work.dpram + generic map ( + init_file => null, + widthad_a => 11, + width_a => 8, + widthad_b => 11, + width_b => 8, + outdata_reg_a => "CLOCK0", + outdata_reg_b => "CLOCK1" + ) + port map ( + clock_a => CLK, + clocken_a => '1', + address_a => IOP_ADDR(10 downto 0), + data_a => IOP_DOUT, + wren_a => IOP_WRENi, + q_a => IOP_CACHE_DINi, + + clock_b => CLK, + clocken_b => '1', + address_b => std_logic_vector(SECTOR_CACHE_ADDRi), + data_b => SECTOR_CACHE_DINi, + wren_b => SECTOR_CACHE_WRENi, + q_b => SECTOR_CACHE_DOUTi + ); + + + SECTOR_SIZEi <= shift_left(to_unsigned(128, SECTOR_SIZEi'length), to_integer(SECTOR_SIZE_CODE)); + FLAG_READONLY <= '1' when WPRT = '1' and (CMD_LAST(7 downto 6) = "10" or CMD_LAST(7 downto 6) = "11") + else '0'; + + TRACK_ZEROi <= '1' when TRACK_CURRENTi = 0 + else '0'; + REG_STATUSi <= not(FLAG_READY) & FLAG_READONLY & FLAG_HEAD_LOADED & FLAG_SEEKERR & FLAG_CRCERR & TRACK_ZEROi & FLAG_INDEX & BUSYi when CMD_LAST(7) = '0' -- Type I command + else + not(FLAG_READY) & FLAG_READONLY & FLAG_WRFAULT & FLAG_SEEKERR & FLAG_CRCERR & FLAG_LOSTDATA & DRQi & BUSYi when CMD_LAST(7) = '1' and unsigned(CMD_LAST) /= X"D" -- Type II/III command + else + "0000000" & BUSYi; -- Type IV command + TRACK_NEXTi <= TRACK_CURRENTi - 1 when (DIN(6) = '1' and DIN(5) = '1') or (DIN(6) = '0' and CMD_STEP_DIRECTION = '1') + else + TRACK_CURRENTi + 1; + XFER_BYTES_NEXTi <= XFER_BYTESi - 1; + WATCHDOG_BARKi <= '1' when WATCHDOG_TIMERi = 0 + else '0'; + DOUTi <= REG_STATUSi when to_integer(unsigned(ADDR)) = A_STATUS + else + REG_TRACKi when to_integer(unsigned(ADDR)) = A_TRACK + else + REG_SECTORi when to_integer(unsigned(ADDR)) = A_SECTOR + else + REG_DATAi when to_integer(unsigned(ADDR)) = A_DATA and IO_FSM_STATE = STATE_IDLE + else + READADDR_DOUTi(to_integer(SECTOR_CACHE_ADDRi(2 downto 0))) when to_integer(unsigned(ADDR)) = A_DATA and CMD_DISK_RW = '0' + else + not SECTOR_CACHE_DOUTi when to_integer(unsigned(ADDR)) = A_DATA and SECTOR_CACHE_READi = '1' and CMD_DISK_RW = '1' and POLARITY = '0' + else + SECTOR_CACHE_DOUTi when to_integer(unsigned(ADDR)) = A_DATA and SECTOR_CACHE_READi = '1' and CMD_DISK_RW = '1' and POLARITY = '1' + else (others => '0'); + DOUT <= DOUTi; + DRQi <= DRQ_BUSYi(1); + DRQ <= DRQi; + BUSYi <= DRQ_BUSYi(0); + BUSY <= BUSYi ; + INTRQ <= INTRQi; + IOP_SECTOR_NO <= REG_SECTORi; + IOP_TRACK_NO <= REG_TRACKi; --std_logic_vector(TRACK_CURRENTi); + IOP_SIDE <= SIDE; + IOP_SECTOR_DIR <= SECTOR_CACHE_WRITEi; + IOP_SECTOR_REQ <= SECTOR_REQUESTi; + IOP_DIN <= IOP_CACHE_DINi when IOP_ADDR(11) = '0' + else + REG_STATUSi when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_STATUS + else + REG_TRACKi when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_TRACK + else + REG_SECTORi when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_SECTOR + else + REG_DATAi when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA + else + CMD_LAST when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+1 + else + READADDR_DOUTi(to_integer(unsigned(IOP_ADDR(2 downto 0)))) when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '1' + else + -- The following are debug output flags, disable in production build. + SECTOR_CACHE_DOUTi when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+2 + else + std_logic_vector(to_unsigned(IO_STATES_ENUM'POS(IO_FSM_STATE), 8)) + when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+3 + else + FLAG_READONLY & FLAG_CRCERR & FLAG_SEEKERR & FLAG_INDEX & FLAG_LOSTDATA & FLAG_WRFAULT & FLAG_DATAREAD & FLAG_DATAWRITE + when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+4 + else + DRQ_BUSYi & CMD_MULTISECTOR & CMD_FORMAT & CMD_DISK_RW & CMD_LAST(7) & CMD_STEP_DIRECTION & FLAG_HEAD_LOADED + when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+5 + else + DISK_TYPE & "00" & POLARITY & WPRT when IOP_ADDR(11) = '1' and IOP_ADDR(4) = '0' and to_integer(unsigned(IOP_ADDR(3 downto 0))) = A_DATA+6 + else (others => '0'); + + -- Data being written to floppy buffer either comes from the host or set to zero if formatting the floppy. + SECTOR_CACHE_DINi <= X"00" when CMD_FORMAT = '1' + else + not DIN when POLARITY = '0' + else DIN; + + -- Enable writing to floppy buffer, this occurs when host makes a write to the DATA register and the buffer write is enabled. The address is setup in the + -- state machine. + SECTOR_CACHE_WRENi <= '1' when WRn = '0' and SECTOR_CACHE_WRITEi = '1' and to_integer(unsigned(ADDR)) = A_DATA + else '0'; + + IOP_WRENi <= '1' when IOP_CSn = '0' and IOP_WRn = '0' + else '0'; + + -- Synchronous process to bring in parameter data from the external host/IO processor. + process(CLK, MRn, DISK_TYPE) + begin + ----------------------- + -- RISING CLOCK EDGE -- + ----------------------- + if rising_edge(CLK) then + + if MRn = '0' then + ROTATION_SPEED_300RPMi <= '1'; + SECTORS_PER_TRACKi <= to_unsigned(9, SECTORS_PER_TRACKi'length); -- 9 x 512 + SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 512 + + else + case to_integer(unsigned(DISK_TYPE(2 downto 0))) is + when 0 => SECTORS_PER_TRACKi <= to_unsigned(16, SECTORS_PER_TRACKi'length); -- 40T DS 16S 256B 320K + when 1 => SECTORS_PER_TRACKi <= to_unsigned(8, SECTORS_PER_TRACKi'length); -- 40T DS 8S 512B 320K + when 2 => SECTORS_PER_TRACKi <= to_unsigned(9, SECTORS_PER_TRACKi'length); -- 80T DS 9S 512B 720K + when 3 => SECTORS_PER_TRACKi <= to_unsigned(10, SECTORS_PER_TRACKi'length); -- 80T DS 10S 512B 800K + when 4 => SECTORS_PER_TRACKi <= to_unsigned(16, SECTORS_PER_TRACKi'length); -- 40T DS 16S 256B 640K + when 5 => SECTORS_PER_TRACKi <= to_unsigned(10, SECTORS_PER_TRACKi'length); -- 35T DS 10S 512B 350K + when 6 => SECTORS_PER_TRACKi <= to_unsigned(16, SECTORS_PER_TRACKi'length); -- 35T DS 16S 256B 280K + when 7 => SECTORS_PER_TRACKi <= to_unsigned(10, SECTORS_PER_TRACKi'length); -- 40T 2H 10S 512B 400K + when others => SECTORS_PER_TRACKi <= to_unsigned(16, SECTORS_PER_TRACKi'length); + end case; + + -- Possible Codes: + -- 0 = 128 + -- 1 = 256 + -- 2 = 512 + -- 3 = 1024 + case to_integer(unsigned(DISK_TYPE(2 downto 0))) is + when 0 => SECTOR_SIZE_CODE <= to_unsigned(1, SECTOR_SIZE_CODE'length); -- 40T DS 16S 256B 320K + when 1 => SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 40T DS 8S 512B 320K + when 2 => SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 80T DS 9S 512B 720K + when 3 => SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 80T DS 10S 512B 800K + when 4 => SECTOR_SIZE_CODE <= to_unsigned(1, SECTOR_SIZE_CODE'length); -- 40T DS 16S 256B 640K + when 5 => SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 35T DS 10S 512B 350K + when 6 => SECTOR_SIZE_CODE <= to_unsigned(1, SECTOR_SIZE_CODE'length); -- 35T DS 16S 256B 280K + when 7 => SECTOR_SIZE_CODE <= to_unsigned(2, SECTOR_SIZE_CODE'length); -- 40T 2H 10S 512B 400K + when others => SECTOR_SIZE_CODE <= to_unsigned(1, SECTOR_SIZE_CODE'length); + end case; + case to_integer(unsigned(DISK_TYPE(2 downto 0))) is + when 0 => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + when 1 => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + when 2 => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + when 3 => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + when 4 => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + when others => ROTATION_SPEED_300RPMi <= '1'; -- 300RPM Disk Speed + end case; + end if; + end if; + end process; + + + -- Watch dog timer. Prevents internal lockup. Set when the host starts a command and if the request is not completed + -- prior to expiry, current command is cancelled and error flags set. + process(CLK, CLKEN, WATCHDOG_SETi) + begin + ----------------------- + -- RISING CLOCK EDGE -- + ----------------------- + if rising_edge(CLK) then + + -- Host access starts the watchdog timer. + if CLKEN = '1' then + if WATCHDOG_SETi = '1' then + WATCHDOG_TIMERi <= to_unsigned(8192, WATCHDOG_TIMERi'length); + + elsif WATCHDOG_TIMERi /= 0 then + WATCHDOG_TIMERi <= WATCHDOG_TIMERi - 1; + end if; + end if; + end if; + end process; + + -- INDEX flag, goes active a fixed period after the I/O Processor indicates it is ready to process data. + -- This simulates a disk turning. When the READY signal is active, a counter counts down and at the end of the + -- count, it sets the INDEX flag and then resets after a fixed period. When not ready, the INDEX flag is cleared. + process(CLK, CLKEN, MRn) + variable CNT : integer; + begin + ----------------------- + -- RISING CLOCK EDGE -- + ----------------------- + if rising_edge(CLK) then + -- Asynchronous RESET is brought into this clock domanin. + if MRn = '0' then + CNT := 0; + INDEX_PULSE <= '0'; + + elsif CLKEN = '1' then + + -- When disk becomes ready, start a counter which simulates the disk rotating. + -- After timer counts down to a final range, set the INDEX_PULSE bit to simulate + -- dection of the disk index hole. If the drive is not ready, then the INDEX_PULSE + -- is always reset. + if FLAG_READY = '1' then + if CNT /= 0 then + CNT := CNT - 1; + else + -- Setup count according to type of disk we are emulating. + -- This allows software which time on the index pulse to be more accurate. + if ROTATION_SPEED_300RPMi = '1' then + CNT := ROTATIONAL_CNT_300RPM; + else + CNT := ROTATIONAL_CNT_360RPM; + end if; + end if; + + -- Index pulse for a fixed period. + if CNT /= 0 and CNT < 100 then + INDEX_PULSE<= '1'; + else + INDEX_PULSE<= '0'; + end if; + else + CNT := 0; + INDEX_PULSE <= '0'; + end if; + end if; + end if; + end process; + + -- CRC16 - Compute next combinational Value + CRC16_COMPUTE: process(LFSR, CRC_DATA) + variable v : std_logic_vector(LFSR'range); + begin + v := LFSR; + for i in BYTE_RANGE loop + v := (v(v'left-1 downto 0) & '0') xor (CRC_INIT_VALUE and (CRC_INIT_VALUE'range => (CRC_DATA(i) xor v(v'left)))); + end loop; + LFSN <= v; + end process; + + -- Remainder Register + CRC16_ADD: process(CLK) + begin + if rising_edge(CLK) then + if CRC_CLR = '1' then + LFSR <= CRC_INIT_VALUE; + elsif CRC_ADD = '1' then + LFSR <= LFSN; + end if; + end if; + CRC_OUT <= LFSR; + end process; + + -- Primary controller. Host side command processor and FSM to execute the commands. + -- + process(CLK, CLKEN, MRn, CSn, RDn, WRn) + variable WR_LASTn : std_logic; -- Signal edge detection. + variable RD_LASTn : std_logic; + begin + ----------------------- + -- RISING CLOCK EDGE -- + ----------------------- + if rising_edge(CLK) then + + -- Asynchronous RESET is brought into this clock domanin. + if(MRn = '0') then + TRACK_CURRENTi <= (others => '0'); + REG_TRACKi <= (others => '0'); + REG_SECTORi <= std_logic_vector(to_unsigned(1, REG_SECTORi'length)); + REG_DATAi <= (others => '0'); + XFER_BYTESi <= (others => '0'); + SECTOR_CACHE_ADDRi <= (others => '0'); + SECTOR_CACHE_READi <= '0'; + SECTOR_CACHE_WRITEi<= '0'; + SECTOR_CURRENTi <= to_unsigned(1, SECTOR_CURRENTi'length); + SECTOR_REQUESTi <= '0'; + IO_FSM_STATE <= STATE_IDLE; + CMD_MULTISECTOR <= '0'; + CMD_FORMAT <= '0'; + CMD_STEP_DIRECTION <= '0'; + CMD_DISK_RW <= '0'; + CMD_30MS_DELAY <= '0'; + FLAG_HEAD_LOADED <= '0'; + CMD_LAST <= (others => '0'); + FLAG_READY <= '0'; + FLAG_DATAREAD <= '0'; + FLAG_DATAWRITE <= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_WRFAULT <= '0'; + FLAG_LOSTDATA <= '0'; + FLAG_INDEX <= '0'; + INTRQi <= '0'; + DRQ_BUSYi <= "00"; + WATCHDOG_SETi <= '0'; + TIMER_READY <= 0; + RD_LASTn := '1'; + WR_LASTn := '1'; + CRC_CLR <= '0'; + CRC_DATA <= (others => '0'); + CRC_ADD <= '0'; + + READ_CYCLE <= '0'; + WRITE_CYCLE <= '0'; + else + -- CRC addition is one controller clock wide. + CRC_ADD <= '0'; + + -- Finite State Machine and internal logic run at the 8MHz FDC clock. + if(CLKEN = '1') then + + -- Disk index detected? Set flag. + if INDEX_PULSE = '1' then + FLAG_INDEX <= '1'; + end if; + + -- READY flag follows the I/O processor state. + if (IOP_READY = '0' and SECTOR_REQUESTi = '0') or RDY = '0' then + FLAG_READY <= '0'; + + elsif IOP_READY = '1' then + FLAG_READY <= '1'; + else + -- An I/O processor sector request is cancelled when IOP_READY goes inactive, signalling the I/O processor has received the interrupt and is processing it. + SECTOR_REQUESTi <= '0'; + end if; + + case (IO_FSM_STATE) is + -- Idle state or buffer to host transfer + when STATE_IDLE => -- do nothing + + -- Read operations interrupt the I/O processor in order to fill the sector cache with the requested sector. Raise the SECTOR_REQUEST flag which will + -- in turn raise an interrupt and set the timeout to sufficient period as to allow the I/O processor to react and process the request. + -- Write operations just delay add the requested delay and then execute the write into cache, interrupting the I/O processor at sector write end. + when STATE_SEARCH => + -- Set timer to 30mS to allow sufficient time for the I/O processor to retrieve a requested sector and for the 30mS delay request. + if CMD_30MS_DELAY = '1' then + TIMER_READY <= DELAY_CNT_30MS; + else + TIMER_READY <= 1000; + end if; + TIMER_READY <= DELAY_CNT_30MS; + + -- Only raise interrupt for read at this state. + if SECTOR_CACHE_WRITEi = '0' then + -- A new SEARCH command sends a request to the I/O processor allowing it sufficient time to setup and read/write the requested sector. + SECTOR_REQUESTi <= '1'; + end if; + IO_FSM_STATE <= STATE_SEARCH_1; + + when STATE_SEARCH_1 => + -- If the I/O processor request has been acknowledged (SECTOR_REQUESTi 1->0) start the delay/timeout. + if SECTOR_REQUESTi = '0' and TIMER_READY /= 0 then + TIMER_READY <= TIMER_READY - 1; + end if; +DETECT AND PROCESS TRACK AND SECTOR OUT OF BOUNDS ERRORS. + -- If the I/O processor becomes ready and the 30mS delay hasnt been requested, terminate delay. + -- if FLAG_READY = '1' and SECTOR_REQUESTi = '0' and CMD_30MS_DELAY = '0' then + -- TIMER_READY <= 0; + -- end if; + + -- At timers expiration check state and forward the FSM according to CMD flags set. + if TIMER_READY = 0 then + -- I/O processor not ready? This shouldnt occur but if the processor is tied up with other requests then error out. + if(FLAG_READY = '0') then + FLAG_SEEKERR <= '1'; + IO_FSM_STATE <= STATE_ENDCOMMAND; + else + SECTOR_CACHE_ADDRi <= (others => '0'); + + -- Sector should be from 1 .. set value unless in READADDRESS mode when it can be 0 due to the SECTOR REG being set to the TRACK REG. + if(CMD_DISK_RW = '1' and (unsigned(REG_SECTORi) = 0 or unsigned(REG_SECTORi) > SECTORS_PER_TRACKi)) then + -- If we are not performing a format operation and the sector is out of bounds, raise a SEEK Error. + if(CMD_FORMAT = '0' and CMD_MULTISECTOR = '0') then + FLAG_SEEKERR <= '1'; + end if; + IO_FSM_STATE <= STATE_ENDCOMMAND; + else + if CMD_DISK_RW = '1' then + IO_FSM_STATE <= STATE_WAIT_READ; + else + IO_FSM_STATE <= STATE_READ; + end if; + end if; + end if; + end if; + + when STATE_SEARCH_2 => + -- if(CMD_DISK_RW = '1' and (SECTOR_CACHE_WRITEi = '1' or CMD_FORMAT = '1')) then + -- IO_FSM_STATE <= STATE_WAIT_READ; + -- elsif(CMD_DISK_RW = '0') then + -- IO_FSM_STATE <= STATE_READ; + -- end if; + + -- ID MFM (A1 A1 A1 FE C H R N CRC1 CRC2) + when STATE_CALC_ID_CRC => + -- Reset clear, CRC now reset. + CRC_CLR <= '0'; + + -- ID field values used are based on the density selected. + if DDEN = '1' then + CRC_CNT <= 0; + else + CRC_CNT <= 3; + end if; + IO_FSM_STATE <= STATE_CALC_ID_CRC_1; + + when STATE_CALC_ID_CRC_1 => + + -- Calculate for the 4 MFM/1 FM bytes plus 4 ID bytes. + if(CRC_CNT > 7) then + READADDR_DOUTi(0) <= READADDR_DOUTi(4); + READADDR_DOUTi(1) <= READADDR_DOUTi(5); + READADDR_DOUTi(2) <= READADDR_DOUTi(6); + READADDR_DOUTi(3) <= READADDR_DOUTi(7); + READADDR_DOUTi(4) <= X"FA"; -- CRC_OUT( 7 downto 0); + READADDR_DOUTi(5) <= X"0C"; --CRC_OUT(15 downto 8); + IO_FSM_STATE <= STATE_SEARCH; + else + CRC_DATA <= READADDR_DOUTi(CRC_CNT); + CRC_CNT <= CRC_CNT + 1; + CRC_ADD <= '1'; + end if; + + -- read before write in case if sector not aligned or smaller than 512b + when STATE_WAIT_READ => + XFER_BYTESi <= SECTOR_SIZEi; + IO_FSM_STATE <= STATE_WAIT_READ_1; + + when STATE_WAIT_READ_1 => + IO_FSM_STATE <= STATE_WAIT_READ_2; + + when STATE_WAIT_READ_2 => + if SECTOR_CACHE_WRITEi = '1' then + IO_FSM_STATE <= STATE_WRITE; + else + IO_FSM_STATE <= STATE_READ; + end if; + + when STATE_READ => + WATCHDOG_SETi <= '1'; + TIMER_READi <= to_unsigned(30, TIMER_READi'length); + IO_FSM_STATE <= STATE_READ_1; + + when STATE_READ_1 => + TIMER_READi <= TIMER_READi - 1; + if(TIMER_READi = 0) then + FLAG_DATAREAD <= '0'; + WATCHDOG_SETi <= '0'; + FLAG_LOSTDATA <= '0'; + DRQ_BUSYi <= "11"; + IO_FSM_STATE <= STATE_READ_2; + end if; + + when STATE_READ_2 => + if(WATCHDOG_BARKi = '1' or (FLAG_DATAREAD = '1' and DRQi = '1')) then + if CMD_DISK_RW = '0' then + READADDR_DOUTi(to_integer(SECTOR_CACHE_ADDRi(2 downto 0))) <= X"55"; + end if; + -- reset drq until next byte is read, nothing is lost + DRQ_BUSYi <= "01"; + FLAG_LOSTDATA <= WATCHDOG_BARKi; + + if(XFER_BYTES_NEXTi = 0) then + -- either read the next sector, or stop if this is track end + if(CMD_MULTISECTOR = '1') then + REG_SECTORi <= std_logic_vector(unsigned(REG_SECTORi) + 1); + IO_FSM_STATE <= STATE_SEARCH; + else + IO_FSM_STATE <= STATE_ENDCOMMAND; + end if; + else + SECTOR_CACHE_ADDRi <= SECTOR_CACHE_ADDRi + 1; + XFER_BYTESi <= XFER_BYTES_NEXTi; + IO_FSM_STATE <= STATE_READ; + end if; + end if; + + when STATE_WAIT_WRITE => + -- IO Processor completed write task, move to next phase. + if FLAG_READY = '1' then + IO_FSM_STATE <= STATE_WAIT_WRITE_1; + + elsif TIMER_READY /= 0 then + TIMER_READY <= TIMER_READY - 1; + -- I/O processor timed out, write fault. + else + FLAG_WRFAULT <= '1'; + IO_FSM_STATE <= STATE_ENDCOMMAND; + end if; + + when STATE_WAIT_WRITE_1 => + IO_FSM_STATE <= STATE_WAIT_WRITE_2; + + when STATE_WAIT_WRITE_2 => + if(CMD_MULTISECTOR = '1') then + REG_SECTORi <= std_logic_vector(unsigned(REG_SECTORi) + 1); + IO_FSM_STATE <= STATE_SEARCH; + else + IO_FSM_STATE <= STATE_ENDCOMMAND; + end if; + + when STATE_WRITE => + WATCHDOG_SETi <= '1'; + TIMER_READi <= to_unsigned(15, TIMER_READi'length); + IO_FSM_STATE <= STATE_WRITE_1; + + when STATE_WRITE_1 => + TIMER_READi <= TIMER_READi - 1; + if(TIMER_READi = 0) then + FLAG_DATAWRITE <= '0'; + WATCHDOG_SETi <= '0'; + FLAG_LOSTDATA <= '0'; + DRQ_BUSYi <= "11"; + IO_FSM_STATE <= STATE_WRITE_2; + end if; + + when STATE_WRITE_2 => + if(WATCHDOG_BARKi = '1' or (FLAG_DATAWRITE = '1' and DRQi = '1')) then + DRQ_BUSYi <= "01"; + FLAG_LOSTDATA <= WATCHDOG_BARKi; + + if(XFER_BYTES_NEXTi = 0) then + IO_FSM_STATE <= STATE_WAIT_WRITE; + + -- Set a timer to wait for the I/O processor to go ready, if it expires then a write fault occurred. + TIMER_READY <= 1000000; + + -- Raise flag to inform I/O processor a data sector is waiting to be read. + SECTOR_REQUESTi <= '1'; + else + SECTOR_CACHE_ADDRi <= SECTOR_CACHE_ADDRi + 1; + XFER_BYTESi <= XFER_BYTES_NEXTi; + IO_FSM_STATE <= STATE_WRITE; + end if; + end if; + + -- Abort current operation ($D0) + when STATE_ABORT => + XFER_BYTESi <= (others => '0'); + FLAG_WRFAULT <= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA <= '0'; + IO_FSM_STATE <= STATE_ENDCOMMAND; + + when STATE_WAIT => + TIMER_WAITi <= 200000; + IO_FSM_STATE <= STATE_WAIT_2; + + when STATE_WAIT_2 => + if(TIMER_WAITi /= 0) then + TIMER_WAITi <= TIMER_WAITi - 1; + else + IO_FSM_STATE <= STATE_ENDCOMMAND; + end if; + + -- End any command. + when STATE_ENDCOMMAND => + CMD_FORMAT <= '0'; + CMD_30MS_DELAY <= '0'; + CMD_MULTISECTOR <= '0'; + CMD_DISK_RW <= '0'; + CMD_STEP_DIRECTION <= '0'; + SECTOR_CACHE_READi <= '0'; + SECTOR_CACHE_WRITEi <= '0'; + IO_FSM_STATE <= STATE_IDLE; + DRQ_BUSYi <= "00"; + INTRQi <= '1'; + + when others => + IO_FSM_STATE <= STATE_ENDCOMMAND; + end case; + + -- Latch address on falling edge of CS+RD/WR as it is used after the signals rise. + if(CSn = '0' and ((RD_LASTn = '1' and RDn = '0') or (WR_LASTn = '1' or WRn = '0'))) then + ADDRi <= ADDR; + end if; + + -- Setup select flags to buffer against host frequency changes and to act on cycle end. + if(CSn = '0' and RDn = '0' and READ_CYCLE = '0') then + READ_CYCLE <= '1'; + end if; + if(CSn = '0' and WRn = '0' and WRITE_CYCLE = '0') then + WRITE_CYCLE <= '1'; + end if; + + -- Register read operations + if(READ_CYCLE = '1' and RDn = '1' and (to_integer(unsigned(ADDRi)) = A_STATUS)) then + INTRQi <= '0'; + FLAG_INDEX <= '0'; + READ_CYCLE <= '0'; + end if; + + -- End of data read + if(READ_CYCLE = '1' and RDn = '1' and (to_integer(unsigned(ADDRi)) = A_DATA)) then + FLAG_DATAREAD <= '1'; + READ_CYCLE <= '0'; + end if; + -- End of data write + if(WRITE_CYCLE = '1' and WRn = '1' and (to_integer(unsigned(ADDRi)) = A_DATA)) then + FLAG_DATAWRITE <= '1'; + WRITE_CYCLE <= '0'; + end if; + + -- Register write operations + if (CSn = '0' and WRn = '0') then + + case (to_integer(unsigned(ADDR))) is + when A_COMMAND => + INTRQi <= '0'; + if (IO_FSM_STATE = STATE_IDLE) or (DIN(7 downto 4) = "1101") then + -- Store this command as the last executed. + CMD_LAST <= DIN; + + -- 16 commands are understood in the WD controller design. Command D (force interrupt) aborts current command if processing. + case DIN(7 downto 4) is + + -- RESTORE + when "0000" => + -- head load as specified, index, track0 + FLAG_HEAD_LOADED<= DIN(3); + FLAG_SEEKERR <= '0'; + REG_TRACKi <= (others => '0'); + TRACK_CURRENTi<= to_unsigned(0, TRACK_CURRENTi'length); + + -- some programs like it when FDC gets busy for a while + DRQ_BUSYi <= "01"; + IO_FSM_STATE <= STATE_WAIT; + + -- SEEK + when "0001" => + -- set real track to datareg + TRACK_CURRENTi<= unsigned(REG_DATAi); + REG_TRACKi <= REG_DATAi; + FLAG_HEAD_LOADED<= DIN(3); + FLAG_SEEKERR <= '0'; + + -- get busy + DRQ_BUSYi <= "01"; + IO_FSM_STATE <= STATE_WAIT; + + -- |- STEP + -- | |- STEP & UPDATE + -- | | |- STEP-IN + -- | | | |- STEP-IN & UPDATE + -- | | | | |- STEP-OUT + -- | | | | | |- STEP-OUT & UPDATE + when "0010" | "0011" | "0100" | "0101" | "0110" | "0111" => + -- if direction is specified, store it for the next time + if (DIN(6) = '1') then + CMD_STEP_DIRECTION <= DIN(5); -- 0: forward/in + end if; + + -- perform step + TRACK_CURRENTi <= TRACK_NEXTi; + + -- update TRACK register too if asked to + if (DIN(4) = '1') then + REG_TRACKi <= std_logic_vector(TRACK_NEXTi); + end if; + + FLAG_HEAD_LOADED<= DIN(3); + FLAG_SEEKERR <= '0'; + + -- some programs like it when FDC gets busy for a while + DRQ_BUSYi <= "01"; + IO_FSM_STATE <= STATE_WAIT; + + -- READ SECTORS WRITE SECTORS + when "1000" | "1001" | "1010" | "1011" => + -- seek data + -- 5: 0: read, 1: write + -- 4: m: 0: one sector, 1: until the track ends + -- 3: S: Side + -- 2: E: add 30ms/15ms(WD1772) delay + -- 1: C: compare side? + -- 0: 0 + + DRQ_BUSYi <= "01"; + FLAG_WRFAULT <= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA <= '0'; + FLAG_HEAD_LOADED<= '0'; + + SECTOR_CACHE_READi <= not(DIN(5)); -- Read Sector 5 = 0 + SECTOR_CACHE_WRITEi<= DIN(5); -- Write Sector 5 = 1 + + CMD_FORMAT <= '0'; --DIN(6); + CMD_MULTISECTOR<= DIN(4); + CMD_DISK_RW <= '1'; + CMD_30MS_DELAY<= DIN(2); + FLAG_DATAWRITE<= '0'; + FLAG_DATAREAD <= '0'; + IO_FSM_STATE <= STATE_SEARCH; + + -- Detect write to protected disk and abort. + if(FLAG_READONLY = '1' and DIN(5) = '1') then + FLAG_WRFAULT <= '1'; + IO_FSM_STATE <= STATE_WAIT; + end if; + + -- READ ADDRESS - Read details from the disk and return as follows. + -- 0 : Track Addr + -- 1 : Side Number + -- 2 : Sector Addr + -- 3 : Sector Length + -- 4 : CRC 1 + -- 5 : CRC 2 + when "1100" => + -- track, side, sector, sector size code, 2-byte checksum (crc?) + DRQ_BUSYi <= "01"; + FLAG_WRFAULT <= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA <= '0'; + FLAG_HEAD_LOADED<= '0'; + + SECTOR_CACHE_WRITEi<= '0'; + + CMD_FORMAT <= '0'; + CMD_MULTISECTOR<= '0'; + CMD_DISK_RW <= '0'; -- Not a physical disk read/write operation, read from internal registers. + CMD_30MS_DELAY<= DIN(2); + FLAG_DATAREAD <= '0'; + XFER_BYTESi <= to_unsigned(6, XFER_BYTESi'length); + + -- Internal register stack is loaded up with required ID information, a CRC is calculated on the ID and placed into the ID + -- and this is then fed to the host in the same way as disk data, ie. read multiple bytes from the DATA register. + READADDR_DOUTi(0) <= X"A1"; + READADDR_DOUTi(1) <= X"A1"; + READADDR_DOUTi(2) <= X"A1"; + READADDR_DOUTi(3) <= X"FE"; + READADDR_DOUTi(4) <= std_logic_vector(REG_TRACKi); + READADDR_DOUTi(5) <= "0000000" & SIDE; + READADDR_DOUTi(6) <= std_logic_vector(REG_SECTORi); --SECTOR_CURRENTi); + READADDR_DOUTi(7) <= "000000" & std_logic_vector(SECTOR_SIZE_CODE); + READADDR_DOUTi(8) <= (others => '0'); + READADDR_DOUTi(9) <= (others => '0'); + + if(SECTOR_CURRENTi >= SECTORS_PER_TRACKi) then + SECTOR_CURRENTi<= to_unsigned(1, SECTOR_CURRENTi'length); + else + SECTOR_CURRENTi<= SECTOR_CURRENTi + 1; + end if; + REG_SECTORi <= std_logic_vector(REG_TRACKi); + -- SECTOR_CACHE_ADDRi <= (others => '0'); + CRC_CLR <= '1'; + IO_FSM_STATE <= STATE_CALC_ID_CRC; + + -- FORCED INTERRUPT + when "1101" => + if(IO_FSM_STATE /= STATE_IDLE) then + IO_FSM_STATE<= STATE_ABORT; + else + FLAG_WRFAULT <= '0'; + FLAG_HEAD_LOADED<= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA<= '0'; + DRQ_BUSYi <= "00"; + end if; + + -- READ TRACK + when "1110" => + if(DIN(6) = '1') then + REG_SECTORi <= std_logic_vector(to_unsigned(1, REG_SECTORi'length)); + end if; + + CMD_30MS_DELAY<= DIN(2); + FLAG_WRFAULT <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA <= '0'; + FLAG_SEEKERR <= '1'; + FLAG_HEAD_LOADED<= '0'; + DRQ_BUSYi <= "01"; + IO_FSM_STATE <= STATE_WAIT; + + -- WRITE TRACK + when "1111" => + CMD_30MS_DELAY<= DIN(2); + FLAG_WRFAULT <= '0'; + FLAG_SEEKERR <= '0'; + FLAG_CRCERR <= '0'; + FLAG_LOSTDATA <= '0'; + FLAG_HEAD_LOADED<= '0'; + DRQ_BUSYi <= "01"; + IO_FSM_STATE <= STATE_WAIT; + + when others => + end case; + end if; + + when A_TRACK => + if (BUSYi = '0') then + REG_TRACKi <= DIN; + end if; + when A_SECTOR => + if (BUSYi = '0') then + -- SECTOR_CURRENTi <= unsigned(DIN); + REG_SECTORi <= DIN; + end if; + when A_DATA => + REG_DATAi <= DIN; + when others => + end case; + end if; + + -- Edge detection. + WR_LASTn := WRn; + RD_LASTn := RDn; + end if; -- CLKEN + end if; -- MR RESET + end if; -- rising_edge(CLK) + end process; +end rtl; diff --git a/FPGA/SW700/v1.3/emuMZ/z8420/Interrupt_v2.vhd b/FPGA/SW700/v1.3/emuMZ/z8420/Interrupt_v2.vhd new file mode 100644 index 0000000..814552e --- /dev/null +++ b/FPGA/SW700/v1.3/emuMZ/z8420/Interrupt_v2.vhd @@ -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; diff --git a/FPGA/SW700/v1.3/emuMZ/z8420/z8420_v2.vhd b/FPGA/SW700/v1.3/emuMZ/z8420/z8420_v2.vhd new file mode 100644 index 0000000..52e21f6 --- /dev/null +++ b/FPGA/SW700/v1.3/emuMZ/z8420/z8420_v2.vhd @@ -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 - 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 . +--------------------------------------------------------------------------------------------------------- +-- +-- 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; diff --git a/software/asm/mz2000_ipl.asm b/software/asm/mz2000_ipl.asm index 96e88b9..d09dde9 100644 --- a/software/asm/mz2000_ipl.asm +++ b/software/asm/mz2000_ipl.asm @@ -53,45 +53,46 @@ ; 0xF6 = Bit 4 Graphics Display on CRT (H), 2:0 colour VRAM enable to Colour CRT / CRT (if enabled). ; 0xF7 = Selection of VRAM bank in memory map when enabled, 0 = None, 1 = Blue, 2 = Red, 3 = Green ; -FDC EQU 0D8h ; MB8866 IO Region 0D8h - 0DBh -FDC_CR EQU FDC + 000h ; Command Register -FDC_STR EQU FDC + 000h ; Status Register -FDC_TR EQU FDC + 001h ; Track Register -FDC_SCR EQU FDC + 002h ; Sector Register -FDC_DR EQU FDC + 003h ; Data Register -FDC_MOTOR EQU FDC + 004h ; DS[0-3] and Motor control. 4 drives DS= BIT 0 -> Bit 2 = Drive number, 2=1,1=0,0=0 DS0, 2=1,1=0,0=1 DS1 etc - ; bit 7 = 1 MOTOR ON LOW (Active) -FDC_SIDE EQU FDC + 005h ; Side select, Bit 0 when set = SIDE SELECT LOW -PPIA EQU 0E0H ; 8255 Port A -PPIB EQU 0E1H ; 8255 Port B -PPIC EQU 0E2H ; 8255 Port C -PPICTL EQU 0E3H ; 8255 Control Port -PIOA EQU 0E8H ; Z80 PIO Port A -PIOCTLA EQU 0E9H ; Z80 PIO Port A Control Port -PIOB EQU 0EAH ; Z80 PIO Port B -PIOCTLB EQU 0EBH ; Z80 PIO Port B Control Port -CRTBKCOLR EQU 0F4H ; Configure external CRT background colour. -CRTGRPHPRIO EQU 0F5H ; Graphics priority register, character or a graphics colour has front display priority. -CRTGRPHSEL EQU 0F6H ; Graphics output select on CRT or external CRT -GRAMCOLRSEL EQU 0F7H ; Graphics RAM colour bank select. -GRAMADDRL EQU 0C000H ; Graphics RAM lower address. -IBUFE EQU 0CF00H ; Cassette header start. -FDNAME EQU 0CF07H ; Floppy disk name when floppy sector read.(ie after type+IPLPRO). -ATRB EQU 0CF00H ; Tape attribute byte. -NAME EQU 0CF01H ; Tape image name. -SIZE EQU 0CF12H ; Tape image size. -DTADR EQU 0CF14H ; Tape image load address. -SUMDT EQU 0FFE0H ; Check sum data. -TMCNT EQU 0FFE2H ; -IBADR1 EQU 0CF00H ; +FDC EQU 0D8h ; MB8866 IO Region 0D8h - 0DBh +FDC_CR EQU FDC + 000h ; Command Register +FDC_STR EQU FDC + 000h ; Status Register +FDC_TR EQU FDC + 001h ; Track Register +FDC_SCR EQU FDC + 002h ; Sector Register +FDC_DR EQU FDC + 003h ; Data Register +FDC_MOTOR EQU FDC + 004h ; DS[0-3] and Motor control. 4 drives DS= BIT 0 -> Bit 2 = Drive number, 2=1,1=0,0=0 DS0, 2=1,1=0,0=1 DS1 etc + ; bit 7 = 1 MOTOR ON LOW (Active) +FDC_SIDE EQU FDC + 005h ; Side select, Bit 0 when set = SIDE SELECT LOW, +FDC_DDEN EQU FDC + 006h ; Double density enable, 0 = double density, 1 = single density disks. +PPIA EQU 0E0H ; 8255 Port A +PPIB EQU 0E1H ; 8255 Port B +PPIC EQU 0E2H ; 8255 Port C +PPICTL EQU 0E3H ; 8255 Control Port +PIOA EQU 0E8H ; Z80 PIO Port A +PIOCTLA EQU 0E9H ; Z80 PIO Port A Control Port +PIOB EQU 0EAH ; Z80 PIO Port B +PIOCTLB EQU 0EBH ; Z80 PIO Port B Control Port +CRTBKCOLR EQU 0F4H ; Configure external CRT background colour. +CRTGRPHPRIO EQU 0F5H ; Graphics priority register, character or a graphics colour has front display priority. +CRTGRPHSEL EQU 0F6H ; Graphics output select on CRT or external CRT +GRAMCOLRSEL EQU 0F7H ; Graphics RAM colour bank select. +GRAMADDRL EQU 0C000H ; Graphics RAM lower address. +IBUFE EQU 0CF00H ; Cassette header start. +FDNAME EQU 0CF07H ; Floppy disk name when floppy sector read.(ie after type+IPLPRO). +ATRB EQU 0CF00H ; Tape attribute byte. +NAME EQU 0CF01H ; Tape image name. +SIZE EQU 0CF12H ; Tape image size. +DTADR EQU 0CF14H ; Tape image load address. +SUMDT EQU 0FFE0H ; Check sum data. +TMCNT EQU 0FFE2H ; +IBADR1 EQU 0CF00H ; IBADR2 EQU 08000H -NTRACK EQU 0FFE0H ; Track number. -NSECT EQU 0FFE1H ; Sector number. -BSIZE EQU 0FFE2H ; Size of file to read. -STTR EQU 0FFE4H ; Starting track. -STSE EQU 0FFE5H ; Starting sector. -MTFG EQU 0FFE6H ; Motor flag. -CLBF0 EQU 0FFE7H ; +NTRACK EQU 0FFE0H ; Track number. +NSECT EQU 0FFE1H ; Sector number. +BSIZE EQU 0FFE2H ; Size of file to read. +STTR EQU 0FFE4H ; Starting track. +STSE EQU 0FFE5H ; Starting sector. +MTFG EQU 0FFE6H ; Motor flag. +CLBF0 EQU 0FFE7H ; CLBF1 EQU 0FFE8H CLBF2 EQU 0FFE9H CLBF3 EQU 0FFEAH @@ -116,149 +117,149 @@ RESET: JR START ; ; NST RESET - Toggling ; -NST: LD A,003H ; Set PC1 NST=1, this toggles the memory into the normal state and resets the CPU thus executing from 0000 in RAM BANK 1 +NST: LD A,003H ; Set PC1 NST=1, this toggles the memory into the normal state and resets the CPU thus executing from 0000 in RAM BANK 1 OUT (PPICTL),A ; -START: LD A,082H ; 8255 A=OUT B=IN C=OUT +START: LD A,082H ; 8255 A=OUT B=IN C=OUT OUT (PPICTL),A - LD A,058H ; BST=1 NST=0 OPEN=1 WRITE=1 + LD A,058H ; BST=1 NST=0 OPEN=1 WRITE=1 OUT (PPIC),A - LD SP,SUMDT ; Setup stack + LD SP,SUMDT ; Setup stack LD A,0F7H - OUT (PPIA),A ; All signals inactive, stop cassette. + OUT (PPIA),A ; All signals inactive, stop cassette. LD A,00FH - OUT (PIOCTLA),A ; Setup PIO A + OUT (PIOCTLA),A ; Setup PIO A LD A,0CFH - OUT (PIOCTLB),A ; Setup PIO B + OUT (PIOCTLB),A ; Setup PIO B LD A,0FFH OUT (PIOCTLB),A ; ; tranZPUter build ensure the FPGA and CPLD are set to the correct initial operating mode. IF BUILDVERSION = 1 - LD A,VMMODE_MZ2000 ; Ensure correct video mode is selected in FPGA. + LD A,VMMODE_MZ2000 ; Ensure correct video mode is selected in FPGA. OUT (VMCTRL),A LD A,MODE_MZ2000|MODE_VIDEO_FPGA|SYSMODE_MZ2000|MODE_RESET_PRESERVE ;LD A,MODE_MZ2000|SYSMODE_MZ2000|MODE_RESET_PRESERVE OUT (CPLDCFG),A ENDIF ; - XOR A ; Set Graphics VRAM to default, no access to GRAM. - OUT (CRTGRPHSEL),A - OUT (CRTBKCOLR),A ; Set background colour on external output to black. - INC A - OUT (GRAMCOLRSEL),A ; Activate Blue graphics RAM bank for graphics operations (this is the default installed bank, red and green are optional). - LD A,007H - OUT (CRTGRPHPRIO),A ; Enable all colour bank graphic output with character priority. - LD A,0D3H - OUT (PIOA),A ; 11010011 = VRAM paged in for characters, 40 char mode, keyboard strobe bank 3. - ; A7 : H 0xD000:0xD7FF or 0xC000:0xFFFF VRAN paged in. - ; A6 : H Select Character VRAM (H) or Graphics VRAM (L) - ; A5 : H Select 80 char mode, 40 char mode = L - ; A4 : L Select all key strobe lines active, for detection of any key press. - ; A3-A0: Keyboard strobe lines - LD HL,0D000H - LD A,0D8H ; Set to character VRAM top address. -CLEAR: LD (HL),000H ; DISPLAY CLEAR - INC HL - CP H - JR NZ,CLEAR - LD A,0FFH ; Disable all PPI A signals. - ; A7 : L APSS Search for next program - ; A6 : L Automatic playback at end of rewind - ; A5 : L Automatic rewind during playback(recording) - ; A4 : L Invert Video - ; A3 : L Stop Cassette - ; A2 : L Play Cassette - ; A1 : L Fast Forward - ; A0 : L Rewind - OUT (PPIA),A - LD A,003H ; Select GREEN graphics RAM bank. - OUT (GRAMCOLRSEL),A - CALL FILLVRAM ; Clear it. - LD A,002H ; Select RED graphics RAM bank. - OUT (GRAMCOLRSEL),A - CALL FILLVRAM - LD A,001H ; Select BLUE graphics RAM bank. - OUT (GRAMCOLRSEL),A - CALL FILLVRAM - LD A,013H ; 00010011 = Disable VRAM (select RAM), keystrobe bank 3. - OUT (PIOA),A - XOR A ; Clear control variables. + XOR A ; Set Graphics VRAM to default, no access to GRAM. + OUT (CRTGRPHSEL),A + OUT (CRTBKCOLR),A ; Set background colour on external output to black. + INC A + OUT (GRAMCOLRSEL),A ; Activate Blue graphics RAM bank for graphics operations (this is the default installed bank, red and green are optional). + LD A,007H + OUT (CRTGRPHPRIO),A ; Enable all colour bank graphic output with character priority. + LD A,0D3H + OUT (PIOA),A ; 11010011 = VRAM paged in for characters, 40 char mode, keyboard strobe bank 3. + ; A7 : H 0xD000:0xD7FF or 0xC000:0xFFFF VRAN paged in. + ; A6 : H Select Character VRAM (H) or Graphics VRAM (L) + ; A5 : H Select 80 char mode, 40 char mode = L + ; A4 : L Select all key strobe lines active, for detection of any key press. + ; A3-A0: Keyboard strobe lines + LD HL,0D000H + LD A,0D8H ; Set to character VRAM top address. +CLEAR: LD (HL),000H ; DISPLAY CLEAR + INC HL + CP H + JR NZ,CLEAR + LD A,0FFH ; Disable all PPI A signals. + ; A7 : L APSS Search for next program + ; A6 : L Automatic playback at end of rewind + ; A5 : L Automatic rewind during playback(recording) + ; A4 : L Invert Video + ; A3 : L Stop Cassette + ; A2 : L Play Cassette + ; A1 : L Fast Forward + ; A0 : L Rewind + OUT (PPIA),A + LD A,003H ; Select GREEN graphics RAM bank. + OUT (GRAMCOLRSEL),A + CALL FILLVRAM ; Clear it. + LD A,002H ; Select RED graphics RAM bank. + OUT (GRAMCOLRSEL),A + CALL FILLVRAM + LD A,001H ; Select BLUE graphics RAM bank. + OUT (GRAMCOLRSEL),A + CALL FILLVRAM + LD A,013H ; 00010011 = Disable VRAM (select RAM), keystrobe bank 3. + OUT (PIOA),A + XOR A ; Clear control variables. LD (DRINO),A LD (MTFG),A ; Boot time function selection key processing. KEYIN: IF BUILDVERSION = 1 CALL KEYS1 - BIT 6,A ; F - Floppy disk + BIT 6,A ; F - Floppy disk JP Z,FD - BIT 3,A ; C - Cassette. - JR Z,CMT - BIT 0,A ; / - Boot external rom. - JP Z,EXROMT + BIT 3,A ; C - Cassette. + JR Z,CMT + BIT 0,A ; / - Boot external rom. + JP Z,EXROMT LD B,016H CALL KEYS - BIT 4,A ; T - TZFS Monitor + BIT 4,A ; T - TZFS Monitor JP Z,TZFS - BIT 6,A ; V - Verification Test + BIT 6,A ; V - Verification Test JP Z,VTEST LD B,010H CALL KEYS - BIT 0,A ; F1 + BIT 0,A ; F1 JP Z,FUNC_F1 - BIT 1,A ; F2 + BIT 1,A ; F2 JP Z,FUNC_F2 - BIT 2,A ; F3 + BIT 2,A ; F3 JP Z,FUNC_F3 - BIT 3,A ; F4 + BIT 3,A ; F4 JP Z,FUNC_F4 - BIT 4,A ; F5 + BIT 4,A ; F5 JP Z,FUNC_F5 - BIT 5,A ; F6 + BIT 5,A ; F6 JP Z,FUNC_F6 - BIT 6,A ; F7 + BIT 6,A ; F7 JP Z,FUNC_F7 - BIT 7,A ; F8 + BIT 7,A ; F8 JP Z,FUNC_F8 ELSE - CALL KEYS1 ; Get a key to decide on first action. - BIT 3,A ; C - Cassette. - JR Z,CMT - BIT 0,A ; / - Boot external rom. - JP Z,EXROMT + CALL KEYS1 ; Get a key to decide on first action. + BIT 3,A ; C - Cassette. + JR Z,CMT + BIT 0,A ; / - Boot external rom. + JP Z,EXROMT ENDIF - JR NKIN ; No selection, so standard startup, try FDC then CMT. - -FILLVRAM: LD HL,GRAMADDRL ; Base of Video Graphics RAM in address space. - DI - IN A,(PIOA) ; Read the PIO register and enable Graphics RAM - SET 7,A - RES 6,A - OUT (PIOA),A + JR NKIN ; No selection, so standard startup, try FDC then CMT. + +FILLVRAM: LD HL,GRAMADDRL ; Base of Video Graphics RAM in address space. + DI + IN A,(PIOA) ; Read the PIO register and enable Graphics RAM + SET 7,A + RES 6,A + OUT (PIOA),A LD DE,GRAMADDRL + 1 LD (HL),000H LD BC,03E7FH L0082: LDIR - RES 7,A ; Read the PIO and disable Video/Graphics RAM (use normal RAM). + RES 7,A ; Read the PIO and disable Video/Graphics RAM (use normal RAM). SET 6,A OUT (PIOA),A EI RET -KEYS1: LD B,014H ;Preserve A4-A7, set A4 to prevent all strobes low, then select line 5 (0-4). -KEYS: IN A,(PIOA) - AND 0F0H - OR B - OUT (PIOA),A - IN A,(PIOB) ;Read the strobed key. +KEYS1: LD B,014H ;Preserve A4-A7, set A4 to prevent all strobes low, then select line 5 (0-4). +KEYS: IN A,(PIOA) + AND 0F0H + OR B + OUT (PIOA),A + IN A,(PIOB) ;Read the strobed key. RET NKIN: IF BUILDVERSION = 1 - JP TRYAG ; Straight to the menu, dont auto start FD or cassette. + JP TRYAG ; Straight to the menu, dont auto start FD or cassette. ELSE - CALL FDCC ; FD available? - JP Z,FD ; Yes, try and boot from floppy. - JR CMT ; No, boot from CMT. + CALL FDCC ; FD available? + JP Z,FD ; Yes, try and boot from floppy. + JR CMT ; No, boot from CMT. ENDIF ; @@ -274,9 +275,9 @@ FDCC: LD A,0A5H CP B RET ; - ; ; - ; CMT CONTROL ; - ; ; + ; ; + ; CMT CONTROL ; + ; ; ; CMT: CALL MOTORSTOP CALL KYEMES @@ -292,10 +293,10 @@ CMT: CALL MOTORSTOP JR NZ,MISMCH CALL RDDAT ST1: PUSH AF - CALL FR ; Rewind tape. + CALL FR ; Rewind tape. POP AF JP C,TRYAG - JP NST ; Set to normal memory mode and restart at 0x0000 + JP NST ; Set to normal memory mode and restart at 0x0000 MISMCH: LD HL,MES16 LD E,00AH @@ -346,7 +347,7 @@ RTAPE: PUSH DE PUSH HL LD H,002H RTP2: CALL SPDIN - JR C,TRTN1 ; BREAK + JR C,TRTN1 ; BREAK JR Z,RTP2 LD D,H LD HL,00000H @@ -389,26 +390,26 @@ TRTN1: POP HL EDGE: IN A,(PPIB) CPL RLCA - RET C ; BREAK + RET C ; BREAK RLCA - JR NC,EDGE ; WAIT ON LOW + JR NC,EDGE ; WAIT ON LOW L016A: IN A,(PPIB) CPL RLCA - RET C ; BREAK + RET C ; BREAK RLCA - JR C,L016A ; WAIT ON HIGH + JR C,L016A ; WAIT ON HIGH RET ; 1 BYTE READ ; DATA=A ; SUMDT STORE RBYTE: PUSH HL - LD HL,00800H ; 8 BITS + LD HL,00800H ; 8 BITS RBY1: CALL SPDIN - JR C,RBY3 ; BREAK - JR Z,RBY2 ; BIT=0 + JR C,RBY3 ; BREAK + JR Z,RBY2 ; BIT=0 PUSH HL - LD HL,(SUMDT) ;CHECKSUM + LD HL,(SUMDT) ;CHECKSUM INC HL LD (SUMDT),HL POP HL @@ -443,10 +444,10 @@ TM3: CALL SPDIN CALL EDGE JR RBY3 -SPDIN: CALL EDGE ;WAIT ON HIGH - RET C ;BREAK +SPDIN: CALL EDGE ;WAIT ON HIGH + RET C ;BREAK CALL DLY2 - IN A,(PPIB) ;READ BIT + IN A,(PPIB) ;READ BIT AND 040H RET ; @@ -487,11 +488,11 @@ MOTORSTOP: LD A,0F7H OUT (PPIA),A RET ;EJECT -OPEN: LD A,008H ;Reset PC4 - EJECT activate +OPEN: LD A,008H ;Reset PC4 - EJECT activate OUT (PPICTL),A CALL DEL6 LD A,009H - OUT (PPICTL),A ;Set PC4 - Deactivate EJECT + OUT (PPICTL),A ;Set PC4 - Deactivate EJECT RET KYEMES: LD HL,MES3 @@ -547,33 +548,33 @@ DLY2A: DEC A JP NZ,DLY2A RET -LDMSG: LD HL,MES1 ; Address of string to display. - LD E,000H ; Offset into first 256 characters of screen for message display. - LD C,00EH ; Number of characters to display. - JR DISP +LDMSG: LD HL,MES1 ; Address of string to display. + LD E,000H ; Offset into first 256 characters of screen for message display. + LD C,00EH ; Number of characters to display. + JR DISP DISP2: LD A,0D3H OUT (PIOA),A - JR DISP1 + JR DISP1 BOOTER: LD HL,MES8 LD E,00AH LD C,00DH -DISP: LD A,0D3H ; Page in character video ram. +DISP: LD A,0D3H ; Page in character video ram. OUT (PIOA),A EXX - LD HL,0D000H ; Clear screen, entire 0xD000:0xDFFF area - LD A,0D8H ; Top of character VRAM. + LD HL,0D000H ; Clear screen, entire 0xD000:0xDFFF area + LD A,0D8H ; Top of character VRAM. DISP3: LD (HL),000H INC HL CP H - JR NZ,DISP3 ; When H moves to 0x0E then complete. + JR NZ,DISP3 ; When H moves to 0x0E then complete. EXX DISP1: XOR A LD B,A - LD D,0D0H ; E points to offset in 0xD000 area for message display, given by caller. - LDIR ; HL set by caller to message string. C set to string length, B always 0. - LD A,013H ; Restore memory, page out video ram. + LD D,0D0H ; E points to offset in 0xD000 area for message display, given by caller. + LDIR ; HL set by caller to message string. C set to string length, B always 0. + LD A,013H ; Restore memory, page out video ram. OUT (PIOA),A RET @@ -581,21 +582,21 @@ DISP1: XOR A ; Print method with full screen addressable. ; PRTSTR: IF BUILDVERSION = 1 - LD A,0D3H ; Enable VRAM. + LD A,0D3H ; Enable VRAM. OUT (PIOA),A - LD A,(HL) ; HL points to destination address and null terminated string. + LD A,(HL) ; HL points to destination address and null terminated string. LD E,A INC HL LD A,(HL) LD D,A PRTSTR1: INC HL - LD A,(HL) ; Copy from source (HL) to destination (DE) until a null byte is encountered. + LD A,(HL) ; Copy from source (HL) to destination (DE) until a null byte is encountered. OR A JR Z,PRTSTR2 LD (DE),A INC DE JR PRTSTR1 -PRTSTR2: LD A,013H ; Restore memory, page out video ram. +PRTSTR2: LD A,013H ; Restore memory, page out video ram. OUT (PIOA),A RET ENDIF @@ -673,6 +674,17 @@ MESF8: DW 0D263H ; RETRY 0FFEBH ; DRINO 0FFECH ; + ; DRIVE NO=DRINO (0-3) + ; + ; CASE OF SEQUENTIAL READ + ; DRIVE NO=DRINO (0-3) + ; BYTE SIZE =IY+2,3 + ; ADDRESS =IX+0,1 + ; NEXT TRACK =IY+0 + ; NEXT SECTOR =IY+1 + ; START TRACK =IY+4 + ; START SECTOR =IY+5 + ; ; Floppy record track 1, sector 1: ; 0x00 Attribute (1 = machine code). ; 0x01 : 0x06 Disk indicator, IPLPRO for bootable disks. @@ -686,30 +698,30 @@ MESF8: DW 0D263H ;FD FD: LD IX,IBUFE XOR A - LD (0CF1EH),A ; Image load location. + LD (0CF1EH),A ; Image load location. LD (0CF1FH),A LD IY,SUMDT - LD HL,0100H ; Load one 256 byte sector - the disk record. + LD HL,0100H ; Load one 256 byte sector - the disk record. LD (IY+002H),L LD (IY+003H),H - CALL BREAD ;INFORMATION INPUT - LD HL,IBUFE ;MASTER CHECK + CALL BREAD ; Read the first sector to verify if it is a valid disk. + LD HL,IBUFE ; Perform a MASTER CHECK, ie. code 01 + IPLPRO LD DE,IPLMC LD B,006H -MCHECK: LD C,(HL) ; Should be 0x1 and IPLPRO at beginning of first track to indicate bootable disk. +MCHECK: LD C,(HL) ; Should be 0x1 and IPLPRO at beginning of first track to indicate bootable disk. LD A,(DE) CP C JP NZ,NMASTE INC HL INC DE - DJNZ MCHECK - CALL LDMSG ; Ok, so bootable, indicate loading. - LD HL,FDNAME ; Offset into first sector for name of file. + DJNZ MCHECK + CALL LDMSG ; Ok, so bootable, indicate loading. + LD HL,FDNAME ; Offset into first sector for name of file. LD E,010H LD C,00AH - CALL DISP2 ; Display Floppy image name. + CALL DISP2 ; Display Floppy image name. LD IX,IBADR2 - LD HL,(0CF14H) ; Setup size of image load. + LD HL,(0CF14H) ; Setup size of image load. LD (IY+002H),L LD (IY+003H),H CALL BREAD @@ -724,67 +736,67 @@ NODISK: LD HL,MES9 ; ; READY CHECK ; -READY: LD A,(MTFG) +READY: LD A,(MTFG) ; Motor flag RRCA - CALL NC,MTON - LD A,(DRINO) ;DRIVE NO GET - OR 084H - OUT (FDC_MOTOR),A ;DRIVE SELECT MOTON + CALL NC,MTON ; Check if motor is already running (cant read hardware so maintain in memory mirror flag). + LD A,(DRINO) ; Get drive number. + OR 084H ; Ensure the motor on and high select bit are set (Drives 0-3 are represented in hardware as 4-7). + OUT (FDC_MOTOR),A ; Send to motor/drive select register. XOR A - CALL DLY60M + CALL DLY60M ; Small delay to allow the motor to spin up etc. LD HL,00000H -REDY0: DEC HL +REDY0: DEC HL ; Delay loop, 65536 loops checking for the BUSY flag to be reset. LD A,H OR L - JR Z,NODISK - IN A,(FDC_CR) - CPL + JR Z,NODISK + IN A,(FDC_CR) ; Get controller statue. + CPL ; Data bus is inverted on original Sharp controller. RLCA - JR C,REDY0 - LD A,(DRINO) + JR C,REDY0 ; Bit set, controller busy, loop. No new command can be issued whilst the controller is busy. + LD A,(DRINO) LD C,A - LD HL,CLBF0 + LD HL,CLBF0 ; Get correct control block flag for selected drive. LD B,000H ADD HL,BC - BIT 0,(HL) + BIT 0,(HL) ; Check to see if drive calibrated. Exit if it has been calibrated. RET NZ - CALL RCLB + CALL RCLB ; Else call calibration, set flag and exit. SET 0,(HL) RET ; ; MOTOR ON ; -MTON: LD A,080H +MTON: LD A,080H ; Spin up motor by enabling motor control. OUT (FDC_MOTOR),A - LD B,00AH ;1SEC DELAY + LD B,00AH ; Wait 1 second for spinup. MTD1: LD HL,03C19H MTD2: DEC HL LD A,L OR H - JR NZ,MTD2 - DJNZ MTD1 - LD A,001H + JR NZ,MTD2 + DJNZ MTD1 + LD A,001H ; Set flag to indicate motor running. LD (MTFG),A RET ; ; Track SEEK ; -SEEK: LD A,01BH +SEEK: LD A,01BH ; Send seek command to controller, the previous stored track will be searched. CPL OUT (FDC_CR),A - CALL BUSY + CALL BUSY ; Wait until the BUSY flag is cleared. CALL DLY60M - IN A,(FDC_STR) + IN A,(FDC_STR) ; Get status for caller to inspect. CPL - AND 099H + AND 099H ; Mask Nit Ready, Seek Error, CRC Error, Busy flags, any set the error occurred. RET ; ;MOTOR OFF ; -MOFF: CALL DLY1M - XOR A +MOFF: CALL DLY1M ; Delay 1ms to allow previous commands to complete. + XOR A ; Clear the motor (and drive select) flags. OUT (FDC_MOTOR),A - LD (CLBF0),A + LD (CLBF0),A ; Clear all disk control block flags. LD (CLBF1),A LD (CLBF2),A LD (CLBF3),A @@ -793,41 +805,41 @@ MOFF: CALL DLY1M ; ; RECALIBRATION ; -RCLB: PUSH HL +RCLB: PUSH HL ; Recalibration is made by sending the RESTORE command to the controller. LD A,00BH CPL OUT (FDC_CR),A - CALL BUSY + CALL BUSY ; Wait until the BUSY flag is reset. CALL DLY60M - IN A,(FDC_STR) + IN A,(FDC_STR) ; Inspect status, TRACK 0 flag should be set. CPL AND 085H XOR 004H POP HL - RET Z - JP ERR + RET Z ; If track 0 flag set, exit. + JP ERR ; Otherwise display error message as track 0 cannot be located, ie. no disk or corruption. ; ; BUSY AND WAIT ; -BUSY: PUSH DE - PUSH HL +BUSY: PUSH DE ; Loop 65536 x 7 times for the BUSY flag in the controller to be reset. + PUSH HL ; Normally a command would have been sent and this method called. CALL DLY80U LD E,007H BUSY2: LD HL,00000H BUSY0: DEC HL LD A,H OR L - JR Z,BUSY1 - IN A,(FDC_STR) + JR Z,BUSY1 + IN A,(FDC_STR) ; Get status, invert due to original controller bus being inverted, the test bit 0, ie. BUSY flag. CPL RRCA - JR C,BUSY0 + JR C,BUSY0 POP HL POP DE RET BUSY1: DEC E - JR NZ,BUSY2 + JR NZ,BUSY2 JP ERR ; ; DATA CHECK @@ -837,140 +849,148 @@ CONVRT: LD B,000H LD HL,(0CF1EH) XOR A TRANS: SBC HL,DE - JR C,TRANS1 + JR C,TRANS1 INC B - JR TRANS + JR TRANS TRANS1: ADD HL,DE - LD H,B ; B contains the track. - INC L ; L contains the sector. + LD H,B ; B contains the track. + INC L ; L contains the sector. LD (IY+004H),H LD (IY+005H),L -DCHK: LD A,(DRINO) ; Check drive number is valid. +DCHK: LD A,(DRINO) ; Check drive number is valid. CP 004H - JR NC,DTCK1 - LD A,(IY+004H) ; Check track boundary + JR NC,DTCK1 + LD A,(IY+004H) ; Check track boundary CP 046H - JR NC,DTCK1 - LD A,(IY+005H) ; Check sector boundary, cant be less than 1 or greater than 16 + JR NC,DTCK1 + LD A,(IY+005H) ; Check sector boundary, cant be less than 1 or greater than 16 OR A - JR Z,DTCK1 + JR Z,DTCK1 CP 011H - JR NC,DTCK1 - LD A,(IY+002H) ; Check the load size is not zero. + JR NC,DTCK1 + LD A,(IY+002H) ; Check the load size is not zero. OR (IY+003H) RET NZ DTCK1: JP ERR ; ; SEQUENTIAL READ ; + ; DRIVE NO=DRINO (0-3) + ; BYTE SIZE =IY+2,3 + ; ADDRESS =IX+0,1 + ; NEXT TRACK =IY+0 + ; NEXT SECTOR =IY+1 + ; START TRACK =IY+4 + ; START SECTOR =IY+5 BREAD: DI CALL CONVRT - LD A,00AH + LD A,00AH ; 10 tries before giving up. LD (RETRY),A -READ1: CALL READY - LD D,(IY+003H) ; Test to see if the size is a multiple of the sector size, if it isnt then increment sector count. - LD A,(IY+002H) +READ1: CALL READY ; Perform a recalibration and check for drive readiness. + LD D,(IY+003H) ; Test to see if the size is a multiple of the sector size, if it isnt then increment sector count. + LD A,(IY+002H) ; D:A contain the number of bytes to read. D is in 256 blocks. OR A - JR Z,RE0 + JR Z,RE0 INC D -RE0: LD A,(IY+005H) - LD (IY+001H),A - LD A,(IY+004H) - LD (IY+000H),A +RE0: LD A,(IY+005H) ; Get start sector. + LD (IY+001H),A ; Set as next sector. + LD A,(IY+004H) ; Get start track. + LD (IY+000H),A ; Set as next track. PUSH IX POP HL -RE8: SRL A +RE8: SRL A ; Adjust track to account for heads, so shift right 1 place. CPL OUT (FDC_DR),A - JR NC,RE1 + JR NC,RE1 ; Set side according to track LSB. 1 sets the /SIDE1 signal to select lower head. LD A,001H - JR RE2 + JR RE2 RE1: LD A,000H RE2: CPL OUT (FDC_SIDE),A - CALL SEEK - JR NZ,REE - LD C,0DBH - LD A,(IY+000H) - SRL A + CALL SEEK ; Seek to track loaded, 0 at start. + JR NZ,REE ; SEEK sets the NZ flag if any error occurred. + LD C,FDC_DR ; FDC Data register for INI command below. + LD A,(IY+000H) ; Get track and set. + SRL A ; Adjust for two heads. CPL OUT (FDC_TR),A - LD A,(IY+001H) + LD A,(IY+001H) ; Get sector and set. CPL OUT (FDC_SCR),A EXX - LD HL,RE3 + LD HL,RE3 ; Place address onto stack for RET C in data input loop. PUSH HL EXX - LD A,094H + LD A,094H ; Command: multiple sector read. CPL OUT (FDC_CR),A - CALL WAIT + CALL WAIT ; Wait until controller finished processing and deactivates busy flag. RE6: LD B,000H -RE4: IN A,(FDC_STR) +RE4: IN A,(FDC_STR) ; Read Command Type II Status Register values. RRCA - RET C + RET C ; Exit if the controller is busy. RRCA - JR C,RE4 - INI - JR NZ,RE4 + JR C,RE4 ; Wait for DRQ to go active. + INI ; Read byte from controller, decrement B and increment HL. + JR NZ,RE4 ; B = 0? Loop for the expected number of bytes in sector. INC (IY+001H) - LD A,(IY+001H) - CP 011H - JR Z,RETS - DEC D - JR NZ,RE6 - JR RE5 + LD A,(IY+001H) ; Next sector past limit? + CP 011H ; 16 Sectors per track is the limit. + JR Z,RETS ; Complete track read, exit. + DEC D ; Next 256 byte block. + JR NZ,RE6 ; Loop until all bytes we expect are read. + JR RE5 ; Exit when all are read. -RETS: DEC D -RE5: LD A,0D8H +RETS: DEC D ; We hit track end so decrement block as per above when we continue to loop. +RE5: LD A,0D8H ; Command: FORCED INTERRUPT to abort the multiple sector transfer command. CPL OUT (FDC_CR),A - CALL BUSY -RE3: IN A,(FDC_STR) + CALL BUSY ; Wait until the BUSY signal goes inactive. +RE3: IN A,(FDC_STR) ; Get type 4 command status values. CPL - AND 0FFH - JR NZ,REE + AND 0FFH ; All flags should be reset if successful, retry otherwise. + JR NZ,REE EXX POP HL EXX - LD A,(IY+001H) - CP 011H - JR NZ,REX - LD A,001H + LD A,(IY+001H) ; Get last sector read. + CP 011H ; Greater than 16? + JR NZ,REX ; If less, skip track increment. + LD A,001H ; Set to sector 1 and increment track. LD (IY+001H),A INC (IY+000H) -REX: LD A,D +REX: LD A,D ; Check D (bytes in 256 byte chunks expected). OR A - JR NZ,RE7 - LD A,080H + JR NZ,RE7 ; If it is not zero then we need to retry, + LD A,080H ; Turn motor off to complete. OUT (FDC_MOTOR),A RET -RE7: LD A,(IY+000H) - JR RE8 +RE7: LD A,(IY+000H) ; Retrieve next track and redo the read with remaining bytes. + JR RE8 -REE: LD A,(RETRY) +REE: LD A,(RETRY) ; The abort (FORCED INTERRUPT) command wasnt successful so retry the complete read. DEC A LD (RETRY),A - JR Z,ERR - CALL RCLB + JR Z,ERR ; Retries expired then raise error. + CALL RCLB ; Perform a recalibration before the retry. JP READ1 ; - ; WAIT AND BUSY OFF + ; Wait until BUSY flag resets - controller has accepted the command, processed it and now ready for data in/out. ; WAIT: PUSH DE PUSH HL - CALL DLY80U - LD E,008H + CALL DLY80U ; Short delay to allow the controller to act on the commmand. + ; LD E,008H ; 8 x 65536 x 1/CPU FREQ max delay. 131ms with a 4MHz clock. + LD E,0FFH ; 8 x 65536 x 1/CPU FREQ max delay. 131ms with a 4MHz clock. WAIT2: LD HL,00000H WAIT0: DEC HL LD A,H OR L JR Z,WAIT1 - IN A,(FDC_STR) + IN A,(FDC_STR) ; Get BUSY flag, loop if it is set. CPL RRCA JR NC,WAIT0 @@ -988,9 +1008,9 @@ NMASTE: LD HL,MES14 CALL DISP JR ERR1 ; - ; ; - ; ERRROR OR BREAK ; - ; ; + ; ; + ; ERRROR OR BREAK ; + ; ; ; ERR: CALL BOOTER ERR1: CALL MOFF @@ -999,69 +1019,69 @@ ERR1: CALL MOFF TRYAG: IF BUILDVERSION = 1 LD HL,MES10 CALL PRTSTR - CALL FDCC ; Check for the FD Interface. - JR NZ,MENU1 ; NZ = FD not available. - LD HL,MES11 ; Floppy menu choice. + CALL FDCC ; Check for the FD Interface. + JR NZ,MENU1 ; NZ = FD not available. + LD HL,MES11 ; Floppy menu choice. CALL PRTSTR -MENU1: LD HL,MES12 ; Cassette menu choice. +MENU1: LD HL,MES12 ; Cassette menu choice. CALL PRTSTR - LD HL,MESBOOT ; Boot current RAM program. + LD HL,MESBOOT ; Boot current RAM program. CALL PRTSTR - LD HL,MESTZFS ; TZFS menu choice. + LD HL,MESTZFS ; TZFS menu choice. CALL PRTSTR - LD HL,MESF1 ; Function 1 menu choice. + LD HL,MESF1 ; Function 1 menu choice. CALL PRTSTR - LD HL,MESF2 ; Function 2 menu choice. + LD HL,MESF2 ; Function 2 menu choice. CALL PRTSTR - LD HL,MESF3 ; Function 3 menu choice. + LD HL,MESF3 ; Function 3 menu choice. CALL PRTSTR - LD HL,MESF4 ; Function 4 menu choice. + LD HL,MESF4 ; Function 4 menu choice. CALL PRTSTR - LD HL,MESF5 ; Function 5 menu choice. + LD HL,MESF5 ; Function 5 menu choice. CALL PRTSTR - LD HL,MESF6 ; Function 6 menu choice. + LD HL,MESF6 ; Function 6 menu choice. CALL PRTSTR - LD HL,MESF7 ; Function 7 menu choice. + LD HL,MESF7 ; Function 7 menu choice. CALL PRTSTR - LD HL,MESF8 ; Function 8 menu choice. + LD HL,MESF8 ; Function 8 menu choice. CALL PRTSTR GETCHOICE: CALL KEYS1 - BIT 6,A ; F - Floppy disk + BIT 6,A ; F - Floppy disk JP Z,DNO - BIT 3,A ; C - Cassette. - JP Z,CMT - BIT 2,A ; B - Boot current RAM 0x0000 program. + BIT 3,A ; C - Cassette. + JP Z,CMT + BIT 2,A ; B - Boot current RAM 0x0000 program. JP Z,NST - BIT 0,A ; / - Boot external rom. - JP Z,EXROMT + BIT 0,A ; / - Boot external rom. + JP Z,EXROMT LD B,016H CALL KEYS - BIT 4,A ; T - TZFS Monitor + BIT 4,A ; T - TZFS Monitor JP Z,TZFS - BIT 6,A ; V - Verification Test + BIT 6,A ; V - Verification Test JP Z,VTEST LD B,010H CALL KEYS - BIT 0,A ; F1 + BIT 0,A ; F1 JP Z,FUNC_F1 - BIT 1,A ; F2 + BIT 1,A ; F2 JP Z,FUNC_F2 - BIT 2,A ; F3 + BIT 2,A ; F3 JP Z,FUNC_F3 - BIT 3,A ; F4 + BIT 3,A ; F4 JP Z,FUNC_F4 - BIT 4,A ; F5 + BIT 4,A ; F5 JP Z,FUNC_F5 - BIT 5,A ; F6 + BIT 5,A ; F6 JP Z,FUNC_F6 - BIT 6,A ; F7 + BIT 6,A ; F7 JP Z,FUNC_F7 - BIT 7,A ; F8 + BIT 7,A ; F8 JP Z,FUNC_F8 JR GETCHOICE ELSE - CALL FDCC ; Check for the FD Interface. - JR NZ,TRYAG3 ; NZ = FD not available. + CALL FDCC ; Check for the FD Interface. + JR NZ,TRYAG3 ; NZ = FD not available. LD HL,MES10 LD E,05AH LD C,00CH @@ -1141,17 +1161,17 @@ DLYT: DEC DE RET ; ; - ; ; - ; INTRAM EXROM ; - ; ; + ; ; + ; INTRAM EXROM ; + ; ; ; EXROMT: LD HL,IBADR2 LD IX,EROM1 JR SEROMA -EROM1: IN A,(0F9H) ; Check to see if the external ROM has been installed. +EROM1: IN A,(0F9H) ; Check to see if the external ROM has been installed. CP 000H - JP NZ,NKIN ; Byte is not 0, no rom so exit. + JP NZ,NKIN ; Byte is not 0, no rom so exit. LD IX,EROM2 ERMT1: JR SEROMA @@ -1170,9 +1190,9 @@ SEROMA: LD A,H LD A,L OUT (0F9H),A LD D,004H -SEROMD: DEC D ; Short pause for the address to latch. +SEROMD: DEC D ; Short pause for the address to latch. JR NZ,SEROMD - JP (IX) ; Pass control to the function provided in IX for address evaluation. + JP (IX) ; Pass control to the function provided in IX for address evaluation. ; ; Load option extensions under the tranZPUter. @@ -1232,7 +1252,7 @@ FN_7: DB NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, ; A simple graphics test routine, not optimised, just to indicate if the graphics are working on the tranZPUter. FUNC_F8: IF BUILDVERSION = 1 - LD A,0AFH ; Setup to enable FPGA video. + LD A,0AFH ; Setup to enable FPGA video. OUT (06EH),A LD A,000H OUT (0B9H),A @@ -1240,41 +1260,41 @@ FUNC_F8: IF BUILDVERSION = 1 OUT (0B8H),A LD A,0D3H OUT (PIOA),A -F8CLR: LD HL,0D800H +F8CLR: LD HL,0D800H LD A,0E0H -F8CLR1: LD (HL),070H ; Load zero's - INC HL - CP H +F8CLR1: LD (HL),070H ; Load zero's + INC HL + CP H JR NZ,F8CLR1 ; -F8LOOP: LD A,001H ; Select BLUE graphics RAM bank. +F8LOOP: LD A,001H ; Select BLUE graphics RAM bank. OUT (GRAMCOLRSEL),A CALL FUNCFILLW - LD A,002H ; Select RED graphics RAM bank. + LD A,002H ; Select RED graphics RAM bank. OUT (GRAMCOLRSEL),A CALL FUNCFILLW - LD A,003H ; Select GREEN graphics RAM bank. + LD A,003H ; Select GREEN graphics RAM bank. OUT (GRAMCOLRSEL),A CALL FUNCFILLW JR F8LOOP -FUNCFILLW: EX AF,AF' ; 2nd bank +FUNCFILLW: EX AF,AF' ; 2nd bank XOR A -FUNCFILLW1: EX AF,AF' ; 1st bank +FUNCFILLW1: EX AF,AF' ; 1st bank CALL FUNCFILL - EX AF,AF' ; 2nd bank + EX AF,AF' ; 2nd bank INC A JR NZ,FUNCFILLW1 - EX AF,AF' ; 1st bank + EX AF,AF' ; 1st bank CALL FUNCFILL RET -FUNCFILL: DI - LD HL,GRAMADDRL ; Base of Video Graphics RAM in address space. - IN A,(PIOA) ; Read the PIO register and enable Graphics RAM - SET 7,A - RES 6,A - OUT (PIOA),A +FUNCFILL: DI + LD HL,GRAMADDRL ; Base of Video Graphics RAM in address space. + IN A,(PIOA) ; Read the PIO register and enable Graphics RAM + SET 7,A + RES 6,A + OUT (PIOA),A LD DE,GRAMADDRL + 1 EX AF,AF' LD (HL),A @@ -1283,7 +1303,7 @@ FUNCFILL: DI EX AF,AF' ; FUNCFILL2: IN A,(PIOA) - RES 7,A ; Read the PIO and disable Video/Graphics RAM (use normal RAM). + RES 7,A ; Read the PIO and disable Video/Graphics RAM (use normal RAM). SET 6,A OUT (PIOA),A EI @@ -1298,33 +1318,33 @@ SDLOADFILE: IF BUILDVERSION = 1 ; HL Contains the source filename to use, copy into structure. LD DE,TZSVC_FILENAME LD BC,TZSVCFILESZ - LDIR ; Copy in the MZF filename. - LD HL,00000H ; Setup the load address to 0x0000 = default load address in IPL mode. + LDIR ; Copy in the MZF filename. + LD HL,00000H ; Setup the load address to 0x0000 = default load address in IPL mode. LD (TZSVC_LOADADDR),HL ; - LD A,TZSVC_FTYPE_MZF ; Default to full file load. + LD A,TZSVC_FTYPE_MZF ; Default to full file load. LD (TZSVC_FILE_TYPE),A - LD A,0FFH ; We have to use filenames not index numbers so mark as unused. + LD A,0FFH ; We have to use filenames not index numbers so mark as unused. LD (TZSVC_FILE_NO), A - LD A,000H ; tranZPUter memory is target for file load. + LD A,000H ; tranZPUter memory is target for file load. LD (TZSVC_MEM_TARGET), A LD A,TZSVC_CMD_LOADFILE - LD (TZSVCCMD), A ; Load up the command into the service record. - CALL SVC_CMD ; And make communications with the I/O processor, returning with the required record. + LD (TZSVCCMD), A ; Load up the command into the service record. + CALL SVC_CMD ; And make communications with the I/O processor, returning with the required record. OR A JR Z, SDLOADFIL2 LD HL,SVCIOERR SDLOADFIL1: CALL PRTSTR RET -SDLOADFIL2: LD A,(TZSVCRESULT) ; Check result, non zero will be a file error. +SDLOADFIL2: LD A,(TZSVCRESULT) ; Check result, non zero will be a file error. OR A LD HL,SVCFILERR JR NZ,SDLOADFIL1 ; - JP NST ; Initiate a normal state restart to run the loaded program. + JP NST ; Initiate a normal state restart to run the loaded program. ENDIF ; Method to send a command to the I/O processor and verify it is being acted upon. @@ -1340,19 +1360,19 @@ SDLOADFIL2: LD A,(TZSVCRESULT) ; Check ; A = 2 - Failure, no result from I/O processor, it could have crashed or SD card removed! SVC_CMD: IF BUILDVERSION = 1 PUSH BC - LD (TZSVCCMD), A ; Load up the command into the service record. + LD (TZSVCCMD), A ; Load up the command into the service record. LD A,TZSVC_STATUS_REQUEST - LD (TZSVCRESULT),A ; Set the service structure result to REQUEST, if this changes then the K64 is processing. + LD (TZSVCRESULT),A ; Set the service structure result to REQUEST, if this changes then the K64 is processing. - LD BC, TZSVCWAITIORETRIES ; Safety in case the IO request wasnt seen by the I/O processor, if we havent seen a response in the service + LD BC, TZSVCWAITIORETRIES ; Safety in case the IO request wasnt seen by the I/O processor, if we havent seen a response in the service SVC_CMD1: PUSH BC LD A,(TZSVCCMD) - OUT (SVCREQ),A ; Make the service request via the service request port. + OUT (SVCREQ),A ; Make the service request via the service request port. LD BC,0 SVC_CMD2: LD A,(TZSVCRESULT) - CP TZSVC_STATUS_REQUEST ; I/O processor when it recognises the request sets the status to PROCESSING or gives a result, if this hasnt occurred the the K64F hasnt begun processing. + CP TZSVC_STATUS_REQUEST ; I/O processor when it recognises the request sets the status to PROCESSING or gives a result, if this hasnt occurred the the K64F hasnt begun processing. JR NZ, SVC_CMD3 DEC BC LD A,B @@ -1362,7 +1382,7 @@ SVC_CMD2: LD A,(TZSVCRESULT) DEC BC LD A,B OR C - JR NZ,SVC_CMD1 ; Retry sending the I/O command. + JR NZ,SVC_CMD1 ; Retry sending the I/O command. ; PUSH DE PUSH HL @@ -1371,15 +1391,15 @@ SVC_CMD2: LD A,(TZSVCRESULT) POP HL POP DE POP BC - LD A,1 ; No response, error. + LD A,1 ; No response, error. RET SVC_CMD3: POP BC ; - LD BC,TZSVCWAITCOUNT ; Number of loops to wait for a response before setting error. + LD BC,TZSVCWAITCOUNT ; Number of loops to wait for a response before setting error. SVC_CMD4: PUSH BC LD BC,0 SVC_CMD5: LD A,(TZSVCRESULT) - CP TZSVC_STATUS_PROCESSING ; Wait until the I/O processor sets the result, again timeout in case it locks up. + CP TZSVC_STATUS_PROCESSING ; Wait until the I/O processor sets the result, again timeout in case it locks up. JR NZ, SVC_CMD6 DEC BC LD A,B @@ -1389,7 +1409,7 @@ SVC_CMD5: LD A,(TZSVCRESULT) DEC BC LD A,B OR C - JR NZ,SVC_CMD4 ; Retry polling for result. + JR NZ,SVC_CMD4 ; Retry polling for result. ; PUSH DE PUSH HL @@ -1400,7 +1420,7 @@ SVC_CMD5: LD A,(TZSVCRESULT) POP BC LD A,2 RET -SVC_CMD6: XOR A ; Success. +SVC_CMD6: XOR A ; Success. POP BC POP BC RET @@ -1421,18 +1441,18 @@ SVCFILERR: IF BUILDVERSION = 1 ; Method to perform a series of dev tests to visually or physically check CPLD/FPGA/firmware changes. VTEST: IF BUILDVERSION = 1 - LD A,0AFH ; Setup to enable FPGA video. + LD A,0AFH ; Setup to enable FPGA video. OUT (06EH),A - LD A,0D3H ; Page in character video ram. + LD A,0D3H ; Page in character video ram. OUT (PIOA),A -VTF0: LD HL,0D000H - LD A,0D8H ; Set to character VRAM top address. -VTF1: LD (HL),030H ; Load zero's - INC HL - CP H +VTF0: LD HL,0D000H + LD A,0D8H ; Set to character VRAM top address. +VTF1: LD (HL),030H ; Load zero's + INC HL + CP H JR NZ,VTF1 LD A,0E0H -VTF2: LD (HL),071H ; Set blue background white foreground. +VTF2: LD (HL),071H ; Set blue background white foreground. INC HL CP H JR NZ,VTF2 diff --git a/software/roms/mz1500_ipl.rom b/software/roms/mz1500_ipl.rom index 50938026f4a1cabced4abd7f41ddc45ee956f9b9..3f9d597f4983219fd091fcf7d2b792cace7ec7fa 100755 GIT binary patch delta 6108 zcmdrwYj7LKd3OZv06sus^`K7FNLrA#dx9eof+-P!#iKwHq!>Ox5!B1_C~h-jO*47e ztz1%)%d77VPm+n_*7d8hPSw3_mAQAvg z5XXLd0Ac zL^1^s!w|D!mN@eqoQ6XO8B#tjd}u8;M)Fa95EMI2cy0pWG+Z7ZwQa>5KPw3!ehZ3mzHGhq-a9_$BzVm z$&hu7))M;Wgtj|WIH}c!j!Y0?0r2CL{x0Ch>2{8}cLyOpkyc zZ8+KTIUrIrphLPr$7aZx8I)9Ae*uCd3$P=?ES#Df**Ye-PFru07iNY(yyK2qH5R=v zjq|87!3QYGpu{^Jh*-u4ga@f1FX)EA@96q3tGcCvy&ppk(oS*ckgx-)E@t)B)`{}ziS(@)u^L^!NKcRYkhOWYyTVIw z8MQ)|mkSCHw)sZ?7|Jg`ZPr{|20$i63np z=4|gw-clew0|?B6!xXWGv2kUIFt3%0=FT#sgygg06>xZ|XoRz#mMonL`ea%#qnp!$ z0ZmWSLms@#aX|dHL13oaF{-q2&0de+i`(}2ci|@Q0nSqIuIHM4ZEXi|U8BE=v+Q>7 z=Jqt!Hf5K!?pn^{_j@}!ytu>D=H)CcZn=f?wKjJ6+Br*OTQlYw{kYlZscUR);IO5I z^E7#JyVp~9fWx&d9rpuv*WJIaud`tK`<#!j3Hac8Upw~Twsv1byQc+|taCSW84=jq zF1J(B0BhxZ9k|8g@9@GJWZjFTSLR?Ih-vR+wGQmnbNm*le3#?{>+2?`6$hwf&t*APGpyjsI&sv3~Z^M z*@jI~_oLa2Y5K+`I@{D#TLM!09-T>u|mBaF$m|j)%?Z8>bVPF8n-RQyDDsekbw=u4JZ=ZhSM)2`v<4rcoPnqqD3#ewklI)+Mc}i?Q=Kc5~Tf<)m0% zKKgsYcP))uTI3->@({JgYq-=Fu9=z)Ao6>~cc_&U3rb z#-TWvH4@u61fG|;Y0GDt zAMh_v8`|$8R99D1DCEaXGHiuJTJPJ_S_cK7&J8)u2YI#i0Ir8j=g*`=fbZfwAkHh( z?C9Z|^(2qI`y2focTx6n?Tt_>Aai$kYddsqdX~-<1j-ziI$P{-^VI4YSjXDKwL;Qw z@qEszhV#_vRVc%}h65f=oXxUZrdsQ`9kk|H{G7J~*LnT0@^m!%TC=6g*HkSYC~w_l zspA^kK^b5B0qm>iFz8^pRjTztlXPia98L7uoN+dvHW4qyBesb?VounYe)MphH?@|W zyHpe}TWL+`?X9VQ#9rM0tb7KW>_Y#uXGA^#Yk`xNAUd4szQ{9N;@>W2%rXU^0`xI#%`Bi-EXG$Dydm#L&GtuWv_BrSK zoS*fHZ7T#w)=~36NAShldDYmnK1wGu5XIj*=X3nJ(OXeW=KhxH+J#$EvjH z<^Tx%;wbYa*mv6QmHbgW0Ari10< zaaoau=uC+#?zuiKUt9-`^crnWT{xB22iL@* zyR$Np@34_4a{_9+%U~2fTl>UUB1du~&*UkCsE7$ZlpI8_XNHIHy5k?<)PMq?c$b=? z?|m~V<6UZ3f!g(TwQHa1&qHe0ch#=Lnf2f@xB#gK>DK7+9?{UCe!w%U@BC$QKyAuH zCQyap#%Xuj5UN-Qe5<}o?gGV8*%mHELRCa(=#RMalsT+D#5%e)!( zR#_L0U6l7(qiUDsKNR$hd9rUF9h)bQ&C?G-=%rKBH(1^g- z-QZ;q4c&kZKJ-8j-ds>HdRaYv@#bJL1N^%)0?IC0?;IOYj}F|Vs@JNgsp@6(kRJoO z*=E_Ic1b4-3e=+(n1OEENt_QeU`Ihppi&_MH26X;9h7#?(^BK3nJe2*w&+;{8dcgj z>Rl~{2A8ueeaWBvCGq}PXn$hzq3U_jEa_NCU5!J7RYUq0-43J~@p`uh^@wW%(6qX1 z(5U*?yU@*9;#-tcE&-5YacvVWvU$-qF*oMkN!wfD9J!bx-(H9*6dAZwiN#go+A8sh zs!&C$O3V@3;sIwBiYe}`C|I@_0B)ou%`VMIzbM~FK1xH2BpQwhv*eXDNFdKIx@%Dt z-a!6$5pXu_`4F!J3|TZEFaJ6jP76`;UOL#nC~c}#+;ynuzn)(t3kySJqXwHkcpB^# z&;}3prl5m_WoH>s;{*_a_}&Kmkz%U_?H$10=dMCF37yAy2k6%c`|obNy37SeN;nUM z0;v$>aY+P$(9Q2eR4iUY6|F|V>_ulS^y*az)cEh-6mV%6V{rj;qORVbl21~g8QPAh z@ZZoCSh!Jx)tFIi1`pe+(Tm^^s0zBnZ9CD6J3i;3MhzKobrp*b86D>TVUy^z>uFL|Wer%Hah6T6UAgJcnnOx0G-KycQfz zOE2@e-~x~YR#GhwvFkI!cCr~^326p|u)63AAo8S8qpQ3WPEccx>iU2s>S-m5m37~R zpgfy~e~AsJt*PsYA_sI4^aRFsVzEbw!^9{q#KLn~{phhMq%VCvVRD&^iX3y-=Xw-r zlTF%Ulbkkbn@!rT9^Zv3!C@~~VLi+fF=fBe${7t;`C?P?%SJ05xNl42*?*{v$tNzr tHP4X!v2yJ1B#j$OR!-e;5-QuV{`Tx*96FUF^ delta 6 NcmZn&Xi!)n000O^0wDkZ diff --git a/software/roms/mz2000_ipl_original.rom b/software/roms/mz2000_ipl_original.rom index d72980beb45a90f8f24013ef22e6b3c3f4b7efb8..28072f3016bd73f3c71dabf2a48be15e7947795c 100644 GIT binary patch delta 14 VcmZn=Xb{*C&C2+Ha}4VeCIBT81nU3* delta 14 VcmZn=Xb{*C&C1BJIfiu!696Ab1M~m@ diff --git a/software/roms/mz2000_ipl_tzpu.rom b/software/roms/mz2000_ipl_tzpu.rom index 0dcb93d88dd3a4611cd75789eb3cfd5c66fc4a40..0079fab48d5907650686b3eda868b71dbc0e71af 100644 GIT binary patch delta 14 VcmZorXi(VT#K!o4voqUXP5>p51p5F0 delta 14 VcmZorXi(VT#Ky?6*_mxGCjcME1Oxy8 diff --git a/software/tools/copytosd.sh b/software/tools/copytosd.sh index 0707b79..ef07d21 100755 --- a/software/tools/copytosd.sh +++ b/software/tools/copytosd.sh @@ -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."