5 Commits
v1.00 ... main

Author SHA1 Message Date
Philip Smart
db9a4552e7 update to README 2026-04-03 12:28:18 +01:00
Philip Smart
2cbb73cc5b Updated README 2026-04-02 23:49:54 +01:00
Philip Smart
efcba04379 Updated RAM selection to use bit D7 of the EXXX and FXXX register, D7=1 is RAM, D7=0 is ROM 2026-04-02 09:35:13 +01:00
Philip Smart
4863dd122f Merge remote-tracking branch 'refs/remotes/public/main' 2026-04-01 23:32:38 +01:00
Philip Smart
7cf8db9aca Synchroniser bug fix 2026-04-01 23:31:52 +01:00
3 changed files with 197 additions and 109 deletions

295
CPLD/v1.2/sfd700.vhd vendored
View File

@@ -12,7 +12,7 @@
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
--
-- History: July 2023 - v1.0 - Initial write.
-- Apr 2026 - v1.1 - Synchroniser bug fixes.
---------------------------------------------------------------------------------------------------------
-- 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
@@ -112,8 +112,6 @@ architecture rtl of cpld128 is
signal EXXX_WR_SELni : std_logic;
signal FXXX_RD_SELni : std_logic;
signal FXXX_WR_SELni : std_logic;
signal RAMEN_RD_SELni : std_logic;
signal RAMEN_WR_SELni : std_logic;
signal MODE_RD_SELni : std_logic;
signal ROMINHSET_WR_SELni : std_logic;
signal ROMINHCLR_WR_SELni : std_logic;
@@ -131,15 +129,44 @@ architecture rtl of cpld128 is
signal REG_SIDE : std_logic;
signal REG_DDEN : std_logic;
signal REG_INT : std_logic;
signal REG_EXXX_PAGE : std_logic_vector(6 downto 0);
signal REG_FXXX_PAGE : std_logic_vector(6 downto 0);
signal REG_EXXX_PAGE : std_logic_vector(7 downto 0); -- D7 = RAM enable, D6:0 = page address.
signal REG_FXXX_PAGE : std_logic_vector(7 downto 0); -- D7 = RAM enable, D6:0 = page address.
signal REG_ROMDIS : std_logic;
signal REG_ROMINH : std_logic;
signal REG_RAMEN : std_logic;
-- Selected mode of the interface, ie. which machine it will be plugged into.
signal IFMODE : integer range 0 to 7 := 0;
-- Synchronised bus select signals (1-stage registered to eliminate glitches from combinational decode).
-- The existing *_LAST variable in each process provides the second synchronisation stage,
-- giving a full 2-stage pipeline for edge detection.
signal SIDE_WR_SYNC : std_logic := '1';
signal DDEN_WR_SYNC : std_logic := '1';
signal DRIVE_WR_SYNC : std_logic := '1';
signal INTEN_SYNC : std_logic := '1';
signal EXXX_WR_SYNC : std_logic := '1';
signal FXXX_WR_SYNC : std_logic := '1';
signal ROMINHSET_WR_SYNC : std_logic := '1';
signal ROMINHCLR_WR_SYNC : std_logic := '1';
signal ROMDISSET_WR_SYNC : std_logic := '1';
signal ROMDISCLR_WR_SYNC : std_logic := '1';
-- Registered Z80 data bus and RDn for clean capture in write processes.
signal Z80_DATA_SYNC : std_logic_vector(7 downto 0) := (others => '0');
signal Z80_RDn_SYNC : std_logic := '1';
-- Registered memory chip selects — eliminates combinational glitches from address bus
-- skew propagating through MEM_EXXX/FXXX decode into ROM_SELni/RAM_SELni.
-- One CLK_16M cycle (62.5ns) of added latency; the Z80 at 3.54MHz has ~200ns of read
-- access window so this is well within margin for typical FlashROM/RAM (55-70ns).
signal ROM_CSn_REG : std_logic := '1';
signal RAM_CSn_REG : std_logic := '1';
-- Registered ID bus output for page address — prevents brief high-Z glitch on
-- ID during address transitions from corrupting the ROM/RAM upper address.
signal ID_REG : std_logic_vector(7 downto 0) := (others => '0');
signal ID_OEn : std_logic := '1';
-- Clocks.
signal CLK_8Mi : std_logic := '0';
@@ -162,53 +189,93 @@ begin
end if;
end process;
-- Process to register configuration mode on reset.
SETMODE: process( Z80_RESETn, MODE )
-- Synchronise asynchronous Z80 bus-derived select signals into the CLK_16M domain.
-- This eliminates glitches caused by address/control bus skew on the combinational decodes.
-- Each signal is registered once here; the *_LAST variable in each consumer process provides
-- the second stage, giving a full 2-stage synchronisation pipeline for edge detection.
SYNCBUS: process( Z80_RESETn, CLK_16M )
begin
if(Z80_RESETn = '0') then
-- Configuration jumpers specify the machine the SFD700 card will be used with.
IFMODE <= to_integer(unsigned(MODE));
SIDE_WR_SYNC <= '1';
DDEN_WR_SYNC <= '1';
DRIVE_WR_SYNC <= '1';
INTEN_SYNC <= '1';
EXXX_WR_SYNC <= '1';
FXXX_WR_SYNC <= '1';
ROMINHSET_WR_SYNC <= '1';
ROMINHCLR_WR_SYNC <= '1';
ROMDISSET_WR_SYNC <= '1';
ROMDISCLR_WR_SYNC <= '1';
Z80_DATA_SYNC <= (others => '0');
Z80_RDn_SYNC <= '1';
elsif(rising_edge(CLK_16M)) then
SIDE_WR_SYNC <= SIDE_WR_SELni;
DDEN_WR_SYNC <= DDEN_WR_SELni;
DRIVE_WR_SYNC <= DRIVE_WR_SELni;
INTEN_SYNC <= INTEN_SELni;
EXXX_WR_SYNC <= EXXX_WR_SELni;
FXXX_WR_SYNC <= FXXX_WR_SELni;
ROMINHSET_WR_SYNC <= ROMINHSET_WR_SELni;
ROMINHCLR_WR_SYNC <= ROMINHCLR_WR_SELni;
ROMDISSET_WR_SYNC <= ROMDISSET_WR_SELni;
ROMDISCLR_WR_SYNC <= ROMDISCLR_WR_SELni;
Z80_DATA_SYNC <= Z80_DATA;
Z80_RDn_SYNC <= Z80_RDn;
end if;
end process;
-- Process to register configuration mode on reset.
-- Synchronous reset captures MODE jumpers cleanly on CLK_16M; once reset deasserts the
-- register holds its value. Avoids latch inference from the original combinational process.
SETMODE: process( CLK_16M )
begin
if(rising_edge(CLK_16M)) then
if(Z80_RESETn = '0') then
-- Configuration jumpers specify the machine the SFD700 card will be used with.
IFMODE <= to_integer(unsigned(MODE));
end if;
end if;
end process;
-- Set Floppy Disk Side.
-- A write to 0xDD bit D0 = low sets Floppy Disk side 1, high sets side 0.
SETSIDE: process( Z80_RESETn, CLK_16M, SIDE_WR_SELni )
-- A write to 0xDD: D0 = 0 selects side 0, D0 = 1 selects side 1 (active-high on SIDE1 output).
SETSIDE: process( Z80_RESETn, CLK_16M )
variable SIDE_SEL_LASTni : std_logic;
begin
if(Z80_RESETn = '0') then
REG_SIDE <= '0';
SIDE_SEL_LASTni := '0';
SIDE_SEL_LASTni := '1';
elsif(rising_edge(CLK_16M)) then
if(SIDE_WR_SELni = '0' and SIDE_SEL_LASTni = '1') then
REG_SIDE <= not Z80_DATA(0);
if(SIDE_WR_SYNC = '0' and SIDE_SEL_LASTni = '1') then
REG_SIDE <= not Z80_DATA_SYNC(0);
end if;
SIDE_SEL_LASTni := SIDE_WR_SELni;
SIDE_SEL_LASTni := SIDE_WR_SYNC;
end if;
end process;
-- Set Double Data Rate Enable.
-- A write to 0xDE bit D0 = low enables double data rate, high enables single data rate.
SETDDEN: process( Z80_RESETn, CLK_16M, DDEN_WR_SELni )
SETDDEN: process( Z80_RESETn, CLK_16M )
variable DDEN_SEL_LASTni : std_logic;
begin
if(Z80_RESETn = '0') then
REG_DDEN <= '1';
DDEN_SEL_LASTni := '0';
DDEN_SEL_LASTni := '1';
elsif(rising_edge(CLK_16M)) then
if(DDEN_WR_SELni = '0' and DDEN_SEL_LASTni = '1') then
REG_DDEN <= not Z80_DATA(0);
if(DDEN_WR_SYNC = '0' and DDEN_SEL_LASTni = '1') then
REG_DDEN <= not Z80_DATA_SYNC(0);
end if;
DDEN_SEL_LASTni := DDEN_WR_SELni;
DDEN_SEL_LASTni := DDEN_WR_SYNC;
end if;
end process;
-- Set Drive Number and Motor
-- A write to 0xDC, bits D2:0 sets the active drive. Bit 2 high enables the active drive, low disables all drives.
-- Bit D7 enables the motor.
SETDRIVE: process( Z80_RESETn, CLK_16M, DRIVE_WR_SELni )
SETDRIVE: process( Z80_RESETn, CLK_16M )
variable DRIVE_SEL_LASTni: std_logic;
begin
if(Z80_RESETn = '0') then
@@ -217,16 +284,16 @@ begin
REG_DRIVEC <= '0';
REG_DRIVED <= '0';
REG_MOTOR <= '0';
DRIVE_SEL_LASTni := '0';
DRIVE_SEL_LASTni := '1';
elsif(rising_edge(CLK_16M)) then
if(DRIVE_WR_SELni = '0' and DRIVE_SEL_LASTni = '1') then
if(DRIVE_WR_SYNC = '0' and DRIVE_SEL_LASTni = '1') then
REG_DRIVEA <= '0';
REG_DRIVEB <= '0';
REG_DRIVEC <= '0';
REG_DRIVED <= '0';
REG_MOTOR <= Z80_DATA(7);
case(to_integer(unsigned(Z80_DATA(2 downto 0)))) is
REG_MOTOR <= Z80_DATA_SYNC(7);
case(to_integer(unsigned(Z80_DATA_SYNC(2 downto 0)))) is
when 0 =>
when 4 =>
@@ -241,86 +308,77 @@ begin
when others =>
end case;
end if;
DRIVE_SEL_LASTni := DRIVE_WR_SELni;
DRIVE_SEL_LASTni := DRIVE_WR_SYNC;
end if;
end process;
-- Set Interrupt Enable
-- A write to 0xDF enables WD1773 interrupts, a read from 0xDF disables WD1773 interrupts.
SETINT: process( Z80_RESETn, CLK_16M, INTEN_SELni )
SETINT: process( Z80_RESETn, CLK_16M )
variable INTEN_SEL_LASTni: std_logic;
begin
if(Z80_RESETn = '0') then
REG_INT <= '0';
INTEN_SEL_LASTni := '0';
INTEN_SEL_LASTni := '1';
elsif(rising_edge(CLK_16M)) then
if(INTEN_SELni = '0' and INTEN_SEL_LASTni = '1') then
REG_INT <= Z80_RDn;
if(INTEN_SYNC = '0' and INTEN_SEL_LASTni = '1') then
REG_INT <= Z80_RDn_SYNC;
end if;
INTEN_SEL_LASTni := INTEN_SELni;
INTEN_SEL_LASTni := INTEN_SYNC;
end if;
end process;
-- Set FlashROM/RAM EXXX (E300:EFFF) page address. Each bank is 4K but due to the memory mapped I/O, only
-- E300:EFFF is useable in RFS.
-- A write t0 0x60 copies D6:0 into the EXXX page address register which selects a required 4K page in region E300:EFFF
SETEXXXPAGE: process( Z80_RESETn, CLK_16M, IFMODE, EXXX_WR_SELni )
-- Set FlashROM/RAM EXXX (E300:EFFF) page address and ROM/RAM select. Each bank is 4K but due to the
-- memory mapped I/O, only E300:EFFF is useable in RFS.
-- A write to 0x60: D7 = 1 selects RAM, D7 = 0 selects FlashROM; D6:0 = 4K page address.
SETEXXXPAGE: process( Z80_RESETn, CLK_16M )
variable EXXX_SEL_LASTni : std_logic;
begin
if(Z80_RESETn = '0') then
-- Customised UROM containing RFS start bank.
REG_EXXX_PAGE <= "0000010";
EXXX_SEL_LASTni := '0';
-- Customised UROM containing RFS start bank (page 2, ROM).
REG_EXXX_PAGE <= "00000010";
EXXX_SEL_LASTni := '1';
elsif(rising_edge(CLK_16M)) then
if(EXXX_WR_SELni = '0' and EXXX_SEL_LASTni = '1') then
REG_EXXX_PAGE <= Z80_DATA(6 downto 0);
if(EXXX_WR_SYNC = '0' and EXXX_SEL_LASTni = '1') then
REG_EXXX_PAGE <= Z80_DATA_SYNC;
end if;
EXXX_SEL_LASTni := EXXX_WR_SELni;
EXXX_SEL_LASTni := EXXX_WR_SYNC;
end if;
end process;
-- Set FlashROM/RAM FXXX (F000:FFFF) page address.
-- A write to 0x61 copies D6:0 into the FXXX page address register which selects a 4K page
-- window of ROM/RAM accessible in the FXXX window.
SETFXXXPAGE: process( Z80_RESETn, CLK_16M, IFMODE, FXXX_WR_SELni )
-- Set FlashROM/RAM FXXX (F000:FFFF) page address and ROM/RAM select.
-- A write to 0x61: D7 = 1 selects RAM, D7 = 0 selects FlashROM; D6:0 = 4K page address.
SETFXXXPAGE: process( Z80_RESETn, CLK_16M )
variable FXXX_SEL_LASTni : std_logic;
variable RESET_DONE : std_logic;
begin
if(Z80_RESETn = '0') then
-- Initial page is set to 0, which is the MZ-80A AFI ROM. The original ROM is 2K so potential to add additional code in the upper block.
-- Initial page is set to 0 (ROM), which is the MZ-80A AFI ROM.
REG_FXXX_PAGE <= (others => '0');
-- MZ-700, starting at the second 4k page, set banking registers to 4K block which stores the MZ-700 AFI ROM.
if(IFMODE = MODE_MZ700) then
REG_FXXX_PAGE(1 downto 0) <= "01";
end if;
FXXX_SEL_LASTni := '1';
RESET_DONE := '0';
elsif(rising_edge(CLK_16M)) then
-- Set 4k F000:FFFF page address.
if(FXXX_WR_SELni = '0' and FXXX_SEL_LASTni = '1') then
REG_FXXX_PAGE <= Z80_DATA(6 downto 0);
-- On the first clock after reset deasserts, apply mode-specific default.
-- IFMODE is a synchronous register captured during reset, so it is stable here.
if(RESET_DONE = '0') then
RESET_DONE := '1';
-- MZ-700, starting at the second 4K page (ROM), set banking register to the block which stores the MZ-700 AFI ROM.
if(IFMODE = MODE_MZ700) then
REG_FXXX_PAGE(1 downto 0) <= "01";
end if;
end if;
-- Set 4k F000:FFFF page address and ROM/RAM select.
if(FXXX_WR_SYNC = '0' and FXXX_SEL_LASTni = '1') then
REG_FXXX_PAGE <= Z80_DATA_SYNC;
end if;
-- Edge detection.
FXXX_SEL_LASTni := FXXX_WR_SELni;
end if;
end process;
-- Enable FlashROM or RAM.
-- A write to 0x62 with D0 = low enables FlashROM, D0 = high enables RAM.
SETRAMEN: process( Z80_RESETn, CLK_16M, RAMEN_WR_SELni )
variable RAMEN_SEL_LASTni: std_logic;
begin
if(Z80_RESETn = '0') then
REG_RAMEN <= '0';
elsif(rising_edge(CLK_16M)) then
if(RAMEN_WR_SELni = '0' and RAMEN_SEL_LASTni = '1') then
REG_RAMEN <= Z80_DATA(0);
end if;
RAMEN_SEL_LASTni := RAMEN_WR_SELni;
FXXX_SEL_LASTni := FXXX_WR_SYNC;
end if;
end process;
@@ -339,7 +397,7 @@ begin
--
-- A write to 0xE1 enables RAM in the region 0xD000:FFFF so the onboard FlashROM/RAM should be disabled. A write to 0xE3/0xE4 re-enables
-- FlashROM/RAM. A write to 0xE5 inhibits all access to region 0xD000:FFFF so the onboard FlashROM/RAM is disabled, write to 0xE6 re-enables it.
SETHIMEM: process( Z80_RESETn, CLK_16M, ROMINHSET_WR_SELni, ROMINHCLR_WR_SELni, ROMDISSET_WR_SELni, ROMDISCLR_WR_SELni )
SETHIMEM: process( Z80_RESETn, CLK_16M )
variable ROMINHSET_LAST : std_logic;
variable ROMINHCLR_LAST : std_logic;
variable ROMDISSET_LAST : std_logic;
@@ -348,28 +406,28 @@ begin
if(Z80_RESETn = '0') then
REG_ROMINH <= '0';
REG_ROMDIS <= '0';
ROMINHSET_LAST := '0';
ROMINHCLR_LAST := '0';
ROMDISSET_LAST := '0';
ROMDISCLR_LAST := '0';
ROMINHSET_LAST := '1';
ROMINHCLR_LAST := '1';
ROMDISSET_LAST := '1';
ROMDISCLR_LAST := '1';
elsif(rising_edge(CLK_16M)) then
if(ROMINHSET_WR_SELni = '0' and ROMINHSET_LAST = '1') then
if(ROMINHSET_WR_SYNC = '0' and ROMINHSET_LAST = '1') then
REG_ROMINH <= '1';
end if;
if(ROMINHCLR_WR_SELni = '0' and ROMINHCLR_LAST = '1') then
if(ROMINHCLR_WR_SYNC = '0' and ROMINHCLR_LAST = '1') then
REG_ROMINH <= '0';
end if;
if(ROMDISSET_WR_SELni = '0' and ROMDISSET_LAST = '1') then
if(ROMDISSET_WR_SYNC = '0' and ROMDISSET_LAST = '1') then
REG_ROMDIS <= '1';
end if;
if(ROMDISCLR_WR_SELni = '0' and ROMDISCLR_LAST = '1') then
if(ROMDISCLR_WR_SYNC = '0' and ROMDISCLR_LAST = '1') then
REG_ROMDIS <= '0';
end if;
ROMINHSET_LAST := ROMINHSET_WR_SELni;
ROMINHCLR_LAST := ROMINHCLR_WR_SELni;
ROMDISSET_LAST := ROMDISSET_WR_SELni;
ROMDISCLR_LAST := ROMDISCLR_WR_SELni;
ROMINHSET_LAST := ROMINHSET_WR_SYNC;
ROMINHCLR_LAST := ROMINHCLR_WR_SYNC;
ROMDISSET_LAST := ROMDISSET_WR_SYNC;
ROMDISCLR_LAST := ROMDISCLR_WR_SYNC;
end if;
end process;
@@ -378,13 +436,19 @@ begin
else '1';
MEM_FXXX_SELni <= '0' when Z80_MREQn = '0' and unsigned(Z80_ADDR(15 downto 8)) >= X"F0" and unsigned(Z80_ADDR(15 downto 8)) <= X"FF"
else '1';
ROM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0' and REG_RAMEN = '0' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
-- ROM/RAM select is now per-region: bit 7 of REG_EXXX_PAGE/REG_FXXX_PAGE determines whether that
-- region maps to FlashROM (bit 7 = 0) or RAM (bit 7 = 1). Each region is independent.
ROM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0'
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '0') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '0'))
else
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A) and REG_RAMEN = '0' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A)
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '0') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '0'))
else '1';
RAM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0' and REG_RAMEN = '1' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
RAM_SELni <= '0' when (IFMODE = MODE_MZ700 or IFMODE = MODE_MZ1500) and REG_ROMINH = '0' and REG_ROMDIS = '0'
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '1') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '1'))
else
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A) and REG_RAMEN = '1' and (MEM_EXXX_SELni = '0' or MEM_FXXX_SELni = '0')
'0' when (IFMODE = MODE_MZ1200 or IFMODE = MODE_MZ80A)
and ((MEM_EXXX_SELni = '0' and REG_EXXX_PAGE(7) = '1') or (MEM_FXXX_SELni = '0' and REG_FXXX_PAGE(7) = '1'))
else '1';
-- I/O Port select.
@@ -406,10 +470,6 @@ begin
else '1';
FXXX_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"61"
else '1';
RAMEN_RD_SELni <= '0' when Z80_IORQn = '0' and Z80_RDn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"62"
else '1';
RAMEN_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"62"
else '1';
MODE_RD_SELni <= '0' when Z80_IORQn = '0' and Z80_RDn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"63"
else '1';
ROMINHSET_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and unsigned(Z80_ADDR(7 downto 0)) = X"E5"
@@ -421,27 +481,54 @@ begin
ROMDISCLR_WR_SELni <= '0' when Z80_IORQn = '0' and Z80_WRn = '0' and (unsigned(Z80_ADDR(7 downto 0)) = X"E3" or unsigned(Z80_ADDR(7 downto 0)) = X"E4")
else '1';
-- Register memory chip selects and ID bus page address on CLK_16M to prevent
-- combinational glitches during Z80 address transitions from reaching the FlashROM/RAM.
-- The FDC data path on ID remains combinational (WD1773 needs immediate response).
MEMCS: process( Z80_RESETn, CLK_16M )
begin
if(Z80_RESETn = '0') then
ROM_CSn_REG <= '1';
RAM_CSn_REG <= '1';
ID_REG <= (others => '0');
ID_OEn <= '1';
elsif(rising_edge(CLK_16M)) then
ROM_CSn_REG <= ROM_SELni;
RAM_CSn_REG <= RAM_SELni;
-- Register the page address (D6:0) and output enable for non-FDC memory accesses.
-- Bit 7 is the ROM/RAM select and is not part of the address.
if(MEM_EXXX_SELni = '0') then
ID_REG <= REG_EXXX_PAGE(6 downto 0) & Z80_ADDR(11);
ID_OEn <= '0';
elsif(MEM_FXXX_SELni = '0') then
ID_REG <= REG_FXXX_PAGE(6 downto 0) & Z80_ADDR(11);
ID_OEn <= '0';
else
ID_OEn <= '1';
end if;
end if;
end process;
-- WD1773 master clock.
CLK_FDC <= CLK_8Mi;
-- Data bus control.
Z80_DATA <= not ID when FDC_SELni = '0' and Z80_WRn = '1' -- WD1773
else
'0' & REG_EXXX_PAGE when EXXX_RD_SELni = '0' -- EXXX Register contents.
REG_EXXX_PAGE when EXXX_RD_SELni = '0' -- EXXX Register: D7=RAM, D6:0=page.
else
'0' & REG_FXXX_PAGE when FXXX_RD_SELni = '0' -- FXXX Register contents.
else
"0000000" & REG_RAMEN when RAMEN_RD_SELni = '0' -- ROM/RAM Enable register contents.
REG_FXXX_PAGE when FXXX_RD_SELni = '0' -- FXXX Register: D7=RAM, D6:0=page.
else
"00000" & MODE when MODE_RD_SELni = '0' -- Configured mode.
else
(others => 'Z');
-- ID is the inverted data bus, the WD1773 uses inverted data. ID doubles up as the FlashROM/RAM upper address bits when not being used for the WD1773.
-- FDC data path remains combinational (WD1773 CSn is also combinational and needs immediate data).
-- Memory page address uses the registered path to prevent glitches during address transitions.
ID <= not Z80_DATA when Z80_WRn = '0' and FDC_SELni = '0'
else
REG_EXXX_PAGE & Z80_ADDR(11) when MEM_EXXX_SELni = '0'
else
REG_FXXX_PAGE & Z80_ADDR(11) when MEM_FXXX_SELni = '0'
ID_REG when ID_OEn = '0'
else
(others => 'Z');
@@ -456,9 +543,9 @@ begin
else Z80_ADDR(10);
RAM_A10 <= Z80_ADDR(10);
-- FlashROM/RAM Chip Select.
ROM_CSn <= ROM_SELni;
RAM_CSn <= RAM_SELni;
-- FlashROM/RAM Chip Select (registered to prevent combinational glitches).
ROM_CSn <= ROM_CSn_REG;
RAM_CSn <= RAM_CSn_REG;
-- Floppy Disk Interface Signals.
FDCn <= FDC_SELni;

View File

@@ -12,6 +12,7 @@
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
--
-- History: July 2023 - Initial write.
-- Apr 2026 - Synchroniser bug fixes.
--
---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify
@@ -112,7 +113,6 @@ package body sfd700_pkg is
else
return b;
end if;
return a;
end function IntMax;
-- Find the number of bits required to represent an integer.
@@ -132,16 +132,17 @@ package body sfd700_pkg is
end function;
-- Function to calculate the number of whole 'clock' cycles in a given time period, the period being in ns.
-- Returns CEIL(period * clock / 1e9), i.e. the minimum number of clock ticks that span the period.
function clockTicks(period : in integer; clock : in integer) return integer is
variable ticks : real;
variable fracTicks : real;
begin
ticks := (Real(period) * Real(clock)) / 1000000000.0;
fracTicks := ticks - CEIL(ticks);
fracTicks := ticks - FLOOR(ticks);
if fracTicks > 0.0001 then
return Integer(CEIL(ticks + 1.0));
return Integer(FLOOR(ticks)) + 1;
else
return Integer(CEIL(ticks));
return Integer(FLOOR(ticks));
end if;
end function;

2
README.md vendored
View File

@@ -6,7 +6,7 @@
<div style="text-align: justify">
<font style="color: green;" size="4">&nbsp; <b>Update February 2024</b> - The v1.2 hardware is fully functional and has been tested on my MZ-80A, MZ-700 and MZ-1500 using the EXT700 adapter and in the MZ-2000 expansion unit. Issues have been reported using the MZ-1U06 which needs to be investigated, seems a CPLD timing issue. The CPLD logic (VHDL) is complete and all ROM/RAM paging and host-mode logic works correctly. The Rom Filing System software ported to the SFD-700 is functional for basic FDC operations (floppy boot via 'F' command) but does not yet include the full suite of floppy disk monitor tools; this is under ongoing development. Client workload has slowed progress — please check the <a href="https://git.eaw.app/eaw/SFD700">Gitea repository</a> for the latest state. The board hasnt been tested in other MZ series machines, hopefully I will do this in due course.</font>
<font style="color: green;" size="4">&nbsp; <b>Update April 2026</b> - A CPLD synchronisation bug was removed and the logic hardened making it more stable. It is advised to put a wire between one of the boards mounting holes and the MZ700 metal chassis as the grounding via the expansion port and joystick 5v pickup is insufficient. The v1.2 hardware is fully functional and has been tested on my MZ-80A, MZ-700 and MZ-1500 using the EXT700 adapter and in the MZ-2000 expansion unit. The CPLD logic (VHDL) is complete and all ROM/RAM paging and host-mode logic works correctly. The Rom Filing System software ported to the SFD-700 is functional for FDC operations (floppy boot via 'FL' command, directory via 'FD' command and a FC command to save memory to a floppy file). The floppy disk code for an MZ700 and MZ80A has been built into the RFS firmware and auto selects based on the jumper configuration. The board hasnt been tested in other MZ series machines, hopefully I will do this in due course.</font>
<div style="padding-top: 0.8em;"></div>
One of the most needed accessories for the Sharp MZ series is the venerable Floppy Disk Controller. These are in relative short supply as not many people used them, mainly due to cost during the Sharp MZ era.