Alpha release candidate rc1

This commit is contained in:
Philip Smart
2023-04-17 21:46:26 +01:00
parent 2200e233e2
commit b0ad5db6c6
59 changed files with 12616 additions and 10889 deletions

23
.gitignore vendored
View File

@@ -1719,4 +1719,27 @@ software/FusionX/src/z80drv/z80drv.ko
software/FusionX/src/z80drv/z80drv.mod.c software/FusionX/src/z80drv/z80drv.mod.c
software/FusionX/host/ software/FusionX/host/
software/FusionX/src/z80drv/sharpbiter software/FusionX/src/z80drv/sharpbiter
CPLD/v1.0/MZ2000/tzpuFusionX.vhd.bak
CPLD/v1.0/MZ2000/tzpuFusionX.vhd.new
CPLD/v1.0/MZ700/tzpuFusionX.vhd.old
CPLD/v1.0/MZ80A/tzpuFusionX.vhd.bak
software/FusionX/src/ttymz/Makefile.kmod
software/FusionX/src/ttymz/z80io.h.old
software/FusionX/src/z80drv/1
software/FusionX/src/z80drv/2
software/FusionX/src/z80drv/Makefile.mod
software/FusionX/src/z80drv/Makefile.x
software/FusionX/src/z80drv/Z80.c.old3
software/FusionX/src/z80drv/Zeta.old3/
software/FusionX/src/z80drv/integral.h
software/FusionX/src/z80drv/src.mz2000/
software/FusionX/src/z80drv/src.mz80a/
software/FusionX/src/z80drv/src.pcw/
software/FusionX/src/z80drv/src/PCW8256_boot.hex
software/FusionX/src/z80drv/src/PCW8256_boot.rom
software/FusionX/src/z80drv/src/ccc
software/FusionX/src/z80drv/src/k64fcpu.c.hld
software/FusionX/src/z80drv/src/z80driver.c.bad
software/FusionX/src/z80drv/src/z80vhw_rfs.c.bad
CPLD/v1.0/MZ2000.old/

View File

@@ -127,10 +127,10 @@ set_location_assignment PIN_21 -to VSOM_RSV[1]
# SOM Control Signals # SOM Control Signals
# =================== # ===================
set_location_assignment PIN_28 -to VSOM_READY set_location_assignment PIN_28 -to VSOM_READY
set_location_assignment PIN_18 -to VSOM_LTSTATE set_location_assignment PIN_19 -to VSOM_LTSTATE
set_location_assignment PIN_27 -to VSOM_BUSRQ set_location_assignment PIN_27 -to VSOM_BUSRQ
set_location_assignment PIN_26 -to VSOM_BUSACK set_location_assignment PIN_26 -to VSOM_BUSACK
set_location_assignment PIN_19 -to VSOM_INT set_location_assignment PIN_18 -to VSOM_INT
set_location_assignment PIN_22 -to VSOM_NMI set_location_assignment PIN_22 -to VSOM_NMI
set_location_assignment PIN_25 -to VSOM_WAIT set_location_assignment PIN_25 -to VSOM_WAIT
set_location_assignment PIN_23 -to VSOM_RESET set_location_assignment PIN_23 -to VSOM_RESET

View File

@@ -2,16 +2,17 @@
-- --
-- Name: tzpuFusionX.vhd -- Name: tzpuFusionX.vhd
-- Version: MZ-2000 -- Version: MZ-2000
-- Created: June 2020 -- Created: Jan 2023
-- Author(s): Philip Smart -- Author(s): Philip Smart
-- Description: tzpuFusionX CPLD logic definition file. -- Description: tzpuFusionX CPLD logic definition file.
-- This module contains the definition of the tzpuFusionX project plus enhancements -- This module contains the definition of the tzpuFusionX project plus enhancements
-- for the MZ-2000. -- for the MZ-2000.
-- --
-- Credits: -- Credits:
-- Copyright: (c) 2018-22 Philip Smart <philip.smart@net2net.org> -- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
-- --
-- History: Oct 2022 - Initial write for the MZ-2000. -- History: Jan 2023 v1.0 - Initial write for the MZ-2000.
-- Apr 2023 v1.1 - Updates from the PCW8256 development.
-- --
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify -- This source file is free software: you can redistribute it and-or modify
@@ -128,7 +129,7 @@ end entity;
architecture rtl of cpld512 is architecture rtl of cpld512 is
-- Finite State Machine states. -- Z80 Finite State Machine states.
type SOMFSMState is type SOMFSMState is
( (
IdleCycle, IdleCycle,
@@ -174,6 +175,14 @@ architecture rtl of cpld512 is
BusReqCycle BusReqCycle
); );
-- Controller FSM states.
type CTRLFSMState is
(
CTRLCMD_Idle,
CTRLCMD_ReadIOWrite,
CTRLCMD_ReadIOWrite_1
);
-- CPU Interface internal signals. -- CPU Interface internal signals.
signal Z80_BUSRQni : std_logic; signal Z80_BUSRQni : std_logic;
signal Z80_INTni : std_logic; signal Z80_INTni : std_logic;
@@ -185,7 +194,6 @@ architecture rtl of cpld512 is
signal Z80_HALTni : std_logic; signal Z80_HALTni : std_logic;
signal Z80_M1ni : std_logic; signal Z80_M1ni : std_logic;
signal Z80_RFSHni : std_logic; signal Z80_RFSHni : std_logic;
signal Z80_DATAi : std_logic_vector(7 downto 0);
signal Z80_BUSRQ_ACKni : std_logic; signal Z80_BUSRQ_ACKni : std_logic;
-- Internal CPU state control. -- Internal CPU state control.
@@ -203,9 +211,10 @@ architecture rtl of cpld512 is
-- Refresh control. -- Refresh control.
signal FSM_STATE : SOMFSMState := IdleCycle; signal FSM_STATE : SOMFSMState := IdleCycle;
signal NEW_SPI_CMD : std_logic := '0'; signal CTRL_STATE : CTRLFSMState := CTRLCMD_Idle;
signal NEW_SPI_DATA : std_logic := '0';
signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11"; signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11";
signal AUTOREFRESH_CNT : integer range 0 to 7; signal AUTOREFRESH_CNT : integer range 0 to 63;
signal FSM_STATUS : std_logic := '0'; signal FSM_STATUS : std_logic := '0';
signal FSM_CHECK_WAIT : std_logic := '0'; signal FSM_CHECK_WAIT : std_logic := '0';
signal FSM_WAIT_ACTIVE : std_logic := '0'; signal FSM_WAIT_ACTIVE : std_logic := '0';
@@ -229,11 +238,12 @@ architecture rtl of cpld512 is
signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register
signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit. signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit.
signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received. signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received.
signal SPI_BIT_CNT : integer range 0 to 16; -- Count of bits tx/rx'd. signal SPI_BIT_CNT : integer range 0 to 7; -- Count of bits tx/rx'd.
signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks). signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks).
-- SPI Command interface. -- SPI Command interface.
signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0'); signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0');
signal SOM_PARAM_CNT : integer range 0 to 3;
signal SPI_NEW_DATA : std_logic; signal SPI_NEW_DATA : std_logic;
signal SPI_PROCESSING : std_logic; signal SPI_PROCESSING : std_logic;
signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0'); signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0');
@@ -275,7 +285,7 @@ begin
-- On the second edge, if occurring within 1 second of the first, the PM_RESET signal to the SOM is triggered, held low for 1 second, -- On the second edge, if occurring within 1 second of the first, the PM_RESET signal to the SOM is triggered, held low for 1 second,
-- forcing the SOM to reboot. -- forcing the SOM to reboot.
SYSRESET: process( Z80_CLKi, Z80_RESETn ) SYSRESET: process( Z80_CLKi, Z80_RESETn )
variable timer1 : integer range 0 to 354000 := 0; variable timer1 : integer range 0 to 400000 := 0;
variable timer100 : integer range 0 to 10 := 0; variable timer100 : integer range 0 to 10 := 0;
variable timerPMReset : integer range 0 to 10 := 0; variable timerPMReset : integer range 0 to 10 := 0;
variable resetCount : integer range 0 to 3 := 0; variable resetCount : integer range 0 to 3 := 0;
@@ -311,7 +321,7 @@ begin
end if; end if;
-- 100ms interval. -- 100ms interval.
if(timer1 = 354000) then if(timer1 = 400000) then
timer100 := timer100 + 1; timer100 := timer100 + 1;
if(timer100 >= 10) then if(timer100 >= 10) then
@@ -340,8 +350,11 @@ begin
-- SPI Slave input. Receive command and data from the SOM. -- SPI Slave input. Receive command and data from the SOM.
SPI_INPUT : process(VSOM_SPI_CLK) SPI_INPUT : process(VSOM_SPI_CLK)
begin begin
-- Chip Select inactive, disable process and reset control flags.
if(VSOM_SPI_CSn = '1') then
-- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge -- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge
if(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then
if(VSOM_SPI_CSn = '0') then if(VSOM_SPI_CSn = '0') then
SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
@@ -356,7 +369,7 @@ begin
elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 3 and SPI_BIT_CNT = 0) then elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 3 and SPI_BIT_CNT = 0) then
SPI_RX_DATA(23 downto 16) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_DATA(23 downto 16) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 4 and SPI_BIT_CNT = 0) then elsif(SPI_FRAME_CNT = 4 and SPI_BIT_CNT = 0) then
SPI_RX_DATA(31 downto 24) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_DATA(31 downto 24) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
end if; end if;
end if; end if;
@@ -366,18 +379,22 @@ begin
-- SPI Slave output. Return the current data set as selected by the input signals XACT. -- SPI Slave output. Return the current data set as selected by the input signals XACT.
SPI_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA) SPI_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA)
begin begin
-- Chip Select inactive, disable process and reset control flags.
if(VSOM_SPI_CSn = '1') then if(VSOM_SPI_CSn = '1') then
SPI_SHIFT_EN <= '0'; SPI_SHIFT_EN <= '0';
SPI_BIT_CNT <= 15; SPI_BIT_CNT <= 7;
-- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => risinge edge -- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => risinge edge
elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = not SPI_CLK_POLARITY) then elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = not SPI_CLK_POLARITY) then
-- Each clock reset the shift enable and done flag in preparation for the next cycle.
SPI_SHIFT_EN <= '1'; SPI_SHIFT_EN <= '1';
-- Bit count decrements to detect when last bit of byte is sent.
if(SPI_BIT_CNT > 0) then if(SPI_BIT_CNT > 0) then
SPI_BIT_CNT <= SPI_BIT_CNT - 1; SPI_BIT_CNT <= SPI_BIT_CNT - 1;
end if; end if;
-- Shift out the next bit.
VSOM_SPI_MISO <= SPI_TX_SREG(6); VSOM_SPI_MISO <= SPI_TX_SREG(6);
SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0'; SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0';
@@ -392,6 +409,7 @@ begin
SPI_FRAME_CNT<= 1; SPI_FRAME_CNT<= 1;
VSOM_SPI_MISO<= SPI_TX_DATA(7); VSOM_SPI_MISO<= SPI_TX_DATA(7);
SPI_TX_SREG <= SPI_TX_DATA(6 downto 0); SPI_TX_SREG <= SPI_TX_DATA(6 downto 0);
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
elsif(SPI_FRAME_CNT = 1) then elsif(SPI_FRAME_CNT = 1) then
SPI_FRAME_CNT<= 2; SPI_FRAME_CNT<= 2;
VSOM_SPI_MISO<= SPI_TX_DATA(15); VSOM_SPI_MISO<= SPI_TX_DATA(15);
@@ -400,13 +418,14 @@ begin
SPI_FRAME_CNT<= 3; SPI_FRAME_CNT<= 3;
VSOM_SPI_MISO<= SPI_TX_DATA(23); VSOM_SPI_MISO<= SPI_TX_DATA(23);
SPI_TX_SREG <= SPI_TX_DATA(22 downto 16); SPI_TX_SREG <= SPI_TX_DATA(22 downto 16);
else elsif(SPI_FRAME_CNT = 3) then
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception. -- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
SPI_FRAME_CNT<= 4; SPI_FRAME_CNT<= 4;
VSOM_SPI_MISO<= SPI_TX_DATA(31); VSOM_SPI_MISO<= SPI_TX_DATA(31);
SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24);
else
SPI_FRAME_CNT<= 0;
end if; end if;
SPI_BIT_CNT <= 7; SPI_BIT_CNT <= 7;
end if; end if;
end if; end if;
@@ -424,6 +443,7 @@ begin
AUTOREFRESH <= '0'; AUTOREFRESH <= '0';
SPI_LOOPBACK_TEST <= '0'; SPI_LOOPBACK_TEST <= '0';
SOM_CMD <= (others => '0'); SOM_CMD <= (others => '0');
SOM_PARAM_CNT <= 0;
SPI_CPU_ADDR <= (others => '0'); SPI_CPU_ADDR <= (others => '0');
SPI_NEW_DATA <= '0'; SPI_NEW_DATA <= '0';
@@ -432,6 +452,7 @@ begin
-- for 8bit, 16bit and 32bit transmissions. -- for 8bit, 16bit and 32bit transmissions.
-- The packet is formatted as follows: -- The packet is formatted as follows:
-- --
-- < SPI_CPU_ADDR > < SPI_CPU_DATA >< SOM_CMD>
-- < SPI_FRAME_CNT=4 >< SPI_FRAME=3 > < SPI_FRAME_CNT=2 >< SPI_FRAME_CNT=1> -- < SPI_FRAME_CNT=4 >< SPI_FRAME=3 > < SPI_FRAME_CNT=2 >< SPI_FRAME_CNT=1>
-- < 16bit Z80 Address > < Z80 Data ><Command=00..80> -- < 16bit Z80 Address > < Z80 Data ><Command=00..80>
-- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
@@ -441,6 +462,13 @@ begin
-- --
elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then
-- If active, decrement parameter count. Parameters sent after a command are not considered as commands.
if(SOM_PARAM_CNT > 0) then
SOM_PARAM_CNT <= SOM_PARAM_CNT - 1;
end if;
-- Process if command, store parameters.
if(SOM_PARAM_CNT = 0) then
-- Command is always located in the upper byte of frame 1. -- Command is always located in the upper byte of frame 1.
SOM_CMD <= SPI_RX_DATA(7 downto 0); SOM_CMD <= SPI_RX_DATA(7 downto 0);
@@ -454,27 +482,53 @@ begin
when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch
X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write
X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read
X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" | -- WriteIO
X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" | --
X"40" | X"41" | X"42" | X"43" | X"44" | X"45" | X"46" | X"47" | --
X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" =>
-- Direct address set. -- Direct address set.
if(SPI_FRAME_CNT = 4) then if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else else
-- if(SPI_CPU_ADDR >= X"D010" and SPI_CPU_ADDR < X"D020") then
-- SPI_CPU_ADDR <= std_logic_vector(X"D020" + unsigned(SPI_RX_DATA(2 downto 0)));
-- else
SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0)));
-- end if;
end if; end if;
if(SPI_FRAME_CNT > 1) then if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if; end if;
when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" => -- ReadIO-Write
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= X"00" & SPI_RX_DATA(15 downto 8);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
-- ReadIO-Write, Read-WriteIO commands require target address, so indicate parameter needed.
if(SPI_RX_DATA(7 downto 0) >= X"38" and SPI_RX_DATA(7 downto 0) < X"40") then
SOM_PARAM_CNT <= 1;
end if;
-- SETSIGSET1: Set control lines directly. -- SETSIGSET1: Set control lines directly.
when X"F0" => when X"F0" =>
VIDEO_SRCi <= SPI_RX_DATA(8); VIDEO_SRCi <= SPI_RX_DATA(8);
@@ -501,6 +555,17 @@ begin
when others => when others =>
end case; end case;
else
-- Store parameter depending on number of frames, either ADDR+DATA, ADDR or DATA.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= SPI_RX_DATA(15 downto 0);
else
SPI_CPU_DATA <= SPI_RX_DATA(7 downto 0);
end if;
end if;
end if; end if;
end process; end process;
@@ -561,9 +626,9 @@ begin
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
CPU_DATA_IN <= (others => '0'); CPU_DATA_IN <= (others => '0');
REFRESH_ADDR <= (others => '0'); REFRESH_ADDR <= (others => '0');
AUTOREFRESH_CNT <= 7; AUTOREFRESH_CNT <= 63;
IPAR <= (others => '0'); IPAR <= (others => '0');
NEW_SPI_CMD <= '0'; NEW_SPI_DATA <= '0';
VCPU_CS_EDGE <= "11"; VCPU_CS_EDGE <= "11";
SPI_PROCESSING <= '0'; SPI_PROCESSING <= '0';
@@ -580,11 +645,28 @@ begin
-- New command, set flag as the signal is only 1 clock wide. -- New command, set flag as the signal is only 1 clock wide.
if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then
NEW_SPI_CMD <= '1'; NEW_SPI_DATA <= '1';
end if;
-- Decrement refresh counter on each Z80 cycle, thus when idle and time expired, a refresh can be performed within parameters (256 cycles in 4ms).
if(AUTOREFRESH = '1' and AUTOREFRESH_CNT /= 0 and Z80_CLK_RE = '1') then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
end if;
-- Refresh status bit. Indicates a Refresh cycle is under way.
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_11 or FSM_STATE = RefreshCycle_20 or FSM_STATE = RefreshCycle_21 or FSM_STATE = RefreshCycle_3 then
RFSH_STATUS <= '1';
else
RFSH_STATUS <= '0';
end if;
-- If we are in a WAIT sampling 1/2 cycle and wait goes active, set the state so we repeat the full clock cycle by winding back 2 places.
if(FSM_CHECK_WAIT = '1' and Z80_WAITn = '0' and Z80_CLK_TGL = '0') then
FSM_WAIT_ACTIVE <= '1';
end if; end if;
-- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default. -- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default.
if(FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) then if((FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) and Z80_CLK_RE = '1') then
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
Z80_MREQni <= '1'; Z80_MREQni <= '1';
Z80_IORQni <= '1'; Z80_IORQni <= '1';
@@ -594,24 +676,34 @@ begin
FSM_STATUS <= '0'; FSM_STATUS <= '0';
Z80_RFSHni <= '1'; Z80_RFSHni <= '1';
-- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences.
-- This will be overriden if the SPI receives a new command. -- This period may be extended if the SPI receives a new command.
-- --
if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
if(AUTOREFRESH_CNT = 0) then if(AUTOREFRESH_CNT = 0) then
FSM_STATE <= RefreshCycle_3; FSM_STATE <= RefreshCycle_3;
FSM_STATUS<= '1';
-- 4164 DRAM = 256 cycles in 4ms.
AUTOREFRESH_CNT <= 63;
end if; end if;
end if; end if;
end if; end if;
--------------------------------------------------------------------------------------------
-- CPLD Macro Command Finite State Machine.
--------------------------------------------------------------------------------------------
-- Controller state machine.
-- When idle, accept and process SPI commands which can lead to a controller macro command.
case CTRL_STATE is
when CTRLCMD_Idle =>
-- If new command has been given and the FSM enters idle state, load up new command for processing. -- If new command has been given and the FSM enters idle state, load up new command for processing.
if(NEW_SPI_CMD = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
NEW_SPI_CMD <= '0';
-- Store new address and data for this command. -- Store new address and data for this command.
if(NEW_SPI_DATA = '1') then
CPU_ADDR <= SPI_CPU_ADDR; CPU_ADDR <= SPI_CPU_ADDR;
if(SPI_CPU_DATA /= CPU_DATA_OUT) then
CPU_DATA_OUT <= SPI_CPU_DATA; CPU_DATA_OUT <= SPI_CPU_DATA;
end if; end if;
@@ -642,6 +734,11 @@ begin
-- Initiate a Read IO Cycle. -- Initiate a Read IO Cycle.
FSM_STATE <= ReadIOCycle; FSM_STATE <= ReadIOCycle;
when X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" =>
-- Initiate a read IO write memory cycle via the controller FSM.
CTRL_STATE <= CTRLCMD_ReadIOWrite;
FSM_STATE <= ReadIOCycle;
when X"50" => when X"50" =>
-- Register a Halt state. -- Register a Halt state.
FSM_STATE <= HaltCycle; FSM_STATE <= HaltCycle;
@@ -670,23 +767,41 @@ begin
SPI_PROCESSING<= not SPI_PROCESSING; SPI_PROCESSING<= not SPI_PROCESSING;
end if; end if;
-- Clear new data flag ready for next cmd/param transfer.
NEW_SPI_DATA <= '0';
-- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion.
FSM_STATUS <= '1'; FSM_STATUS <= '1';
end if; end if;
-- Refresh status bit. Indicates a Refresh cycle is under way. when CTRLCMD_ReadIOWrite =>
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_11 or FSM_STATE = RefreshCycle_20 or FSM_STATE = RefreshCycle_21 or FSM_STATE = RefreshCycle_3 then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
RFSH_STATUS <= '1'; NEW_SPI_DATA <= '0';
else CPU_DATA_EN <= '0';
RFSH_STATUS <= '0'; Z80_IORQni <= '1';
Z80_RDni <= '1';
Z80_RFSHni <= '1';
CPU_ADDR <= SPI_CPU_ADDR;
CPU_DATA_OUT <= CPU_DATA_IN;
FSM_STATE <= WriteCycle;
CTRL_STATE <= CTRLCMD_ReadIOWrite_1;
end if; end if;
-- If we are in a WAIT sampling 1/2 cycle and wait goes active, set the state so we repeat the full clock cycle by winding back 2 places. when CTRLCMD_ReadIOWrite_1 =>
if(FSM_CHECK_WAIT = '1' and Z80_WAITn = '0' and Z80_CLK_TGL = '0') then if(FSM_STATE = WriteCycle_31) then
FSM_WAIT_ACTIVE <= '1'; CTRL_STATE <= CTRLCMD_Idle;
end if; end if;
-- On each Z80 edge we advance the FSM to recreate the Z80 external signal transactions. when others =>
CTRL_STATE <= CTRLCMD_Idle;
end case;
--------------------------------------------------------------------------------------------
-- Z80 Finite State Machine.
--------------------------------------------------------------------------------------------
-- On each Z80 edge we advance the Z80 FSM to recreate the Z80 external signal transactions.
if(Z80_CLK_TGL = '1') then if(Z80_CLK_TGL = '1') then
-- The FSM advances to the next stage on each Z80 edge unless in Idle state. -- The FSM advances to the next stage on each Z80 edge unless in Idle state.
@@ -704,7 +819,7 @@ begin
when IdleCycle => when IdleCycle =>
CPU_LAST_T_STATE <= '1'; CPU_LAST_T_STATE <= '1';
-- FSM_STATE <= IdleCycle; FSM_STATUS <= '0';
----------------------------- -----------------------------
-- Z80 Fetch Cycle. -- Z80 Fetch Cycle.
@@ -727,7 +842,7 @@ begin
when FetchCycle_30 => when FetchCycle_30 =>
-- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until -- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until
-- a short period before the falling edge of T3 (could be an MZ-2000 design restriction or the Z80 timing diagrams are a bit out). -- a short period before the falling edge of T3.
FSM_STATE <= RefreshCycle; FSM_STATE <= RefreshCycle;
----------------------------- -----------------------------
@@ -736,18 +851,18 @@ begin
when RefreshCycle => when RefreshCycle =>
-- Latch data from mainboard. -- Latch data from mainboard.
CPU_DATA_IN <= Z80_DATA; CPU_DATA_IN <= Z80_DATA;
FSM_STATUS <= '0';
Z80_RFSHni <= '0'; Z80_RFSHni <= '0';
when RefreshCycle_11 => when RefreshCycle_11 =>
-- Falling edge of T3 activates the MREQ line. -- Falling edge of T3 activates the MREQ line.
Z80_MREQni <= '0'; Z80_MREQni <= '0';
FSM_STATUS <= '0';
when RefreshCycle_20 => when RefreshCycle_20 =>
when RefreshCycle_21 => when RefreshCycle_21 =>
Z80_MREQni <= '1'; Z80_MREQni <= '1';
REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1; REFRESH_ADDR <= REFRESH_ADDR + 1;
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
when RefreshCycle_3 => when RefreshCycle_3 =>
@@ -805,7 +920,6 @@ begin
FSM_STATUS <= '0'; FSM_STATUS <= '0';
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
----------------------------- -----------------------------
-- Z80 IO Write Cycle. -- Z80 IO Write Cycle.
----------------------------- -----------------------------
@@ -825,7 +939,7 @@ begin
when WriteIOCycle_31 => when WriteIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= WriteIOCycle_20; FSM_STATE <= WriteIOCycle_30;
end if; end if;
when WriteIOCycle_40 => when WriteIOCycle_40 =>
@@ -854,7 +968,7 @@ begin
when ReadIOCycle_31 => when ReadIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= ReadIOCycle_20; FSM_STATE <= ReadIOCycle_30;
end if; end if;
when ReadIOCycle_40 => when ReadIOCycle_40 =>
@@ -902,8 +1016,6 @@ begin
Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1'
else else
(others => 'Z'); (others => 'Z');
-- Z80_DATAi <= Z80_DATA when Z80_RDn = '0'
-- else (others => '1');
Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1'
else 'Z'; else 'Z';
Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1'
@@ -982,4 +1094,8 @@ begin
--VGA_PXL_CLK <= CLK_50M; --VGA_PXL_CLK <= CLK_50M;
MONO_PXL_CLK <= VGA_PXL_CLK; MONO_PXL_CLK <= VGA_PXL_CLK;
-- Currently unassigned.
VGA_COLR <= '0';
MONO_RSV <= '0';
end architecture; end architecture;

View File

@@ -127,10 +127,10 @@ set_location_assignment PIN_21 -to VSOM_RSV[1]
# SOM Control Signals # SOM Control Signals
# =================== # ===================
set_location_assignment PIN_28 -to VSOM_READY set_location_assignment PIN_28 -to VSOM_READY
set_location_assignment PIN_18 -to VSOM_LTSTATE set_location_assignment PIN_19 -to VSOM_LTSTATE
set_location_assignment PIN_27 -to VSOM_BUSRQ set_location_assignment PIN_27 -to VSOM_BUSRQ
set_location_assignment PIN_26 -to VSOM_BUSACK set_location_assignment PIN_26 -to VSOM_BUSACK
set_location_assignment PIN_19 -to VSOM_INT set_location_assignment PIN_18 -to VSOM_INT
set_location_assignment PIN_22 -to VSOM_NMI set_location_assignment PIN_22 -to VSOM_NMI
set_location_assignment PIN_25 -to VSOM_WAIT set_location_assignment PIN_25 -to VSOM_WAIT
set_location_assignment PIN_23 -to VSOM_RESET set_location_assignment PIN_23 -to VSOM_RESET

View File

@@ -2,16 +2,17 @@
-- --
-- Name: tzpuFusionX.vhd -- Name: tzpuFusionX.vhd
-- Version: MZ-700 -- Version: MZ-700
-- Created: June 2020 -- Created: Jan 2023
-- Author(s): Philip Smart -- Author(s): Philip Smart
-- Description: tzpuFusionX CPLD logic definition file. -- Description: tzpuFusionX CPLD logic definition file.
-- This module contains the definition of the tzpuFusionX project plus enhancements -- This module contains the definition of the tzpuFusionX project plus enhancements
-- for the MZ700. -- for the MZ-700.
-- --
-- Credits: -- Credits:
-- Copyright: (c) 2018-22 Philip Smart <philip.smart@net2net.org> -- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
-- --
-- History: Oct 2022 - Initial write for the MZ-700. -- History: Jan 2023 v1.0 - Initial write for the MZ-700.
-- Apr 2023 v1.1 - Updates from the PCW8256 development.
-- --
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify -- This source file is free software: you can redistribute it and-or modify
@@ -128,42 +129,62 @@ end entity;
architecture rtl of cpld512 is architecture rtl of cpld512 is
-- Finite State Machine states. -- Z80 Finite State Machine states.
type SOMFSMState is type SOMFSMState is
( (
IdleCycle, IdleCycle,
FetchCycle, FetchCycle,
FetchCycle_2, FetchCycle_11,
FetchCycle_3, FetchCycle_20,
FetchCycle_4, FetchCycle_21,
FetchCycle_30,
RefreshCycle, RefreshCycle,
RefreshCycle_1, RefreshCycle_11,
RefreshCycle_2, RefreshCycle_20,
RefreshCycle_21,
RefreshCycle_3, RefreshCycle_3,
WriteCycle, WriteCycle,
WriteCycle_2, WriteCycle_11,
WriteCycle_3, WriteCycle_20,
WriteCycle_4, WriteCycle_21,
WriteCycle_30,
WriteCycle_31,
ReadCycle, ReadCycle,
ReadCycle_2, ReadCycle_11,
ReadCycle_3, ReadCycle_20,
ReadCycle_4, ReadCycle_21,
ReadCycle_30,
ReadCycle_31,
WriteIOCycle, WriteIOCycle,
WriteIOCycle_2, WriteIOCycle_11,
WriteIOCycle_3, WriteIOCycle_20,
WriteIOCycle_4, WriteIOCycle_21,
WriteIOCycle_5, WriteIOCycle_30,
WriteIOCycle_31,
WriteIOCycle_40,
WriteIOCycle_41,
ReadIOCycle, ReadIOCycle,
ReadIOCycle_2, ReadIOCycle_11,
ReadIOCycle_3, ReadIOCycle_20,
ReadIOCycle_4, ReadIOCycle_21,
ReadIOCycle_30,
ReadIOCycle_31,
ReadIOCycle_40,
ReadIOCycle_41,
HaltCycle, HaltCycle,
BusReqCycle BusReqCycle
); );
-- Controller FSM states.
type CTRLFSMState is
(
CTRLCMD_Idle,
CTRLCMD_ReadIOWrite,
CTRLCMD_ReadIOWrite_1
);
-- CPU Interface internal signals. -- CPU Interface internal signals.
signal Z80_BUSRQni : std_logic; signal Z80_BUSRQni : std_logic;
signal Z80_BUSAKni : std_logic;
signal Z80_INTni : std_logic; signal Z80_INTni : std_logic;
signal Z80_IORQni : std_logic; signal Z80_IORQni : std_logic;
signal Z80_MREQni : std_logic; signal Z80_MREQni : std_logic;
@@ -171,10 +192,8 @@ architecture rtl of cpld512 is
signal Z80_RDni : std_logic; signal Z80_RDni : std_logic;
signal Z80_WRni : std_logic; signal Z80_WRni : std_logic;
signal Z80_HALTni : std_logic; signal Z80_HALTni : std_logic;
signal Z80_WAITni : std_logic;
signal Z80_M1ni : std_logic; signal Z80_M1ni : std_logic;
signal Z80_RFSHni : std_logic; signal Z80_RFSHni : std_logic;
signal Z80_DATAi : std_logic_vector(7 downto 0);
signal Z80_BUSRQ_ACKni : std_logic; signal Z80_BUSRQ_ACKni : std_logic;
-- Internal CPU state control. -- Internal CPU state control.
@@ -192,22 +211,24 @@ architecture rtl of cpld512 is
-- Refresh control. -- Refresh control.
signal FSM_STATE : SOMFSMState := IdleCycle; signal FSM_STATE : SOMFSMState := IdleCycle;
signal NEW_SPI_CMD : std_logic := '0'; signal CTRL_STATE : CTRLFSMState := CTRLCMD_Idle;
signal NEW_SPI_DATA : std_logic := '0';
signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11"; signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11";
signal AUTOREFRESH_CNT : integer range 0 to 7; signal AUTOREFRESH_CNT : integer range 0 to 63;
signal FSM_STATUS : std_logic := '0'; signal FSM_STATUS : std_logic := '0';
signal FSM_CHECK_WAIT : std_logic := '0';
signal FSM_WAIT_ACTIVE : std_logic := '0';
signal RFSH_STATUS : std_logic := '0'; signal RFSH_STATUS : std_logic := '0';
signal REFRESH_ADDR : std_logic_vector(7 downto 0); signal REFRESH_ADDR : std_logic_vector(7 downto 0);
signal IPAR : std_logic_vector(7 downto 0); signal IPAR : std_logic_vector(7 downto 0);
signal AUTOREFRESH : std_logic; signal AUTOREFRESH : std_logic;
-- Clock edge detection and flagging. -- Clock edge detection and flagging.
signal Z80_CLK_RE : std_logic := '0'; signal Z80_CLKi : std_logic;
signal Z80_CLK_FE : std_logic := '0'; signal Z80_CLK_LAST : std_logic_vector(1 downto 0);
signal Z80_CLK_LEVEL : std_logic := '0'; signal Z80_CLK_RE : std_logic;
signal Z80_CLK_LAST : std_logic := '0'; signal Z80_CLK_FE : std_logic;
signal CPU_T_STATE : integer range 0 to 5; signal Z80_CLK_TGL : std_logic;
signal CPU_T_STATES : integer range 0 to 5;
signal CPU_T_STATE_SET : integer range 0 to 5; signal CPU_T_STATE_SET : integer range 0 to 5;
signal CPU_LAST_T_STATE : std_logic := '0'; signal CPU_LAST_T_STATE : std_logic := '0';
@@ -217,11 +238,12 @@ architecture rtl of cpld512 is
signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register
signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit. signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit.
signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received. signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received.
signal SPI_BIT_CNT : integer range 0 to 16; -- Count of bits tx/rx'd. signal SPI_BIT_CNT : integer range 0 to 7; -- Count of bits tx/rx'd.
signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks). signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks).
-- SPI Command interface. -- SPI Command interface.
signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0'); signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0');
signal SOM_PARAM_CNT : integer range 0 to 3;
signal SPI_NEW_DATA : std_logic; signal SPI_NEW_DATA : std_logic;
signal SPI_PROCESSING : std_logic; signal SPI_PROCESSING : std_logic;
signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0'); signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0');
@@ -256,7 +278,7 @@ begin
-- On the first edge the VSOM_RESETn signal is set which allows the SOM to see it and the Z80 application to enter a reset state. -- On the first edge the VSOM_RESETn signal is set which allows the SOM to see it and the Z80 application to enter a reset state.
-- On the second edge, if occurring within 1 second of the first, the PM_RESET signal to the SOM is triggered, held low for 1 second, -- On the second edge, if occurring within 1 second of the first, the PM_RESET signal to the SOM is triggered, held low for 1 second,
-- forcing the SOM to reboot. -- forcing the SOM to reboot.
SYSRESET: process( Z80_CLK, Z80_RESETn ) SYSRESET: process( Z80_CLKi, Z80_RESETn )
variable timer1 : integer range 0 to 354000 := 0; variable timer1 : integer range 0 to 354000 := 0;
variable timer100 : integer range 0 to 10 := 0; variable timer100 : integer range 0 to 10 := 0;
variable timerPMReset : integer range 0 to 10 := 0; variable timerPMReset : integer range 0 to 10 := 0;
@@ -264,7 +286,7 @@ begin
variable cpuResetEdge : std_logic := '1'; variable cpuResetEdge : std_logic := '1';
begin begin
-- Synchronous on the HOST Clock. -- Synchronous on the HOST Clock.
if(rising_edge(Z80_CLK)) then if(rising_edge(Z80_CLKi)) then
-- If the PM Reset timer is active, count down and on expiry release the SOM PM_RESET line. -- If the PM Reset timer is active, count down and on expiry release the SOM PM_RESET line.
if(timerPMReset = 0 and PM_RESETi = '1') then if(timerPMReset = 0 and PM_RESETi = '1') then
@@ -322,8 +344,11 @@ begin
-- SPI Slave input. Receive command and data from the SOM. -- SPI Slave input. Receive command and data from the SOM.
SPI_INPUT : process(VSOM_SPI_CLK) SPI_INPUT : process(VSOM_SPI_CLK)
begin begin
-- Chip Select inactive, disable process and reset control flags.
if(VSOM_SPI_CSn = '1') then
-- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge -- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge
if(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then
if(VSOM_SPI_CSn = '0') then if(VSOM_SPI_CSn = '0') then
SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
@@ -338,7 +363,7 @@ begin
elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 3 and SPI_BIT_CNT = 0) then elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 3 and SPI_BIT_CNT = 0) then
SPI_RX_DATA(23 downto 16) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_DATA(23 downto 16) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 4 and SPI_BIT_CNT = 0) then elsif(SPI_FRAME_CNT = 4 and SPI_BIT_CNT = 0) then
SPI_RX_DATA(31 downto 24) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_DATA(31 downto 24) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
end if; end if;
end if; end if;
@@ -348,18 +373,22 @@ begin
-- SPI Slave output. Return the current data set as selected by the input signals XACT. -- SPI Slave output. Return the current data set as selected by the input signals XACT.
SPI_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA) SPI_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA)
begin begin
-- Chip Select inactive, disable process and reset control flags.
if(VSOM_SPI_CSn = '1') then if(VSOM_SPI_CSn = '1') then
SPI_SHIFT_EN <= '0'; SPI_SHIFT_EN <= '0';
SPI_BIT_CNT <= 15; SPI_BIT_CNT <= 7;
-- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => risinge edge -- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => risinge edge
elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = not SPI_CLK_POLARITY) then elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = not SPI_CLK_POLARITY) then
-- Each clock reset the shift enable and done flag in preparation for the next cycle.
SPI_SHIFT_EN <= '1'; SPI_SHIFT_EN <= '1';
-- Bit count decrements to detect when last bit of byte is sent.
if(SPI_BIT_CNT > 0) then if(SPI_BIT_CNT > 0) then
SPI_BIT_CNT <= SPI_BIT_CNT - 1; SPI_BIT_CNT <= SPI_BIT_CNT - 1;
end if; end if;
-- Shift out the next bit.
VSOM_SPI_MISO <= SPI_TX_SREG(6); VSOM_SPI_MISO <= SPI_TX_SREG(6);
SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0'; SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0';
@@ -374,6 +403,7 @@ begin
SPI_FRAME_CNT<= 1; SPI_FRAME_CNT<= 1;
VSOM_SPI_MISO<= SPI_TX_DATA(7); VSOM_SPI_MISO<= SPI_TX_DATA(7);
SPI_TX_SREG <= SPI_TX_DATA(6 downto 0); SPI_TX_SREG <= SPI_TX_DATA(6 downto 0);
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
elsif(SPI_FRAME_CNT = 1) then elsif(SPI_FRAME_CNT = 1) then
SPI_FRAME_CNT<= 2; SPI_FRAME_CNT<= 2;
VSOM_SPI_MISO<= SPI_TX_DATA(15); VSOM_SPI_MISO<= SPI_TX_DATA(15);
@@ -382,13 +412,14 @@ begin
SPI_FRAME_CNT<= 3; SPI_FRAME_CNT<= 3;
VSOM_SPI_MISO<= SPI_TX_DATA(23); VSOM_SPI_MISO<= SPI_TX_DATA(23);
SPI_TX_SREG <= SPI_TX_DATA(22 downto 16); SPI_TX_SREG <= SPI_TX_DATA(22 downto 16);
else elsif(SPI_FRAME_CNT = 3) then
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception. -- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
SPI_FRAME_CNT<= 4; SPI_FRAME_CNT<= 4;
VSOM_SPI_MISO<= SPI_TX_DATA(31); VSOM_SPI_MISO<= SPI_TX_DATA(31);
SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24);
else
SPI_FRAME_CNT<= 0;
end if; end if;
SPI_BIT_CNT <= 7; SPI_BIT_CNT <= 7;
end if; end if;
end if; end if;
@@ -406,6 +437,7 @@ begin
AUTOREFRESH <= '1'; AUTOREFRESH <= '1';
SPI_LOOPBACK_TEST <= '0'; SPI_LOOPBACK_TEST <= '0';
SOM_CMD <= (others => '0'); SOM_CMD <= (others => '0');
SOM_PARAM_CNT <= 0;
SPI_CPU_ADDR <= (others => '0'); SPI_CPU_ADDR <= (others => '0');
SPI_NEW_DATA <= '0'; SPI_NEW_DATA <= '0';
@@ -414,15 +446,23 @@ begin
-- for 8bit, 16bit and 32bit transmissions. -- for 8bit, 16bit and 32bit transmissions.
-- The packet is formatted as follows: -- The packet is formatted as follows:
-- --
-- < SPI_CPU_ADDR > < SPI_CPU_DATA >< SOM_CMD>
-- < SPI_FRAME_CNT=4 >< SPI_FRAME=3 > < SPI_FRAME_CNT=2 >< SPI_FRAME_CNT=1> -- < SPI_FRAME_CNT=4 >< SPI_FRAME=3 > < SPI_FRAME_CNT=2 >< SPI_FRAME_CNT=1>
-- < 16bit Z80 Address > < Z80 Data ><Command=00..80> -- < 16bit Z80 Address > < Z80 Data ><Command=00..80>
-- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 2 0 -- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
-- --
-- < > < Data ><Command=F0..FF> -- < > < Data ><Command=F0..FF>
-- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 2 0 -- 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
-- --
elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then
-- If active, decrement parameter count. Parameters sent after a command are not considered as commands.
if(SOM_PARAM_CNT > 0) then
SOM_PARAM_CNT <= SOM_PARAM_CNT - 1;
end if;
-- Process if command, store parameters.
if(SOM_PARAM_CNT = 0) then
-- Command is always located in the upper byte of frame 1. -- Command is always located in the upper byte of frame 1.
SOM_CMD <= SPI_RX_DATA(7 downto 0); SOM_CMD <= SPI_RX_DATA(7 downto 0);
@@ -436,23 +476,53 @@ begin
when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch
X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write
X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read
X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" | -- WriteIO
X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" | --
X"40" | X"41" | X"42" | X"43" | X"44" | X"45" | X"46" | X"47" | --
X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" =>
-- Direct address set. -- Direct address set.
if(SPI_FRAME_CNT = 4) then if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else else
SPI_CPU_ADDR <= std_logic_vector(unsigned(CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0)));
end if; end if;
if(SPI_FRAME_CNT > 1) then if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if; end if;
when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" => -- ReadIO-Write
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= X"00" & SPI_RX_DATA(15 downto 8);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
-- ReadIO-Write, Read-WriteIO commands require target address, so indicate parameter needed.
if(SPI_RX_DATA(7 downto 0) >= X"38" and SPI_RX_DATA(7 downto 0) < X"40") then
SOM_PARAM_CNT <= 1;
end if;
-- SETSIGSET1: Set control lines directly. -- SETSIGSET1: Set control lines directly.
when X"F0" => when X"F0" =>
VIDEO_SRCi <= SPI_RX_DATA(8); VIDEO_SRCi <= SPI_RX_DATA(8);
@@ -479,60 +549,50 @@ begin
when others => when others =>
end case; end case;
else
-- Store parameter depending on number of frames, either ADDR+DATA, ADDR or DATA.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= SPI_RX_DATA(15 downto 0);
else
SPI_CPU_DATA <= SPI_RX_DATA(7 downto 0);
end if;
end if;
end if; end if;
end process; end process;
-- Z80 Clock edge detection. Each falling and rising edge sets a flag for 1 cycle along with the current clock level flag. -- Process to detect the Z80 Clock edges. Each edge is used to recreate the Z80 external signals.
-- On expiry of the defined T-States, a flag is raised to indicate end of cycle. --
CLKEDGE: process( CLK_50M, Z80_RESETn, Z80_CLK, CPU_T_STATE_SET ) Z80CLK: process( CLK_50M, Z80_CLKi, Z80_RESETn )
begin begin
if(Z80_RESETn = '0') then if(Z80_RESETn = '0') then
Z80_CLK_RE <= '0'; Z80_CLK_RE <= '1';
Z80_CLK_FE <= '0'; Z80_CLK_FE <= '1';
Z80_CLK_LEVEL <= '0'; Z80_CLK_TGL <= '1';
CPU_T_STATE <= 1;
CPU_LAST_T_STATE <= '0';
elsif(rising_edge(CLK_50M)) then elsif(rising_edge(CLK_50M)) then
-- Host clock edge detection. For Z80 operations the clock edge is required to meet host and original timings.
if(Z80_CLK = '1' and Z80_CLK_LAST = '0') then
Z80_CLK_RE <= '1';
Z80_CLK_LEVEL <= '1';
-- T state increments on each rising edge unless WAIT asserted. -- Default is to clear the signals, only active for 1 clock period.
if(Z80_WAITni = '1') then
-- Wrap around to next T-State if limit for last transaction type reached.
if(CPU_T_STATE = CPU_T_STATES) then
CPU_T_STATE <= 1;
CPU_LAST_T_STATE <= '0';
else
CPU_T_STATE <= CPU_T_STATE + 1;
end if;
end if;
elsif(Z80_CLK = '0' and Z80_CLK_LAST = '1') then
Z80_CLK_FE <= '1';
Z80_CLK_LEVEL <= '0';
-- Falling edge of the last T-State sets the Last T-State flag. This gives the SOM sufficient time to detect it
-- and execute necessary code before next T-State commences.
if(CPU_T_STATE = CPU_T_STATES) then
CPU_LAST_T_STATE <= '1';
end if;
else
Z80_CLK_RE <= '0'; Z80_CLK_RE <= '0';
Z80_CLK_FE <= '0'; Z80_CLK_FE <= '0';
end if; Z80_CLK_TGL <= '0';
-- Mechanism to set the T-State. -- Rising Edge.
if(CPU_T_STATE_SET /= 0) then if(Z80_CLKi = '1' and Z80_CLK_LAST = "00") then
CPU_T_STATE <= CPU_T_STATE_SET; Z80_CLK_RE <= '1';
CPU_LAST_T_STATE <= '0';
end if;
Z80_CLK_LAST <= Z80_CLK; -- Toggle on rising edge is delayed by one clock to allow time for command to be decoded.
elsif(Z80_CLKi = '1' and Z80_CLK_LAST = "01") then
Z80_CLK_TGL <= '1';
-- Falling Edge.
elsif(Z80_CLKi = '0' and Z80_CLK_LAST = "11") then
Z80_CLK_FE <= '1';
Z80_CLK_TGL <= '1';
end if;
Z80_CLK_LAST <= Z80_CLK_LAST(0) & Z80_CLKi;
end if; end if;
end process; end process;
@@ -541,10 +601,9 @@ begin
-- A command processor, based on an FSM concept, to process requested commands, ie. Z80 Write, Z80 Read etc. -- A command processor, based on an FSM concept, to process requested commands, ie. Z80 Write, Z80 Read etc.
-- The external signal SOM_CMD_EN, when set, indicates a new command available in SOM_CMD. -- The external signal SOM_CMD_EN, when set, indicates a new command available in SOM_CMD.
-- --
SOMFSM: process( CLK_50M, Z80_RESETn ) SOMFSM: process( CLK_50M, Z80_CLKi, Z80_RESETn )
begin begin
if(Z80_RESETn = '0') then if(Z80_RESETn = '0') then
Z80_BUSAKni <= '1';
Z80_IORQni <= '1'; Z80_IORQni <= '1';
Z80_MREQni <= '1'; Z80_MREQni <= '1';
Z80_RDni <= '1'; Z80_RDni <= '1';
@@ -553,25 +612,22 @@ begin
Z80_M1ni <= '1'; Z80_M1ni <= '1';
Z80_RFSHni <= '1'; Z80_RFSHni <= '1';
Z80_BUSRQ_ACKni <= '1'; Z80_BUSRQ_ACKni <= '1';
FSM_CHECK_WAIT <= '0';
FSM_WAIT_ACTIVE <= '0';
FSM_STATUS <= '0'; FSM_STATUS <= '0';
FSM_STATE <= IdleCycle;
RFSH_STATUS <= '0'; RFSH_STATUS <= '0';
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
CPU_DATA_IN <= (others => '0'); CPU_DATA_IN <= (others => '0');
REFRESH_ADDR <= (others => '0'); REFRESH_ADDR <= (others => '0');
AUTOREFRESH_CNT <= 7; AUTOREFRESH_CNT <= 63;
IPAR <= (others => '0'); IPAR <= (others => '0');
FSM_STATE <= IdleCycle; NEW_SPI_DATA <= '0';
NEW_SPI_CMD <= '0';
VCPU_CS_EDGE <= "11"; VCPU_CS_EDGE <= "11";
CPU_T_STATES <= 3;
CPU_T_STATE_SET <= 0;
SPI_PROCESSING <= '0'; SPI_PROCESSING <= '0';
elsif(rising_edge(CLK_50M)) then elsif(rising_edge(CLK_50M)) then
-- Setup of T State is one cycle wide.
CPU_T_STATE_SET <= 0;
-- Bus request mechanism. If an externel Bus Request comes in and the FSM is idle, run the Bus Request command which -- Bus request mechanism. If an externel Bus Request comes in and the FSM is idle, run the Bus Request command which
-- suspends processing and tri-states the bus. -- suspends processing and tri-states the bus.
if(Z80_BUSRQn = '0' and Z80_BUSRQ_ACKni = '1' and FSM_STATE = IdleCycle) then if(Z80_BUSRQn = '0' and Z80_BUSRQ_ACKni = '1' and FSM_STATE = IdleCycle) then
@@ -583,11 +639,28 @@ begin
-- New command, set flag as the signal is only 1 clock wide. -- New command, set flag as the signal is only 1 clock wide.
if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then
NEW_SPI_CMD <= '1'; NEW_SPI_DATA <= '1';
end if;
-- Decrement refresh counter on each Z80 cycle, thus when idle and time expired, a refresh can be performed within parameters (256 cycles in 4ms).
if(AUTOREFRESH = '1' and AUTOREFRESH_CNT /= 0 and Z80_CLK_RE = '1') then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
end if;
-- Refresh status bit. Indicates a Refresh cycle is under way.
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_11 or FSM_STATE = RefreshCycle_20 or FSM_STATE = RefreshCycle_21 or FSM_STATE = RefreshCycle_3 then
RFSH_STATUS <= '1';
else
RFSH_STATUS <= '0';
end if;
-- If we are in a WAIT sampling 1/2 cycle and wait goes active, set the state so we repeat the full clock cycle by winding back 2 places.
if(FSM_CHECK_WAIT = '1' and Z80_WAITn = '0' and Z80_CLK_TGL = '0') then
FSM_WAIT_ACTIVE <= '1';
end if; end if;
-- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default. -- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default.
if(FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) then if((FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) and Z80_CLK_RE = '1') then
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
Z80_MREQni <= '1'; Z80_MREQni <= '1';
Z80_IORQni <= '1'; Z80_IORQni <= '1';
@@ -597,24 +670,34 @@ begin
FSM_STATUS <= '0'; FSM_STATUS <= '0';
Z80_RFSHni <= '1'; Z80_RFSHni <= '1';
-- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences.
-- This will be overriden if the SPI receives a new command. -- This period may be extended if the SPI receives a new command.
-- --
if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
if(AUTOREFRESH_CNT = 0) then if(AUTOREFRESH_CNT = 0) then
FSM_STATE <= RefreshCycle_3; FSM_STATE <= RefreshCycle_3;
FSM_STATUS<= '1';
-- 4164 DRAM = 256 cycles in 4ms.
AUTOREFRESH_CNT <= 63;
end if; end if;
end if; end if;
end if; end if;
--------------------------------------------------------------------------------------------
-- CPLD Macro Command Finite State Machine.
--------------------------------------------------------------------------------------------
-- Controller state machine.
-- When idle, accept and process SPI commands which can lead to a controller macro command.
case CTRL_STATE is
when CTRLCMD_Idle =>
-- If new command has been given and the FSM enters idle state, load up new command for processing. -- If new command has been given and the FSM enters idle state, load up new command for processing.
if(NEW_SPI_CMD = '1' and FSM_STATE = IdleCycle) then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
NEW_SPI_CMD <= '0';
-- Store new address and data for this command. -- Store new address and data for this command.
if(NEW_SPI_DATA = '1') then
CPU_ADDR <= SPI_CPU_ADDR; CPU_ADDR <= SPI_CPU_ADDR;
if(SPI_CPU_DATA /= CPU_DATA_OUT) then
CPU_DATA_OUT <= SPI_CPU_DATA; CPU_DATA_OUT <= SPI_CPU_DATA;
end if; end if;
@@ -645,6 +728,11 @@ begin
-- Initiate a Read IO Cycle. -- Initiate a Read IO Cycle.
FSM_STATE <= ReadIOCycle; FSM_STATE <= ReadIOCycle;
when X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" =>
-- Initiate a read IO write memory cycle via the controller FSM.
CTRL_STATE <= CTRLCMD_ReadIOWrite;
FSM_STATE <= ReadIOCycle;
when X"50" => when X"50" =>
-- Register a Halt state. -- Register a Halt state.
FSM_STATE <= HaltCycle; FSM_STATE <= HaltCycle;
@@ -673,51 +761,83 @@ begin
SPI_PROCESSING<= not SPI_PROCESSING; SPI_PROCESSING<= not SPI_PROCESSING;
end if; end if;
-- Clear new data flag ready for next cmd/param transfer.
NEW_SPI_DATA <= '0';
-- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion.
FSM_STATUS <= '1'; FSM_STATUS <= '1';
end if; end if;
-- Refresh status bit. Indicates a Refresh cycle is under way. when CTRLCMD_ReadIOWrite =>
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_1 or FSM_STATE = RefreshCycle_2 or FSM_STATE = RefreshCycle_3 then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
RFSH_STATUS <= '1'; NEW_SPI_DATA <= '0';
else CPU_DATA_EN <= '0';
RFSH_STATUS <= '0'; Z80_IORQni <= '1';
Z80_RDni <= '1';
Z80_RFSHni <= '1';
CPU_ADDR <= SPI_CPU_ADDR;
CPU_DATA_OUT <= CPU_DATA_IN;
FSM_STATE <= WriteCycle;
CTRL_STATE <= CTRLCMD_ReadIOWrite_1;
end if; end if;
when CTRLCMD_ReadIOWrite_1 =>
if(FSM_STATE = WriteCycle_31) then
CTRL_STATE <= CTRLCMD_Idle;
end if;
when others =>
CTRL_STATE <= CTRLCMD_Idle;
end case;
--------------------------------------------------------------------------------------------
-- Z80 Finite State Machine.
--------------------------------------------------------------------------------------------
-- On each Z80 edge we advance the Z80 FSM to recreate the Z80 external signal transactions.
if(Z80_CLK_TGL = '1') then
-- The FSM advances to the next stage on each Z80 edge unless in Idle state.
if(FSM_STATE /= IdleCycle) then
FSM_STATE <= SOMFSMState'val(SOMFSMState'POS(FSM_STATE)+1);
end if;
-- Half cycle expired so we dont check the Z80 wait again.
FSM_CHECK_WAIT <= '0';
FSM_WAIT_ACTIVE <= '0';
-- FSM to implement all the required Z80 cycles. -- FSM to implement all the required Z80 cycles.
-- --
case FSM_STATE is case FSM_STATE is
when IdleCycle => when IdleCycle =>
CPU_LAST_T_STATE <= '1';
FSM_STATUS <= '0';
----------------------------- -----------------------------
-- Z80 Fetch Cycle. -- Z80 Fetch Cycle.
----------------------------- -----------------------------
when FetchCycle => when FetchCycle =>
if(Z80_CLK_RE = '1' or Z80_CLK_LEVEL = '1') then
CPU_T_STATE_SET<= 1;
CPU_T_STATES <= 4;
Z80_M1ni <= '0'; Z80_M1ni <= '0';
FSM_STATE <= FetchCycle_2;
end if;
when FetchCycle_2 => when FetchCycle_11 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then Z80_M1ni <= '0';
Z80_MREQni <= '0'; Z80_MREQni <= '0';
Z80_RDni <= '0'; Z80_RDni <= '0';
FSM_STATE <= FetchCycle_3;
when FetchCycle_20 =>
FSM_CHECK_WAIT <= '1';
when FetchCycle_21 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= FetchCycle_20;
end if; end if;
when FetchCycle_3 => when FetchCycle_30 =>
if(CPU_T_STATE = 2 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
FSM_STATE <= FetchCycle_4;
end if;
when FetchCycle_4 =>
-- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until -- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until
-- a short period before the falling edge of T3 (could be an MZ-700 design restriction or the Z80 timing diagrams are a bit out). -- a short period before the falling edge of T3.
if(CPU_T_STATE = 3 and Z80_CLK_RE = '1' and Z80_WAITni = '1') then
FSM_STATE <= RefreshCycle; FSM_STATE <= RefreshCycle;
end if;
----------------------------- -----------------------------
-- Z80 Refresh Cycle. -- Z80 Refresh Cycle.
@@ -725,167 +845,135 @@ begin
when RefreshCycle => when RefreshCycle =>
-- Latch data from mainboard. -- Latch data from mainboard.
CPU_DATA_IN <= Z80_DATA; CPU_DATA_IN <= Z80_DATA;
FSM_STATUS <= '0';
Z80_RFSHni <= '0'; Z80_RFSHni <= '0';
FSM_STATE <= RefreshCycle_1;
when RefreshCycle_1 => when RefreshCycle_11 =>
-- Falling edge of T3 activates the MREQ line. -- Falling edge of T3 activates the MREQ line.
if(Z80_CLK_FE = '1' and CPU_T_STATE = 3) then
Z80_MREQni <= '0'; Z80_MREQni <= '0';
FSM_STATE <= RefreshCycle_2; FSM_STATUS <= '0';
end if;
when RefreshCycle_2 => when RefreshCycle_20 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 4) then
when RefreshCycle_21 =>
Z80_MREQni <= '1'; Z80_MREQni <= '1';
end if; REFRESH_ADDR <= REFRESH_ADDR + 1;
if(Z80_MREQni = '1' and CPU_T_STATE = 4) then
REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1;
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
end if;
when RefreshCycle_3 => when RefreshCycle_3 =>
CPU_T_STATE_SET <= 3;
CPU_T_STATES <= 4;
Z80_RFSHni <= '0'; Z80_RFSHni <= '0';
FSM_STATE <= RefreshCycle_1; FSM_STATE <= RefreshCycle_11;
----------------------------- -----------------------------
-- Z80 Write Cycle. -- Z80 Write Cycle.
----------------------------- -----------------------------
when WriteCycle => when WriteCycle =>
FSM_STATUS <= '0';
if(Z80_CLK_RE = '1' or Z80_CLK_LEVEL = '1') then
CPU_T_STATE_SET<= 1;
CPU_T_STATES <= 3;
FSM_STATE <= WriteCycle_2;
end if;
when WriteCycle_2 => when WriteCycle_11 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then
Z80_MREQni <= '0'; Z80_MREQni <= '0';
CPU_DATA_EN <= '1'; CPU_DATA_EN <= '1';
FSM_STATE <= WriteCycle_3;
end if;
when WriteCycle_3 => when WriteCycle_20 =>
if(CPU_T_STATE = 2 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then FSM_CHECK_WAIT <= '1';
when WriteCycle_21 =>
Z80_WRni <= '0'; Z80_WRni <= '0';
FSM_STATE <= WriteCycle_4; if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= WriteCycle_20;
end if; end if;
when WriteCycle_4 => when WriteCycle_30 =>
if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
when WriteCycle_31 =>
FSM_STATUS <= '0';
Z80_MREQni <= '1'; Z80_MREQni <= '1';
Z80_WRni <= '1'; Z80_WRni <= '1';
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
end if;
----------------------------- -----------------------------
-- Z80 Read Cycle. -- Z80 Read Cycle.
----------------------------- -----------------------------
when ReadCycle => when ReadCycle =>
if(Z80_CLK_RE = '1' or Z80_CLK_LEVEL = '1') then
CPU_T_STATE_SET<= 1;
CPU_T_STATES <= 3;
FSM_STATE <= ReadCycle_2;
end if;
when ReadCycle_2 => when ReadCycle_11 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then
Z80_MREQni <= '0'; Z80_MREQni <= '0';
Z80_RDni <= '0'; Z80_RDni <= '0';
FSM_STATE <= ReadCycle_3;
when ReadCycle_20 =>
FSM_CHECK_WAIT <= '1';
when ReadCycle_21 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= ReadCycle_20;
end if; end if;
when ReadCycle_3 => when ReadCycle_30 =>
if(CPU_T_STATE = 2 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
FSM_STATE <= ReadCycle_4;
end if;
when ReadCycle_4 => when ReadCycle_31 =>
if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
-- Latch data from mainboard. -- Latch data from mainboard.
CPU_DATA_IN <= Z80_DATA; CPU_DATA_IN <= Z80_DATA;
FSM_STATUS <= '0'; FSM_STATUS <= '0';
-- MREQ/RD are deactivated at idle giving 1 clock to latch the data in.
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
end if;
----------------------------- -----------------------------
-- Z80 IO Write Cycle. -- Z80 IO Write Cycle.
----------------------------- -----------------------------
when WriteIOCycle => when WriteIOCycle =>
FSM_STATUS <= '0';
if(Z80_CLK_RE = '1' or Z80_CLK_LEVEL = '1') then
CPU_T_STATE_SET<= 1;
CPU_T_STATES <= 4;
FSM_STATE <= WriteIOCycle_2;
end if;
when WriteIOCycle_2 => when WriteIOCycle_11 =>
if(Z80_CLK_FE = '1') then
CPU_DATA_EN <= '1'; CPU_DATA_EN <= '1';
FSM_STATE <= WriteIOCycle_3;
end if;
when WriteIOCycle_3 => when WriteIOCycle_20 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 2) then
Z80_IORQni <= '0'; Z80_IORQni <= '0';
Z80_WRni <= '0'; Z80_WRni <= '0';
FSM_STATE <= WriteIOCycle_4;
when WriteIOCycle_21 =>
when WriteIOCycle_30 =>
FSM_CHECK_WAIT <= '1';
when WriteIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= WriteIOCycle_30;
end if; end if;
when WriteIOCycle_4 => when WriteIOCycle_40 =>
-- Add automatic Wait State (called T3 here but actually TW).
if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
FSM_STATE <= WriteIOCycle_5;
end if;
when WriteIOCycle_5 => when WriteIOCycle_41 =>
if(CPU_T_STATE = 4 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then FSM_STATUS <= '0';
Z80_IORQni <= '1'; Z80_IORQni <= '1';
Z80_WRni <= '1'; Z80_WRni <= '1';
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
end if;
----------------------------- -----------------------------
-- Z80 IO Read Cycle. -- Z80 IO Read Cycle.
----------------------------- -----------------------------
when ReadIOCycle => when ReadIOCycle =>
if(Z80_CLK_RE = '1' or Z80_CLK_LEVEL = '1') then
CPU_T_STATE_SET<= 1;
CPU_T_STATES <= 4;
FSM_STATE <= ReadIOCycle_2;
end if;
when ReadIOCycle_2 => when ReadIOCycle_11 =>
if(Z80_CLK_FE = '1' and CPU_T_STATE = 2) then
when ReadIOCycle_20 =>
Z80_IORQni <= '0'; Z80_IORQni <= '0';
Z80_RDni <= '0'; Z80_RDni <= '0';
FSM_STATE <= ReadIOCycle_3;
when ReadIOCycle_21 =>
when ReadIOCycle_30 =>
FSM_CHECK_WAIT <= '1';
when ReadIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= ReadIOCycle_30;
end if; end if;
when ReadIOCycle_3 => when ReadIOCycle_40 =>
-- Add automatic Wait State.
if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
CPU_T_STATE_SET<= 2;
FSM_STATE <= ReadIOCycle_4;
end if;
when ReadIOCycle_4 => when ReadIOCycle_41 =>
if(CPU_T_STATE = 4 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then
-- Latch data from mainboard. -- Latch data from mainboard.
CPU_DATA_IN <= Z80_DATA; CPU_DATA_IN <= Z80_DATA;
FSM_STATUS <= '0'; FSM_STATUS <= '0';
-- IORA/RD are deactivated at idle giving 1 clock to latch the data in. -- IORQ/RD are deactivated at idle giving 1 clock to latch the data in.
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
end if;
----------------------------- -----------------------------
-- Halt Request. -- Halt Request.
@@ -901,12 +989,18 @@ begin
when BusReqCycle => when BusReqCycle =>
Z80_BUSRQ_ACKni <= '0'; Z80_BUSRQ_ACKni <= '0';
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
when others =>
FSM_STATE <= IdleCycle;
end case; end case;
end if;
VCPU_CS_EDGE <= VCPU_CS_EDGE(0) & VSOM_SPI_CSn; VCPU_CS_EDGE <= VCPU_CS_EDGE(0) & VSOM_SPI_CSn;
end if; end if;
end process; end process;
Z80_CLKi <= not Z80_CLK;
-- CPU Interface tri-state control based on acknowledged bus request. -- CPU Interface tri-state control based on acknowledged bus request.
Z80_ADDR <= IPAR & REFRESH_ADDR when Z80_RFSHni = '0' Z80_ADDR <= IPAR & REFRESH_ADDR when Z80_RFSHni = '0'
else else
@@ -916,8 +1010,6 @@ begin
Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1'
else else
(others => 'Z'); (others => 'Z');
-- Z80_DATAi <= Z80_DATA when Z80_RDn = '0'
-- else (others => '1');
Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1'
else 'Z'; else 'Z';
Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1'
@@ -938,7 +1030,6 @@ begin
-- CPU Interface single state input. -- CPU Interface single state input.
Z80_NMIni <= Z80_NMIn; Z80_NMIni <= Z80_NMIn;
Z80_INTni <= Z80_INTn; Z80_INTni <= Z80_INTn;
Z80_WAITni <= Z80_WAITn;
Z80_BUSRQni <= Z80_BUSRQn; Z80_BUSRQni <= Z80_BUSRQn;
-- SOM Reset. -- SOM Reset.
@@ -947,14 +1038,15 @@ begin
-- SOM to CPLD Interface. -- SOM to CPLD Interface.
VSOM_DATA_OUT <= CPU_DATA_IN when VSOM_HBYTE = '1' VSOM_DATA_OUT <= CPU_DATA_IN when VSOM_HBYTE = '1'
else else
FSM_STATUS & RFSH_STATUS & Z80_BUSRQ_ACKni & Z80_BUSRQn & Z80_INTn & Z80_NMIn & Z80_WAITn & Z80_RESETn when VSOM_HBYTE = '0' FSM_STATUS & RFSH_STATUS & Z80_BUSRQ_ACKni & Z80_BUSRQni & Z80_INTni & Z80_NMIni & Z80_WAITn & Z80_RESETn when VSOM_HBYTE = '0'
else else
(others => '0'); (others => '0');
-- Loopback test, echo what was received. -- Loopback test, echo what was received.
SPI_TX_DATA <= SPI_RX_DATA when SPI_LOOPBACK_TEST = '1' SPI_TX_DATA <= SPI_RX_DATA when SPI_LOOPBACK_TEST = '1'
else else
CPU_ADDR & CPU_DATA_IN & FSM_STATUS & RFSH_STATUS & Z80_BUSRQ_ACKni & Z80_BUSRQn & Z80_INTn & Z80_NMIn & Z80_WAITn & Z80_RESETn; --CPU_ADDR & SOM_CMD & FSM_STATUS & RFSH_STATUS & std_logic_vector(to_unsigned(SOMFSMState'POS(FSM_STATE), 6));
CPU_ADDR & CPU_DATA_IN & FSM_STATUS & RFSH_STATUS & Z80_BUSRQ_ACKni & Z80_BUSRQni & Z80_INTni & Z80_NMIni & Z80_WAITn & Z80_RESETn;
-- Signal mirrors. -- Signal mirrors.
VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING
@@ -990,10 +1082,14 @@ begin
-- Generate composite sync. -- Generate composite sync.
VGA_CSYNCn <= VGA_HSYNCn xor not VGA_VSYNCn; VGA_CSYNCn <= VGA_HSYNCn xor not VGA_VSYNCn;
MONO_CSYNCn <= VGA_HSYNCn xor not VGA_VSYNCn; MONO_CSYNCn <= not VGA_HSYNCn xor not VGA_VSYNCn;
-- DAC clocks. -- DAC clocks.
--VGA_PXL_CLK <= CLK_50M; --VGA_PXL_CLK <= CLK_50M;
MONO_PXL_CLK <= VGA_PXL_CLK; MONO_PXL_CLK <= VGA_PXL_CLK;
-- Currently unassigned.
VGA_COLR <= '0';
MONO_RSV <= '0';
end architecture; end architecture;

View File

@@ -15,6 +15,7 @@
-- Feb 2023 v1.1 - Updates, after numerous tests to try and speed up the Z80 transaction -- Feb 2023 v1.1 - Updates, after numerous tests to try and speed up the Z80 transaction
-- from SSD202 issuing a command to data being returned. Source now -- from SSD202 issuing a command to data being returned. Source now
-- different to the MZ-700/MZ-2000 so will need back porting. -- different to the MZ-700/MZ-2000 so will need back porting.
-- Apr 2023 v1.2 - Updated from the PCW8256 development.
-- --
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify -- This source file is free software: you can redistribute it and-or modify
@@ -131,7 +132,7 @@ end entity;
architecture rtl of cpld512 is architecture rtl of cpld512 is
-- Finite State Machine states. -- Z80 Finite State Machine states.
type SOMFSMState is type SOMFSMState is
( (
IdleCycle, IdleCycle,
@@ -177,6 +178,14 @@ architecture rtl of cpld512 is
BusReqCycle BusReqCycle
); );
-- Controller FSM states.
type CTRLFSMState is
(
CTRLCMD_Idle,
CTRLCMD_ReadIOWrite,
CTRLCMD_ReadIOWrite_1
);
-- CPU Interface internal signals. -- CPU Interface internal signals.
signal Z80_BUSRQni : std_logic; signal Z80_BUSRQni : std_logic;
signal Z80_INTni : std_logic; signal Z80_INTni : std_logic;
@@ -188,7 +197,6 @@ architecture rtl of cpld512 is
signal Z80_HALTni : std_logic; signal Z80_HALTni : std_logic;
signal Z80_M1ni : std_logic; signal Z80_M1ni : std_logic;
signal Z80_RFSHni : std_logic; signal Z80_RFSHni : std_logic;
signal Z80_DATAi : std_logic_vector(7 downto 0);
signal Z80_BUSRQ_ACKni : std_logic; signal Z80_BUSRQ_ACKni : std_logic;
-- Internal CPU state control. -- Internal CPU state control.
@@ -206,14 +214,15 @@ architecture rtl of cpld512 is
-- Refresh control. -- Refresh control.
signal FSM_STATE : SOMFSMState := IdleCycle; signal FSM_STATE : SOMFSMState := IdleCycle;
signal NEW_SPI_CMD : std_logic := '0'; signal CTRL_STATE : CTRLFSMState := CTRLCMD_Idle;
signal NEW_SPI_DATA : std_logic := '0';
signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11"; signal VCPU_CS_EDGE : std_logic_vector(1 downto 0) := "11";
signal AUTOREFRESH_CNT : integer range 0 to 7; signal AUTOREFRESH_CNT : integer range 0 to 31;
signal FSM_STATUS : std_logic := '0'; signal FSM_STATUS : std_logic := '0';
signal FSM_CHECK_WAIT : std_logic := '0'; signal FSM_CHECK_WAIT : std_logic := '0';
signal FSM_WAIT_ACTIVE : std_logic := '0'; signal FSM_WAIT_ACTIVE : std_logic := '0';
signal RFSH_STATUS : std_logic := '0'; signal RFSH_STATUS : std_logic := '0';
signal REFRESH_ADDR : std_logic_vector(7 downto 0); signal REFRESH_ADDR : std_logic_vector(6 downto 0);
signal IPAR : std_logic_vector(7 downto 0); signal IPAR : std_logic_vector(7 downto 0);
signal AUTOREFRESH : std_logic; signal AUTOREFRESH : std_logic;
@@ -232,11 +241,12 @@ architecture rtl of cpld512 is
signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register signal SPI_RX_SREG : std_logic_vector(7 downto 0); -- RX Shift Register
signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit. signal SPI_TX_DATA : std_logic_vector(31 downto 0); -- Data to transmit.
signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received. signal SPI_RX_DATA : std_logic_vector(31 downto 0); -- Data received.
signal SPI_BIT_CNT : integer range 0 to 16; -- Count of bits tx/rx'd. signal SPI_BIT_CNT : integer range 0 to 7; -- Count of bits tx/rx'd.
signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks). signal SPI_FRAME_CNT : integer range 0 to 4; -- Number of frames received (8bit chunks).
-- SPI Command interface. -- SPI Command interface.
signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0'); signal SOM_CMD : std_logic_vector(7 downto 0) := (others => '0');
signal SOM_PARAM_CNT : integer range 0 to 3;
signal SPI_NEW_DATA : std_logic; signal SPI_NEW_DATA : std_logic;
signal SPI_PROCESSING : std_logic; signal SPI_PROCESSING : std_logic;
signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0'); signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0');
@@ -340,8 +350,11 @@ begin
-- SPI Slave input. Receive command and data from the SOM. -- SPI Slave input. Receive command and data from the SOM.
SPI_INPUT : process(VSOM_SPI_CLK) SPI_INPUT : process(VSOM_SPI_CLK)
begin begin
-- Chip Select inactive, disable process and reset control flags.
if(VSOM_SPI_CSn = '1') then
-- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge -- SPI_CLK_POLARITY='0' => falling edge; SPI_CLK_POLARITY='1' => rising edge
if(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then
if(VSOM_SPI_CSn = '0') then if(VSOM_SPI_CSn = '0') then
SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI;
@@ -396,6 +409,7 @@ begin
SPI_FRAME_CNT<= 1; SPI_FRAME_CNT<= 1;
VSOM_SPI_MISO<= SPI_TX_DATA(7); VSOM_SPI_MISO<= SPI_TX_DATA(7);
SPI_TX_SREG <= SPI_TX_DATA(6 downto 0); SPI_TX_SREG <= SPI_TX_DATA(6 downto 0);
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
elsif(SPI_FRAME_CNT = 1) then elsif(SPI_FRAME_CNT = 1) then
SPI_FRAME_CNT<= 2; SPI_FRAME_CNT<= 2;
VSOM_SPI_MISO<= SPI_TX_DATA(15); VSOM_SPI_MISO<= SPI_TX_DATA(15);
@@ -404,13 +418,14 @@ begin
SPI_FRAME_CNT<= 3; SPI_FRAME_CNT<= 3;
VSOM_SPI_MISO<= SPI_TX_DATA(23); VSOM_SPI_MISO<= SPI_TX_DATA(23);
SPI_TX_SREG <= SPI_TX_DATA(22 downto 16); SPI_TX_SREG <= SPI_TX_DATA(22 downto 16);
else elsif(SPI_FRAME_CNT = 3) then
-- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception. -- Increment frame count for each word received. We handle 8bit (1 frame), 16bit (2 frames) or 32bit (4 frames) reception.
SPI_FRAME_CNT<= 4; SPI_FRAME_CNT<= 4;
VSOM_SPI_MISO<= SPI_TX_DATA(31); VSOM_SPI_MISO<= SPI_TX_DATA(31);
SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24);
else
SPI_FRAME_CNT<= 0;
end if; end if;
SPI_BIT_CNT <= 7; SPI_BIT_CNT <= 7;
end if; end if;
end if; end if;
@@ -428,6 +443,7 @@ begin
AUTOREFRESH <= '0'; AUTOREFRESH <= '0';
SPI_LOOPBACK_TEST <= '0'; SPI_LOOPBACK_TEST <= '0';
SOM_CMD <= (others => '0'); SOM_CMD <= (others => '0');
SOM_PARAM_CNT <= 0;
SPI_CPU_ADDR <= (others => '0'); SPI_CPU_ADDR <= (others => '0');
SPI_NEW_DATA <= '0'; SPI_NEW_DATA <= '0';
@@ -446,6 +462,13 @@ begin
-- --
elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then
-- If active, decrement parameter count. Parameters sent after a command are not considered as commands.
if(SOM_PARAM_CNT > 0) then
SOM_PARAM_CNT <= SOM_PARAM_CNT - 1;
end if;
-- Process if command, store parameters.
if(SOM_PARAM_CNT = 0) then
-- Command is always located in the upper byte of frame 1. -- Command is always located in the upper byte of frame 1.
SOM_CMD <= SPI_RX_DATA(7 downto 0); SOM_CMD <= SPI_RX_DATA(7 downto 0);
@@ -459,27 +482,53 @@ begin
when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" | -- Fetch
X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" | -- Write
X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read
X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" | -- WriteIO
X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" | --
X"40" | X"41" | X"42" | X"43" | X"44" | X"45" | X"46" | X"47" | --
X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" =>
-- Direct address set. -- Direct address set.
if(SPI_FRAME_CNT = 4) then if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else else
-- if(SPI_CPU_ADDR >= X"D010" and SPI_CPU_ADDR < X"D020") then
-- SPI_CPU_ADDR <= std_logic_vector(X"D020" + unsigned(SPI_RX_DATA(2 downto 0)));
-- else
SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0)));
-- end if;
end if; end if;
if(SPI_FRAME_CNT > 1) then if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if; end if;
when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" | -- ReadIO
X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" => -- ReadIO-Write
-- Direct address set.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= X"00" & SPI_RX_DATA(15 downto 8);
else
SPI_CPU_ADDR <= X"000" & '0' & std_logic_vector(unsigned(SPI_RX_DATA(2 downto 0)));
end if;
if(SPI_FRAME_CNT > 1) then
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
end if;
-- ReadIO-Write, Read-WriteIO commands require target address, so indicate parameter needed.
if(SPI_RX_DATA(7 downto 0) >= X"38" and SPI_RX_DATA(7 downto 0) < X"40") then
SOM_PARAM_CNT <= 1;
end if;
-- SETSIGSET1: Set control lines directly. -- SETSIGSET1: Set control lines directly.
when X"F0" => when X"F0" =>
VIDEO_SRCi <= SPI_RX_DATA(8); VIDEO_SRCi <= SPI_RX_DATA(8);
@@ -506,6 +555,17 @@ begin
when others => when others =>
end case; end case;
else
-- Store parameter depending on number of frames, either ADDR+DATA, ADDR or DATA.
if(SPI_FRAME_CNT = 4) then
SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16);
SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8);
elsif(SPI_FRAME_CNT = 2) then
SPI_CPU_ADDR <= SPI_RX_DATA(15 downto 0);
else
SPI_CPU_DATA <= SPI_RX_DATA(7 downto 0);
end if;
end if;
end if; end if;
end process; end process;
@@ -566,9 +626,9 @@ begin
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
CPU_DATA_IN <= (others => '0'); CPU_DATA_IN <= (others => '0');
REFRESH_ADDR <= (others => '0'); REFRESH_ADDR <= (others => '0');
AUTOREFRESH_CNT <= 7; AUTOREFRESH_CNT <= 31;
IPAR <= (others => '0'); IPAR <= (others => '0');
NEW_SPI_CMD <= '0'; NEW_SPI_DATA <= '0';
VCPU_CS_EDGE <= "11"; VCPU_CS_EDGE <= "11";
SPI_PROCESSING <= '0'; SPI_PROCESSING <= '0';
@@ -585,11 +645,28 @@ begin
-- New command, set flag as the signal is only 1 clock wide. -- New command, set flag as the signal is only 1 clock wide.
if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then if(SPI_LOOPBACK_TEST = '0' and VSOM_SPI_CSn = '1' and VCPU_CS_EDGE = "01") then
NEW_SPI_CMD <= '1'; NEW_SPI_DATA <= '1';
end if;
-- Decrement refresh counter on each Z80 cycle, thus when idle and time expired, a refresh can be performed within parameters (256 cycles in 4ms).
if(AUTOREFRESH = '1' and AUTOREFRESH_CNT /= 0 and Z80_CLK_RE = '1') then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
end if;
-- Refresh status bit. Indicates a Refresh cycle is under way.
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_11 or FSM_STATE = RefreshCycle_20 or FSM_STATE = RefreshCycle_21 or FSM_STATE = RefreshCycle_3 then
RFSH_STATUS <= '1';
else
RFSH_STATUS <= '0';
end if;
-- If we are in a WAIT sampling 1/2 cycle and wait goes active, set the state so we repeat the full clock cycle by winding back 2 places.
if(FSM_CHECK_WAIT = '1' and Z80_WAITn = '0' and Z80_CLK_TGL = '0') then
FSM_WAIT_ACTIVE <= '1';
end if; end if;
-- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default. -- Whenever we return to Idle or just prior to Refresh from a Fetch cycle set all control signals to default.
if((FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) and Z80_CLK_TGL = '1') then if((FSM_STATE = IdleCycle or FSM_STATE = RefreshCycle) and Z80_CLK_RE = '1') then
CPU_DATA_EN <= '0'; CPU_DATA_EN <= '0';
Z80_MREQni <= '1'; Z80_MREQni <= '1';
Z80_IORQni <= '1'; Z80_IORQni <= '1';
@@ -599,25 +676,34 @@ begin
FSM_STATUS <= '0'; FSM_STATUS <= '0';
Z80_RFSHni <= '1'; Z80_RFSHni <= '1';
-- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences.
-- This will be overriden if the SPI receives a new command. -- This period may be extended if the SPI receives a new command.
-- --
if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then
AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1;
if(AUTOREFRESH_CNT = 0) then if(AUTOREFRESH_CNT = 0) then
FSM_STATE <= RefreshCycle_3; FSM_STATE <= RefreshCycle_3;
FSM_STATUS<= '1'; FSM_STATUS<= '1';
-- 4116 DRAM = 128 cycles in 2ms.
AUTOREFRESH_CNT <= 31;
end if; end if;
end if; end if;
end if; end if;
--------------------------------------------------------------------------------------------
-- CPLD Macro Command Finite State Machine.
--------------------------------------------------------------------------------------------
-- Controller state machine.
-- When idle, accept and process SPI commands which can lead to a controller macro command.
case CTRL_STATE is
when CTRLCMD_Idle =>
-- If new command has been given and the FSM enters idle state, load up new command for processing. -- If new command has been given and the FSM enters idle state, load up new command for processing.
if(NEW_SPI_CMD = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
NEW_SPI_CMD <= '0';
-- Store new address and data for this command. -- Store new address and data for this command.
if(NEW_SPI_DATA = '1') then
CPU_ADDR <= SPI_CPU_ADDR; CPU_ADDR <= SPI_CPU_ADDR;
if(SPI_CPU_DATA /= CPU_DATA_OUT) then
CPU_DATA_OUT <= SPI_CPU_DATA; CPU_DATA_OUT <= SPI_CPU_DATA;
end if; end if;
@@ -648,6 +734,11 @@ begin
-- Initiate a Read IO Cycle. -- Initiate a Read IO Cycle.
FSM_STATE <= ReadIOCycle; FSM_STATE <= ReadIOCycle;
when X"38" | X"39" | X"3A" | X"3B" | X"3C" | X"3D" | X"3E" | X"3F" =>
-- Initiate a read IO write memory cycle via the controller FSM.
CTRL_STATE <= CTRLCMD_ReadIOWrite;
FSM_STATE <= ReadIOCycle;
when X"50" => when X"50" =>
-- Register a Halt state. -- Register a Halt state.
FSM_STATE <= HaltCycle; FSM_STATE <= HaltCycle;
@@ -662,7 +753,7 @@ begin
-- Set the Refresh Address register. -- Set the Refresh Address register.
when X"E1" => when X"E1" =>
REFRESH_ADDR <= CPU_DATA_OUT; REFRESH_ADDR <= CPU_DATA_OUT(6 downto 0);
-- Set the Interrupt Page Address Register. -- Set the Interrupt Page Address Register.
when X"E2" => when X"E2" =>
@@ -676,23 +767,41 @@ begin
SPI_PROCESSING<= not SPI_PROCESSING; SPI_PROCESSING<= not SPI_PROCESSING;
end if; end if;
-- Clear new data flag ready for next cmd/param transfer.
NEW_SPI_DATA <= '0';
-- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion.
FSM_STATUS <= '1'; FSM_STATUS <= '1';
end if; end if;
-- Refresh status bit. Indicates a Refresh cycle is under way. when CTRLCMD_ReadIOWrite =>
if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_11 or FSM_STATE = RefreshCycle_20 or FSM_STATE = RefreshCycle_21 or FSM_STATE = RefreshCycle_3 then if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then
RFSH_STATUS <= '1'; NEW_SPI_DATA <= '0';
else CPU_DATA_EN <= '0';
RFSH_STATUS <= '0'; Z80_IORQni <= '1';
Z80_RDni <= '1';
Z80_RFSHni <= '1';
CPU_ADDR <= SPI_CPU_ADDR;
CPU_DATA_OUT <= CPU_DATA_IN;
FSM_STATE <= WriteCycle;
CTRL_STATE <= CTRLCMD_ReadIOWrite_1;
end if; end if;
-- If we are in a WAIT sampling 1/2 cycle and wait goes active, set the state so we repeat the full clock cycle by winding back 2 places. when CTRLCMD_ReadIOWrite_1 =>
if(FSM_CHECK_WAIT = '1' and Z80_WAITn = '0' and Z80_CLK_TGL = '0') then if(FSM_STATE = WriteCycle_31) then
FSM_WAIT_ACTIVE <= '1'; CTRL_STATE <= CTRLCMD_Idle;
end if; end if;
-- On each Z80 edge we advance the FSM to recreate the Z80 external signal transactions. when others =>
CTRL_STATE <= CTRLCMD_Idle;
end case;
--------------------------------------------------------------------------------------------
-- Z80 Finite State Machine.
--------------------------------------------------------------------------------------------
-- On each Z80 edge we advance the Z80 FSM to recreate the Z80 external signal transactions.
if(Z80_CLK_TGL = '1') then if(Z80_CLK_TGL = '1') then
-- The FSM advances to the next stage on each Z80 edge unless in Idle state. -- The FSM advances to the next stage on each Z80 edge unless in Idle state.
@@ -711,7 +820,6 @@ begin
when IdleCycle => when IdleCycle =>
CPU_LAST_T_STATE <= '1'; CPU_LAST_T_STATE <= '1';
FSM_STATUS <= '0'; FSM_STATUS <= '0';
-- FSM_STATE <= IdleCycle;
----------------------------- -----------------------------
-- Z80 Fetch Cycle. -- Z80 Fetch Cycle.
@@ -734,7 +842,7 @@ begin
when FetchCycle_30 => when FetchCycle_30 =>
-- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until -- To meet the timing diagrams, just after Rising edge on T3 clear signals. Data wont be available until
-- a short period before the falling edge of T3 (could be an MZ-80A design restriction or the Z80 timing diagrams are a bit out). -- a short period before the falling edge of T3.
FSM_STATE <= RefreshCycle; FSM_STATE <= RefreshCycle;
----------------------------- -----------------------------
@@ -754,7 +862,7 @@ begin
when RefreshCycle_21 => when RefreshCycle_21 =>
Z80_MREQni <= '1'; Z80_MREQni <= '1';
REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1; REFRESH_ADDR <= REFRESH_ADDR + 1;
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
when RefreshCycle_3 => when RefreshCycle_3 =>
@@ -812,7 +920,6 @@ begin
FSM_STATUS <= '0'; FSM_STATUS <= '0';
FSM_STATE <= IdleCycle; FSM_STATE <= IdleCycle;
----------------------------- -----------------------------
-- Z80 IO Write Cycle. -- Z80 IO Write Cycle.
----------------------------- -----------------------------
@@ -832,7 +939,7 @@ begin
when WriteIOCycle_31 => when WriteIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= WriteIOCycle_20; FSM_STATE <= WriteIOCycle_30;
end if; end if;
when WriteIOCycle_40 => when WriteIOCycle_40 =>
@@ -861,7 +968,7 @@ begin
when ReadIOCycle_31 => when ReadIOCycle_31 =>
if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then
FSM_STATE <= ReadIOCycle_20; FSM_STATE <= ReadIOCycle_30;
end if; end if;
when ReadIOCycle_40 => when ReadIOCycle_40 =>
@@ -901,7 +1008,7 @@ begin
Z80_CLKi <= not Z80_CLK; Z80_CLKi <= not Z80_CLK;
-- CPU Interface tri-state control based on acknowledged bus request. -- CPU Interface tri-state control based on acknowledged bus request.
Z80_ADDR <= IPAR & REFRESH_ADDR when Z80_RFSHni = '0' Z80_ADDR <= IPAR & '0' & REFRESH_ADDR when Z80_RFSHni = '0'
else else
CPU_ADDR when Z80_BUSRQ_ACKni = '1' CPU_ADDR when Z80_BUSRQ_ACKni = '1'
else else
@@ -909,8 +1016,6 @@ begin
Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1'
else else
(others => 'Z'); (others => 'Z');
-- Z80_DATAi <= Z80_DATA when Z80_RDn = '0'
-- else (others => '1');
Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1'
else 'Z'; else 'Z';
Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1'

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: tzpuFusionX_Toplevel.vhd
-- Version: Amstrad PCW-8256
-- Created: Mar 2023
-- Author(s): Philip Smart
-- Description: tzpuFusionX CPLD Top Level module.
--
-- This module contains the basic pin definition of the CPLD<->logic needed in the
-- project which targets the MZ-80A host.
--
-- Credits:
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
--
-- History: Nov 2022 v1.0 - Initial write for the MZ-2000, adaption to the MZ-80A.
-- Feb 2023 v1.1 - Updates, after numerous tests to try and speed up the Z80 transaction
-- from SSD202 issuing a command to data being returned. Source now
-- different to the MZ-700/MZ-2000 so will need back porting.
-- Mar 2023 v1.0 - Snapshot taken from MZ-80A source for the Amstrad PCW-8256 version.
-- Version reset to v1.0 for the Amstrad.
--
---------------------------------------------------------------------------------------------------------
-- 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.
--
-- along with this program. If not, see <http:--www.gnu.org-licenses->.
---------------------------------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.tzpuFusionX_pkg.all;
library altera;
use altera.altera_syn_attributes.all;
entity tzpuFusionX_PCW8256 is
port (
-- Z80 Address Bus
Z80_ADDR : inout std_logic_vector(15 downto 0);
-- Z80 Data Bus
Z80_DATA : inout std_logic_vector(7 downto 0);
-- Z80 Control signals.
Z80_BUSRQn : in std_logic;
Z80_BUSAKn : out std_logic;
Z80_INTn : in std_logic;
Z80_IORQn : inout std_logic;
Z80_MREQn : inout std_logic;
Z80_NMIn : in std_logic;
Z80_RDn : inout std_logic;
Z80_WRn : inout std_logic;
Z80_RESETn : in std_logic; -- Host CPU Reset signal, also CPLD reset.
Z80_HALTn : out std_logic;
Z80_WAITn : in std_logic;
Z80_M1n : inout std_logic;
Z80_RFSHn : inout std_logic;
-- SOM SPI
VSOM_SPI_CSn : in std_logic; -- SPI Slave Select
VSOM_SPI_CLK : in std_logic; -- SPI Clock
VSOM_SPI_MOSI : in std_logic; -- SPI Master Output Slave Input
VSOM_SPI_MISO : out std_logic; -- SPI Master Input Slave Output
-- SOM Parallel Bus.
VSOM_DATA_OUT : out std_logic_vector(7 downto 0); -- Address/Data bus for CPLD control registers.
VSOM_HBYTE : in std_logic; -- Parallel Bus High (1)/Low (0) byte.
VSOM_READY : out std_logic; -- FSM Ready (1), Busy (0)
VSOM_LTSTATE : out std_logic; -- Last T-State in current cycle, 1 = active.
VSOM_BUSRQ : out std_logic; -- Host device requesting Z80 Bus.
VSOM_BUSACK : out std_logic; -- Host device granted Z80 Bus
VSOM_INT : out std_logic; -- Z80 INT signal
VSOM_NMI : out std_logic; -- Z80 NMI signal
VSOM_WAIT : out std_logic; -- Z80 WAIT signal
VSOM_RESET : out std_logic; -- Z80 RESET signal
VSOM_RSV : out std_logic_vector(1 downto 1); -- Reserved pins.
-- SOM Control Signals
PM_RESET : out std_logic; -- Reset SOM
-- VGA_Palette Control
VGA_R : in std_logic_vector(9 downto 7); -- Signals used for detecting blank or no video output.
VGA_G : in std_logic_vector(9 downto 7);
VGA_B : in std_logic_vector(9 downto 8);
-- VGA Control Signals
VGA_PXL_CLK : in std_logic; -- VGA Pixel clock for DAC conversion.
VGA_DISPEN : in std_logic; -- Displayed Enabled (SOM video output).
VGA_VSYNCn : in std_logic; -- SOM VSync.
VGA_HSYNCn : in std_logic; -- SOM HSync.
VGA_COLR : out std_logic; -- COLR colour carrier frequency.
VGA_CSYNCn : out std_logic; -- VGA Composite Sync.
VGA_BLANKn : out std_logic; -- VGA Blank detected.
-- CRT Control Signals
MONO_PXL_CLK : out std_logic; -- Mono CRT pixel clock for DAC conversion.
MONO_BLANKn : out std_logic; -- Mono CRT Blank (no active pixel) detection.
MONO_CSYNCn : out std_logic; -- Mono CRT composite sync.
MONO_RSV : out std_logic;
-- CRT Lower Chrominance Control
MONO_R : out std_logic_vector(2 downto 0); -- Signals to fine tune Red level of monochrome chrominance.
MONO_G : out std_logic_vector(2 downto 0); -- Signals to fine tune Green level of monochrome chrominance.
MONO_B : out std_logic_vector(2 downto 1); -- Signals to fine tune Blue level of monochrome chrominance.
-- MUX Control Signals
VIDEO_SRC : out std_logic; -- Select video source, Mainboard or SOM.
MONO_VIDEO_SRC : out std_logic; -- Select crt video source, Mainboard or SOM.
AUDIO_SRC_L : out std_logic; -- Select Audio Source Left Channel, Mainboard or SOM.
AUDIO_SRC_R : out std_logic; -- Select Audio Source Right Channel, Mainboard or SOM.
-- Mainboard Reset Signals
MB_RESETn : in std_logic; -- Motherboard Reset pressed.
MB_IPLn : in std_logic; -- Motherboard IPL pressed.
-- USB Power Control
VBUS_EN : out std_logic; -- USB Enable Power Output
-- Clocks.
Z80_CLK : in std_logic; -- Host CPU Clock
CLK_50M : in std_logic -- 50MHz oscillator.
);
END entity;
architecture rtl of tzpuFusionX_PCW8256 is
begin
cpldl512Toplevel : entity work.cpld512
generic map (
SPI_CLK_POLARITY => '0'
)
port map
(
-- Z80 Address Bus
Z80_ADDR => Z80_ADDR,
-- Z80 Data Bus
Z80_DATA => Z80_DATA,
-- Z80 Control signals.
Z80_BUSRQn => Z80_BUSRQn,
Z80_BUSAKn => Z80_BUSAKn,
Z80_INTn => Z80_INTn,
Z80_IORQn => Z80_IORQn,
Z80_MREQn => Z80_MREQn,
Z80_NMIn => Z80_NMIn,
Z80_RDn => Z80_RDn,
Z80_WRn => Z80_WRn,
Z80_RESETn => Z80_RESETn,
Z80_HALTn => Z80_HALTn,
Z80_WAITn => Z80_WAITn,
Z80_M1n => Z80_M1n,
Z80_RFSHn => Z80_RFSHn,
-- SOM SPI
VSOM_SPI_CSn => VSOM_SPI_CSn, -- SPI Slave Select
VSOM_SPI_CLK => VSOM_SPI_CLK, -- SPI Clock
VSOM_SPI_MOSI => VSOM_SPI_MOSI, -- SPI Master Output Slave Input
VSOM_SPI_MISO => VSOM_SPI_MISO, -- SPI Master Input Slave Output
-- SOM Parallel Bus.
VSOM_DATA_OUT => VSOM_DATA_OUT, -- Address/Data bus for CPLD control registers.
VSOM_HBYTE => VSOM_HBYTE, -- Parallel Bus High (1)/Low (0) byte.
VSOM_READY => VSOM_READY, -- FSM Ready (1), Busy (0)
VSOM_LTSTATE => VSOM_LTSTATE, -- Last T-State in current cycle.
VSOM_BUSRQ => VSOM_BUSRQ, -- Host device requesting Z80 Bus.
VSOM_BUSACK => VSOM_BUSACK, -- Host device granted Z80 Bus
VSOM_INT => VSOM_INT, -- Z80 INT signal
VSOM_NMI => VSOM_NMI, -- Z80 NMI signal
VSOM_WAIT => VSOM_WAIT, -- Z80 WAIT signal
VSOM_RESET => VSOM_RESET, -- Z80 RESET signal
VSOM_RSV => VSOM_RSV, -- Reserved pins.
-- SOM Control Signals
PM_RESET => PM_RESET, -- Reset SOM
-- VGA_Palette Control
VGA_R => VGA_R, -- Signals used for detecting blank or no video output.
VGA_G => VGA_G,
VGA_B => VGA_B,
-- VGA Control Signals
VGA_PXL_CLK => VGA_PXL_CLK, -- VGA Pixel clock for DAC conversion.
VGA_DISPEN => VGA_DISPEN, -- Displayed Enabled (SOM video output).
VGA_VSYNCn => VGA_VSYNCn, -- SOM VSync.
VGA_HSYNCn => VGA_HSYNCn, -- SOM HSync.
VGA_COLR => VGA_COLR, -- COLR colour carrier frequency.
VGA_CSYNCn => VGA_CSYNCn, -- VGA Composite Sync.
VGA_BLANKn => VGA_BLANKn, -- VGA Blank detected.
-- CRT Control Signals
MONO_PXL_CLK => MONO_PXL_CLK, -- Mono CRT pixel clock for DAC conversion.
MONO_BLANKn => MONO_BLANKn, -- Mono CRT Blank (no active pixel) detection.
MONO_CSYNCn => MONO_CSYNCn, -- Mono CRT composite sync.
MONO_RSV => MONO_RSV,
-- CRT Lower Chrominance Control
MONO_R => MONO_R, -- Signals to fine tune Red level of monochrome chrominance.
MONO_G => MONO_G, -- Signals to fine tune Green level of monochrome chrominance.
MONO_B => MONO_B, -- Signals to fine tune Blue level of monochrome chrominance.
-- MUX Control Signals
VIDEO_SRC => VIDEO_SRC, -- Select video source, Mainboard or SOM.
MONO_VIDEO_SRC => MONO_VIDEO_SRC, -- Select crt video source, Mainboard or SOM.
AUDIO_SRC_L => AUDIO_SRC_L, -- Select Audio Source Left Channel, Mainboard or SOM.
AUDIO_SRC_R => AUDIO_SRC_R, -- Select Audio Source Right Channel, Mainboard or SOM.
-- Mainboard Reset Signals=> MONO_R,
MB_RESETn => MB_RESETn, -- Motherboard Reset pressed.
MB_IPLn => MB_IPLn, -- Motherboard IPL pressed.
-- USB Power Control
VBUS_EN => VBUS_EN, -- USB Enable Power Output
-- Clocks.
Z80_CLK => Z80_CLK, -- Host CPU Clock
CLK_50M => CLK_50M -- 50MHz oscillator.
);
end architecture;

View File

@@ -0,0 +1,227 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: tzpuFusionX_pkg.vhd
-- Created: Mar 2023
-- Author(s): Philip Smart
-- Description: tzpuFusionX CPLD configuration file.
--
-- This module contains parameters for the CPLD in the tzpuFusionX project
-- which targets the Amstrad PCW8256 host.
--
-- Credits:
-- Copyright: (c) 2018-23 Philip Smart <philip.smart@net2net.org>
--
-- History: Nov 2022 v1.0 - Initial write for the MZ-2000, adaption to the MZ-80A.
-- Feb 2023 v1.1 - Updates, after numerous tests to try and speed up the Z80 transaction
-- from SSD202 issuing a command to data being returned. Source now
-- different to the MZ-700/MZ-2000 so will need back porting.
-- Mar 2023 v1.0 - Snapshot taken from MZ-80A source for the Amstrad PCW-8256 version.
-- Version reset to v1.0 for the Amstrad.
--
---------------------------------------------------------------------------------------------------------
-- This source file is free software: you can redistribute it and-or modify
-- it under the terms of the GNU General Public License as published
-- by the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http:--www.gnu.org-licenses->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
package tzpuFusionX_pkg is
------------------------------------------------------------
-- Constants
------------------------------------------------------------
-- Potential logic state constants.
constant YES : std_logic := '1';
constant NO : std_logic := '0';
constant HI : std_logic := '1';
constant LO : std_logic := '0';
constant ONE : std_logic := '1';
constant ZERO : std_logic := '0';
constant HIZ : std_logic := 'Z';
-- CPLD Command instructions.
constant CPLD_CMD_RESET_HOST : integer := 1;
constant CPLD_CMD_HOLD_HOST_BUS : integer := 2;
constant CPLD_CMD_RELEASE_HOST_BUS: integer := 3;
-- Target hardware modes.
constant MODE_MZ80K : integer := 0;
constant MODE_MZ80C : integer := 1;
constant MODE_MZ1200 : integer := 2;
constant MODE_MZ80A : integer := 3;
constant MODE_MZ700 : integer := 4;
constant MODE_MZ800 : integer := 5;
constant MODE_MZ80B : integer := 6;
constant MODE_MZ2000 : integer := 7;
constant MODE_PCW8256 : integer := 8;
-- Memory management modes.
constant TZMM_ORIG : integer := 00; -- Original Sharp mode, no tranZPUter features are selected except the I/O control registers (default: 0x60-063).
constant TZMM_BOOT : integer := 01; -- Original mode but E800-EFFF is mapped to tranZPUter RAM so TZFS can be booted.
constant TZMM_TZFS : integer := 02; -- TZFS main memory configuration. all memory is in tranZPUter RAM, E800-FFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected.
constant TZMM_TZFS2 : integer := 03; -- TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 1.
constant TZMM_TZFS3 : integer := 04; -- TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 2.
constant TZMM_TZFS4 : integer := 05; -- TZFS main memory configuration. all memory is in tranZPUter RAM, E800-EFFF is used by TZFS, SA1510 is at 0000-1000 and RAM is 1000-CFFF, 64K Block 0 selected, F000-FFFF is in 64K Block 3.
constant TZMM_CPM : integer := 06; -- CPM main memory configuration, all memory on the tranZPUter board, 64K block 4 selected. Special case for F3C0:F3FF & F7C0:F7FF (floppy disk paging vectors) which resides on the mainboard.
constant TZMM_CPM2 : integer := 07; -- CPM main memory configuration, F000-FFFF are on the tranZPUter board in block 4, 0040-CFFF and E800-EFFF are in block 5, mainboard for D000-DFFF (video), E000-E800 (Memory control) selected.
-- Special case for 0000:003F (interrupt vectors) which resides in block 4, F3FE:F3FF & F7FE:F7FF (floppy disk paging vectors) which resides on the mainboard.
constant TZMM_COMPAT : integer := 08; -- Compatibility monitor mode, monitor ROM on mainboard, RAM on tranZPUter in Block 0 1000-CFFF.
constant TZMM_HOSTACCESS : integer := 09; -- Monitor ROM 0000-0FFF and Main DRAM 0x1000-0xD000, video and memory mapped I/O are on the host machine, User/Floppy ROM E800-FFFF are in tranZPUter memory.
constant TZMM_MZ700_0 : integer := 10; -- MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the mainboard.
constant TZMM_MZ700_1 : integer := 11; -- MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6.
constant TZMM_MZ700_2 : integer := 12; -- MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is on the tranZPUter in block 6.
constant TZMM_MZ700_3 : integer := 13; -- MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 0, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible.
constant TZMM_MZ700_4 : integer := 14; -- MZ700 Mode - 0000:0FFF is on the tranZPUter board in block 6, 1000:CFFF is on the tranZPUter board in block 0, D000:FFFF is inaccessible.
constant TZMM_MZ800 : integer := 15; -- MZ800 Mode - Running on MZ800 hardware, configuration set according to MZ700/MZ800 mode.
constant TZMM_MZ2000 : integer := 16; -- MZ2000 Mode - Running on MZ2000 hardware, configuration set according to runtime configuration registers.
constant TZMM_FPGA : integer := 21; -- Open up access for the K64F to the FPGA resources such as memory. All other access to RAM or mainboard is blocked.
constant TZMM_TZPUM : integer := 22; -- Everything in on mainboard, no access to tranZPUter memory.
constant TZMM_TZPU : integer := 23; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 0 is selected.
constant TZMM_TZPU0 : integer := 24; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 0 is selected.
constant TZMM_TZPU1 : integer := 25; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 1 is selected.
constant TZMM_TZPU2 : integer := 26; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 2 is selected.
constant TZMM_TZPU3 : integer := 27; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 3 is selected.
constant TZMM_TZPU4 : integer := 28; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 4 is selected.
constant TZMM_TZPU5 : integer := 29; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 5 is selected.
constant TZMM_TZPU6 : integer := 30; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 6 is selected.
constant TZMM_TZPU7 : integer := 31; -- Everything is in tranZPUter domain, no access to underlying Sharp mainboard unless memory management mode is switched. tranZPUter RAM 64K block 7 is selected.
------------------------------------------------------------
-- Configurable parameters.
------------------------------------------------------------
-- Target hardware.
constant CPLD_HOST_HW : integer := MODE_PCW8256;
-- Target video hardware.
constant CPLD_HAS_FPGA_VIDEO : std_logic := '1';
-- Version of hdl.
constant CPLD_VERSION : integer := 2;
-- Clock source for the secondary clock. If a K64F is installed then enable it otherwise use the onboard oscillator.
--
constant USE_K64F_CTL_CLOCK : integer := 1;
------------------------------------------------------------
-- Function prototypes
------------------------------------------------------------
-- Find the maximum of two integers.
function IntMax(a : in integer; b : in integer) return integer;
-- Find the number of bits required to represent an integer.
function log2ceil(arg : positive) return natural;
-- Function to calculate the number of whole 'clock' cycles in a given time period, the period being in ns.
function clockTicks(period : in integer; clock : in integer) return integer;
-- Function to reverse the order of the bits in a standard logic vector.
-- ie. 1010 becomes 0101
function reverse_vector(slv:std_logic_vector) return std_logic_vector;
-- Function to convert an integer (0 or 1) into std_logic.
--
function to_std_logic(i : in integer) return std_logic;
-- Function to return the value of a bit as an integer for array indexing etc.
function bit_to_integer( s : std_logic ) return natural;
------------------------------------------------------------
-- Records
------------------------------------------------------------
------------------------------------------------------------
-- Components
------------------------------------------------------------
end tzpuFusionX_pkg;
------------------------------------------------------------
-- Function definitions.
------------------------------------------------------------
package body tzpuFusionX_pkg is
-- Find the maximum of two integers.
function IntMax(a : in integer; b : in integer) return integer is
begin
if a > b then
return a;
else
return b;
end if;
return a;
end function IntMax;
-- 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;
-- Function to calculate the number of whole 'clock' cycles in a given time period, the period being in ns.
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);
if fracTicks > 0.0001 then
return Integer(CEIL(ticks + 1.0));
else
return Integer(CEIL(ticks));
end if;
end function;
function reverse_vector(slv:std_logic_vector) return std_logic_vector is
variable target : std_logic_vector(slv'high downto slv'low);
begin
for idx in slv'high downto slv'low loop
target(idx) := slv(slv'low + (slv'high-idx));
end loop;
return target;
end reverse_vector;
function to_std_logic(i : in integer) return std_logic is
begin
if i = 0 then
return '0';
end if;
return '1';
end function;
-- Function to return the value of a bit as an integer for array indexing etc.
function bit_to_integer( s : std_logic ) return natural is
begin
if s = '1' then
return 1;
else
return 0;
end if;
end function;
end package body;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,15 +1,17 @@
# Select the target host. # Select the target host.
#MODEL := MZ2000 #MODEL := MZ2000
#MODEL := MZ700 #MODEL := MZ700
#DEBUG := y #MODEL := MZ80A
MODEL := MZ80A #MODEL := PCW8XXX
#MODEL := PCW9XXX
KERNEL := $(PWD)/../../../linux/kernel KERNEL := $(PWD)/../../../linux/kernel
FUSIONX := $(PWD)/../.. FUSIONX := $(PWD)/../..
CROSS := arm-linux-gnueabihf- CROSS := arm-linux-gnueabihf-
CTRLINC = -IZeta/API -IZ80/API -DTARGET_HOST_$(MODEL)=1
ccflags-y = -O2 -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ -DTARGET_HOST_$(MODEL)=1
ifeq ($(DEBUG),y) ifeq ($(DEBUG),y)
ccflags-y += -DTTYMZ_DEBUG ccflags-y += -DTTYMZ_DEBUG
else
ccflags-y += -O2 -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__
endif endif
obj-m += ttymzdrv.o obj-m += ttymzdrv.o
ttymzdrv-objs += ttymz.o z80io.o sharpmz.o ttymzdrv-objs += ttymz.o z80io.o sharpmz.o
@@ -18,16 +20,42 @@ ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o
all: all:
@echo "Specify target host, ie. make <host>"
@echo "Supported hosts: MZ80A, MZ700, MZ2000, PCW8XXX, PCW9XXX"
MZ80A: MODEL_MZ80A
MZ700: MODEL_MZ700
MZ2000: MODEL_MZ2000
PCW8XXX: MODEL_PCW8XXX
PCW9XXX: MODEL_PCW9XXX
MODEL_MZ80A:
$(MAKE) MODEL=MZ80A BUILD_MZ80A
MODEL_MZ700:
$(MAKE) MODEL=MZ700 BUILD_MZ700
MODEL_MZ2000:
$(MAKE) MODEL=MZ2000 BUILD_MZ2000
MODEL_PCW8XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW8XXX
MODEL_PCW9XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW9XXX
BUILD_MZ80A: kmod
BUILD_MZ700: kmod
BUILD_MZ2000: kmod
BUILD_PCW8XXX: kmod
BUILD_PCW9XXX: kmod
kmod:
@echo "" @echo ""
@echo "Build TTYMZ driver for host: $(MODEL)" @echo "Build TTYMZ driver for host: $(MODEL)"
make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules
@echo ""
install: install:
@echo "Copy kernel driver..." @echo "Copy kernel driver..."
@cp ttymz.ko $(FUSIONX)/modules/ @cp ttymzdrv.ko $(FUSIONX)/modules/
clean: clean:
make -C $(KERNEL) M=$(PWD) clean make -C $(KERNEL) M=$(PWD) clean
@rm -f ttymz

View File

@@ -542,10 +542,10 @@ static t_scanCodeMap scanCodeMap[] = {
CTRL_G , // ^G F7 CTRL_G , // ^G F7
CTRL_H , // ^H F8 CTRL_H , // ^H F8
// S5 28 - 2F // S5 28 - 2F
NOKEY , HOTKEY_ORIGINAL, // 1 - Hotkey to invoke original mode.
NOKEY , HOTKEY_RFS40, // 2 - Hotkey to invoke RFS 40 mode.
NOKEY , HOTKEY_TZFS, // 3 - Hotkey to invoke TZFS mode.
NOKEY , HOTKEY_LINUX, // 4 - Hotkey to invoke Linux mode.
NOKEY , NOKEY ,
NOKEY , NOKEY ,
NOKEY , NOKEY ,
@@ -681,8 +681,8 @@ static t_scanCodeMap scanCodeMap[] = {
NOKEY NOKEY
}} }}
}; };
#endif
#if (TARGET_HOST_MZ80A == 1) #elif (TARGET_HOST_MZ80A == 1)
static t_scanCodeMap scanCodeMap[] = { static t_scanCodeMap scanCodeMap[] = {
// MZ_80A NO SHIFT // MZ_80A NO SHIFT
{{ {{
@@ -1151,6 +1151,477 @@ static t_scanCodeMap scanCodeMap[] = {
}} }}
}; };
#elif (TARGET_HOST_MZ2000 == 1)
static t_scanCodeMap scanCodeMap[] = {
// MZ_2000 NO SHIFT
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
GRAPHKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'2' , // 2
'1' , // 1
'w' , // w
'q' , // q
'a' , // a
BACKS , // DELETE
NOKEY , // NULL
'z' , // z
// S2 10 - 17
'4' , // 4
'3' , // 3
'r' , // r
'e' , // e
'd' , // d
's' , // s
'x' , // x
'c' , // c
// S3 18 - 1F
'6' , // 6
'5' , // 5
'y' , // y
't' , // t
'g' , // g
'f' , // f
'v' , // v
'b' , // b
// S4 20 - 27
'8' , // 8
'7' , // 7
'i' , // i
'u' , // u
'j' , // j
'h' , // h
'n' , // n
' ' , // SPACE
// S5 28 - 2F
'0' , // 0
'9' , // 9
'p' , // p
'o' , // o
'l' , // l
'k' , // k
',' , // ,
'm' , // m
// S6 30 - 37
'^' , // ^
'-' , // -
'[' , // [
'@' , // @
':' , // :
';' , // ;
'/' , // /
'.' , // .
// S7 38 - 3F
HOMEKEY , // HOME.
'\\' , // Backslash
CURSRIGHT, // CURSOR RIGHT
CURSUP , // CURSOR UP
CR , // CR
']' , // ]
NOKEY , //
'?' , // ?
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 CAPS LOCK
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'2' , // 2
'1' , // 1
'W' , // W
'Q' , // Q
'A' , // A
BACKS , // DELETE
NOKEY , // NULL
'Z' , // Z
// S2 10 - 17
'4' , // 4
'3' , // 3
'R' , // R
'E' , // E
'D' , // D
'S' , // S
'X' , // X
'C' , // C
// S3 18 - 1F
'6' , // 6
'5' , // 5
'Y' , // Y
'T' , // T
'G' , // G
'F' , // F
'V' , // V
'B' , // B
// S4 20 - 27
'8' , // 8
'7' , // 7
'I' , // I
'U' , // U
'J' , // J
'H' , // H
'N' , // N
' ' , // SPACE
// S5 28 - 2F
'0' , // 0
'9' , // 9
'P' , // P
'O' , // O
'L' , // L
'K' , // K
',' , // ,
'M' , // M
// S6 30 - 37
'^' , // ^
'-' , // -
'[' , // [
'@' , // @
':' , // :
';' , // ;
'/' , // /
'.' , // .
// S7 38 - 3F
HOMEKEY , // HOME.
'\\' , // Backslash
CURSRIGHT, // CURSOR RIGHT
CURSUP , // CURSOR UP
CR , // CR
']' , // ]
NOKEY , //
'?' , // ?
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 SHIFT LOCK.
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAKEY , // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
'"' , // "
'!' , // !
'W' , // W
'Q' , // Q
'A' , // A
INSERT , // INSERT
NOKEY , // NULL
'Z' , // Z
// S2 10 - 17
'$' , // $
'#' , // #
'R' , // R
'E' , // E
'D' , // D
'S' , // S
'X' , // X
'C' , // C
// S3 18 - 1F
'&' , // &
'%' , // %
'Y' , // Y
'T' , // T
'G' , // G
'F' , // F
'V' , // V
'B' , // B
// S4 20 - 27
'(' , // (
'\'' , // '
'I' , // I
'U' , // U
'J' , // J
'H' , // H
'N' , // N
' ' , // SPACE
// S5 28 - 2F
'_' , // _
')' , // )
'P' , // P
'O' , // O
'L' , // L
'K' , // K
'<' , // <
'M' , // M
// S6 30 - 37
'~' , // ~
'=' , // =
'{' , // {
'`' , // `
'*' , // *
'+' , // +
NOKEY , //
'>' , // >
// S7 38 - 3F
CLRKEY , // CLR.
'|' , // |
CURSLEFT , // CURSOR LEFT
CURSDOWN , // CURSOR DOWN
CR , // CR
'}' , // }
NOKEY , //
NOKEY , //
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}},
// MZ_2000 CONTROL CODE
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
ALPHAGRAPHKEY, // GRAPH
NOKEY , // SHIFT
// S1 08 - 0F
NOKEY , //
NOKEY , //
CTRL_W , // CTRL_W
CTRL_Q , // CTRL_Q
CTRL_A , // CTRL_A
DELETE , // DELETE
NOKEY , // NULL
CTRL_Z , // CTRL_Z
// S2 10 - 17
NOKEY , //
NOKEY , //
CTRL_R , // CTRL_R
CTRL_E , // CTRL_E
CTRL_D , // CTRL_D
CTRL_S , // CTRL_S
CTRL_X , // CTRL_X
CTRL_C , // CTRL_C
// S3 18 - 1F
NOKEY , //
NOKEY , //
CTRL_Y , // CTRL_Y
CTRL_T , // CTRL_T
CTRL_G , // CTRL_G
CTRL_F , // CTRL_F
CTRL_V , // CTRL_V
CTRL_B , // CTRL_B
// S4 20 - 27
NOKEY , //
NOKEY , //
CTRL_I , // CTRL_I
CTRL_U , // CTRL_U
CTRL_J , // CTRL_J
CTRL_H , // CTRL_H
CTRL_N , // CTRL_N
' ' , // SPACE
// S5 28 - 2F
CTRL_UNDSCR, // CTRL+_
NOKEY , //
CTRL_P , // CTRL_P
CTRL_O , // CTRL_O
CTRL_L , // CTRL_L
CTRL_K , // CTRL_K
NOKEY , //
CTRL_M , // CTRL_M
// S6 30 - 37
CTRL_CAPPA, // CTRL+^
NOKEY , //
CTRL_LB , // CTRL+[
CTRL_AT , // CTRL+@
NOKEY , //
NOKEY , //
CTRL_SLASH, // CTRL+/
NOKEY , //
// S7 38 - 3F
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
CTRL_RB , // CTRL+]
NOKEY , //
NOKEY , //
// S8 40 - 47 - Keypad keys.
NOKEY , // Keypad 8
NOKEY , // 7
NOKEY , // 5
HOTKEY_LINUX, // 4 - Hotkey to invoke Linux mode.
HOTKEY_RFS40, // 2 - Hotkey to invoke RFS 40 mode.
HOTKEY_RFS80, // 1 - Hotkey to invoke RFS 80 mode.
NOKEY , // 00
HOTKEY_ORIGINAL, // 0 - Hotkey to invoke original mode.
// S9 48 - 4F - Keypad keys.
NOKEY , // +
NOKEY , // 9
NOKEY , // -
NOKEY , // 6
NOKEY , //
HOTKEY_TZFS, // 3 - Hotkey to invoke TZFS mode.
NOKEY ,
NOKEY // .
}},
// MZ_2000 KANA
{{
// S0 00 - 07
NOKEY , // BREAK/CTRL
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
NOKEY , //
GRAPHKEY , // DAKU TEN
NOKEY , //
// S1 08 - 0F
0x35 , // HA
0x77 , // TA
0xD7 , // WA
0xB3 , // YO
0xB7 , // HANDAKU
NOKEY ,
NOKEY ,
NOKEY ,
// S2 10 - 17
0x7C , // KA
0x70 , // KE
0x41 , // SHI
0x31 , // KO
0x39 , // HI
0xA6 , // TE
0x78 , // KI
0xDD , // CHI
// S3 18 - 1F
0x3D , // FU
0x5D , // MI
0x6C , // MU
0x56 , // ME
0x1D , // RHI
0x33 , // RA
0xD5 , // HE
0xB1 , // HO
// S4 20 - 27
0x46 , // SA
0x6E , // TO
0xD9 , // THU
0x48 , // SU
0x74 , // KU
0x43 , // SE
0x4C , // SO
0x73 , // MA
// S5 28 - 2F
0x3F , // A
0x36 , // I
0x7E , // U
0x3B , // E
0x7A , // O
0x1E , // NA
0x5F , // NI
0xA2 , // NU
// S6 30 - 37
0xD3 , // YO
0x9F , // YU
0xD1 , // YA
0x00 , // SPACE
0x9D , // NO
0xA3 , // NE
0xD0 , // RU
0xB9 , // RE
// S7 38 - 3F
0xC6 , // ?CLR
0xC5 , // ?HOME
0xC2 , // ?CURSOR UP
0xC1 , // ?CURSOR DOWN
0xC3 , // ?CURSOR RIGHT
0xC4 , // ?CURSOR LEFT
0xBB , // DASH
0xBE , // RO
// S8 40 - 47 - Keypad keys.
'8' , // Keypad 8
'7' , // 7
'5' , // 5
'4' , // 4
'2' , // 2
'1' , // 1
DBLZERO , // 00
'0' , // 0
// S9 48 - 4F - Keypad keys.
'+' , // +
'0' , // 9
'-' , // -
'6' , // 6
NOKEY , //
'3' , // 3
NOKEY ,
'.' // .
}}
};
#else
#error "Unknown host, no keyboard map configured."
#endif #endif
// Mapping table of sharp special control keys to ANSI ESCape sequences. // Mapping table of sharp special control keys to ANSI ESCape sequences.
@@ -1203,8 +1674,36 @@ static t_ansiKeyMap ansiKeySeq[] = {
// Colour map for the Ansi Terminal. // Colour map for the Ansi Terminal.
const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
#endif
#if (TARGET_HOST_MZ80A == 1) #elif (TARGET_HOST_MZ80A == 1)
// Static structures for controlling and managing hardware features.
// Display control structure. Used to manage the display including the Ansi Terminal.
const t_displayBuffer displayDefault = { .displayAttr = 0x71, .backingRow = 0, .displayCol = 0, .displayRow = 0, .maxBackingRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS),
.maxDisplayRow = VC_MAX_ROWS, .maxBackingCol = 80, .useAnsiTerm = 1, .lineWrap = 0, .inDebug = 0
};
// Keyboard control structure. Used to manage keyboard sweep, mapping and store.
const t_keyboard keyboardDefault = { .holdTimer = 0L, .autorepeat = 0, .mode = KEYB_LOWERCASE, .cursorOn = 1, .flashTimer = 0L, .keyBuf[0] = 0x00, .keyBufPtr = 0,
.mode = KEYB_LOWERCASE, .dualmode = KEYB_DUAL_GRAPH
};
// Audio control structure. Used to manage audio output.
const t_audio audioDefault = { .audioStopTimer = 0
};
// AnsiTerminal control structure. Used to manage the inbuilt Ansi Terminal.
const t_AnsiTerm ansitermDefault = { .state = ANSITERM_ESC, .charcnt = 0, .paramcnt = 0, .setDisplayMode = 0, .setExtendedMode = 0, .saveRow = 0, .saveCol = 0,
};
// Module control structure.
const t_control ctrlDefault = { .suspendIO = 0, .debug = 0
};
// Colour map for the Ansi Terminal.
const unsigned char ansiColourMap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
#elif (TARGET_HOST_MZ2000 == 1)
// Static structures for controlling and managing hardware features. // Static structures for controlling and managing hardware features.
// Display control structure. Used to manage the display including the Ansi Terminal. // Display control structure. Used to manage the display including the Ansi Terminal.
const t_displayBuffer displayDefault = { .displayAttr = 0x71, .backingRow = 0, .displayCol = 0, .displayRow = 0, .maxBackingRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS), const t_displayBuffer displayDefault = { .displayAttr = 0x71, .backingRow = 0, .displayCol = 0, .displayRow = 0, .maxBackingRow = (VC_DISPLAY_BUFFER_SIZE / VC_MAX_COLUMNS),
@@ -1249,6 +1748,11 @@ static t_ansiKeyMap ansiKeySeq[] = {
// //
uint8_t mzInitMBHardware(void) uint8_t mzInitMBHardware(void)
{ {
#if (TARGET_HOST_MZ700 == 1)
// Ensure memory paging is set to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// From the 1Z-013A monitor code, initialise the 8255 PIO. // From the 1Z-013A monitor code, initialise the 8255 PIO.
// //
WRITE_HARDWARE(1, MBADDR_KEYPF, 0x8A); // 10001010 CTRL WORD MODE0 WRITE_HARDWARE(1, MBADDR_KEYPF, 0x8A); // 10001010 CTRL WORD MODE0
@@ -1312,10 +1816,11 @@ void mzBeep(uint32_t freq, uint32_t timeout)
// Locals. // Locals.
#if (TARGET_HOST_MZ80A == 1) #if (TARGET_HOST_MZ80A == 1)
uint16_t freqDiv = TIMER_8253_MZ80A_FREQ/(freq*2); uint16_t freqDiv = TIMER_8253_MZ80A_FREQ/(freq*2);
#else #elif (TARGET_HOST_MZ700 == 1)
uint16_t freqDiv = TIMER_8253_MZ700_FREQ/freq; uint16_t freqDiv = TIMER_8253_MZ700_FREQ/freq;
#endif #endif
#if (TARGET_HOST_MZ2000 == 0)
// Setup the 8253 Timer 0 to output a sound, enable output to amplifier and set timeout. // Setup the 8253 Timer 0 to output a sound, enable output to amplifier and set timeout.
WRITE_HARDWARE(0, MBADDR_CONTF, 0x34 ); // Timer 0 to square wave generator, load LSB first. WRITE_HARDWARE(0, MBADDR_CONTF, 0x34 ); // Timer 0 to square wave generator, load LSB first.
WRITE_HARDWARE(0, MBADDR_CONT0, (freqDiv&0xff) ); WRITE_HARDWARE(0, MBADDR_CONT0, (freqDiv&0xff) );
@@ -1324,6 +1829,7 @@ void mzBeep(uint32_t freq, uint32_t timeout)
// Set a 500ms timeout on the sound to create beep effect. // Set a 500ms timeout on the sound to create beep effect.
audio.audioStopTimer = timeout == 0 ? 11 : (timeout/10)+1; // Each unit is 10ms, valid range 1..n audio.audioStopTimer = timeout == 0 ? 11 : (timeout/10)+1; // Each unit is 10ms, valid range 1..n
#endif
return; return;
} }
@@ -2964,8 +3470,33 @@ uint8_t mzSweepKeys(void)
{ {
keyboard.shiftKey = 0; keyboard.shiftKey = 0;
} }
#endif
#if (TARGET_HOST_MZ80A == 1) #elif (TARGET_HOST_MZ80A == 1)
// Check for modifiers.
//
if((keyboard.scanbuf[0][0] & 0x01) == 0)
{
keyboard.shiftKey = 1;
} else
{
keyboard.shiftKey = 0;
}
if((keyboard.scanbuf[0][0] & 0x80) == 0 && keyboard.shiftKey == 0)
{
keyboard.ctrlKey = 1;
} else
{
keyboard.ctrlKey = 0;
}
if((keyboard.scanbuf[0][0] & 0x80) == 0 && keyboard.shiftKey == 1)
{
keyboard.breakKey = 1;
} else
{
keyboard.breakKey = 0;
}
#elif (TARGET_HOST_MZ2000 == 1)
// Check for modifiers. // Check for modifiers.
// //
if((keyboard.scanbuf[0][0] & 0x01) == 0) if((keyboard.scanbuf[0][0] & 0x01) == 0)

View File

@@ -36,13 +36,41 @@
extern "C" { extern "C" {
#endif #endif
// Build time target. Overrides if compile time definition given.
#if defined(TARGET_HOST_MZ700)
#define TARGET_HOST_MZ700 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ2000)
#define TARGET_HOST_MZ2000 1
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ80A)
#define TARGET_HOST_MZ80A 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_PCW8XXX) || defined(TARGET_HOST_PCW9XXX)
#define TARGET_HOST_PCW 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#else
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700 #define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000 #define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 1 // MZ80A #define TARGET_HOST_MZ80A 0 // MZ80A
#define TARGET_HOST_PCW 0 // Amstrad PCW8XXX/9XXX
#endif
// Video display constants. // Video display constants.
#define VC_MAX_ROWS 25 // Maximum number of rows on display. #define VC_MAX_ROWS 25 // Maximum number of rows on display.
#if defined(TARGET_HOST_MZ700)
#define VC_MAX_COLUMNS 40 // Maximum number of columns on display.
#else
#define VC_MAX_COLUMNS 80 // Maximum number of columns on display. #define VC_MAX_COLUMNS 80 // Maximum number of columns on display.
#endif
#define VC_MAX_BUFFER_ROWS 50 // Maximum number of backing store rows for scrollback feature. #define VC_MAX_BUFFER_ROWS 50 // Maximum number of backing store rows for scrollback feature.
#define VC_DISPLAY_BUFFER_SIZE VC_MAX_COLUMNS * VC_MAX_BUFFER_ROWS // Size of the display buffer for scrollback. #define VC_DISPLAY_BUFFER_SIZE VC_MAX_COLUMNS * VC_MAX_BUFFER_ROWS // Size of the display buffer for scrollback.
@@ -57,7 +85,7 @@
// Audio constants. // Audio constants.
#define TIMER_8253_MZ80A_FREQ 2000000 // Base input frequency of Timer 0 for square wave generation. #define TIMER_8253_MZ80A_FREQ 2000000 // Base input frequency of Timer 0 for square wave generation.
#define TIMER_8253_MZ700 768000 // Base input frequency of Timer 0 for square wave generation. #define TIMER_8253_MZ700_FREQ 768000 // Base input frequency of Timer 0 for square wave generation.
// Base addresses and sizes within the Video Controller. // Base addresses and sizes within the Video Controller.
#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller. #define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller.
@@ -285,7 +313,7 @@ typedef struct {
uint8_t dispCode; uint8_t dispCode;
} t_dispCodeMap; } t_dispCodeMap;
// Mapping table from keyboard scan codes to Sharp MZ-700 keys. // Mapping table from keyboard scan codes to Sharp MZ keys.
// //
typedef struct { typedef struct {
uint8_t scanCode[80]; uint8_t scanCode[80];

View File

@@ -431,7 +431,7 @@ uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
//-------------------------------------------------------- //--------------------------------------------------------
// Test Methods. // Test Methods.
//-------------------------------------------------------- //--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS #if defined(INCLUDE_TEST_METHODS) && INCLUDE_TEST_METHODS == 1
#include "z80io_test.c" #include "z80io_test.c"
#else #else
uint8_t z80io_Z80_TestMemory(void) uint8_t z80io_Z80_TestMemory(void)

View File

@@ -39,7 +39,7 @@
#endif #endif
// Definitions to control compilation. // Definitions to control compilation.
//#define INCLUDE_TEST_METHODS 0 #define INCLUDE_TEST_METHODS 0
// CPLD Commands. // CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10 #define CPLD_CMD_FETCH_ADDR 0x10
@@ -82,6 +82,14 @@
#define CPLD_CMD_READIO_ADDR_P5 0x35 #define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36 #define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37 #define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_READIO_WRITE_ADDR 0x38
#define CPLD_CMD_READIO_WRITE_ADDR_P1 0x39
#define CPLD_CMD_READIO_WRITE_ADDR_P2 0x3A
#define CPLD_CMD_READIO_WRITE_ADDR_P3 0x3B
#define CPLD_CMD_READIO_WRITE_ADDR_P4 0x3C
#define CPLD_CMD_READIO_WRITE_ADDR_P5 0x3D
#define CPLD_CMD_READIO_WRITE_ADDR_P6 0x3E
#define CPLD_CMD_READIO_WRITE_ADDR_P7 0x3F
#define CPLD_CMD_HALT 0x50 #define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51 #define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0 #define CPLD_CMD_SET_SIGROUP1 0xF0
@@ -386,8 +394,8 @@
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4) #define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4) #define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4)
#define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4) #define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
@@ -396,9 +404,18 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
} }
#define SPI_SEND_I_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -406,10 +423,9 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -417,24 +433,63 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
#define SPI_SEND32i(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) != 0);\
pr_info("Stage 0");\
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
pr_info("Stage 1");\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
pr_info("Stage 2");\ while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
timeout = MAX_CHECK_CNT; \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
pr_info("Stage 3");\ }
#define SPI_SET_FRAME_SIZE() { MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_I_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND_48(_d1_, _d2_, _d3_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 6); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d3_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+2, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
// while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };
// read 2 byte // read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2)) #define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte // write 2 byte

View File

@@ -1,794 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80ctrl.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Control Interface
// This file contains a command line utility tool for controlling the z80drv device
// driver. The tool allows manipulation of the emulated Z80, inspection of its
// memory and data, transmission of adhoc commands to the underlying CPLD-Z80
// gateway and loading/saving of programs and data to/from the Z80 virtual and
// host memory.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of the Z80 device driver.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <Z/constants/pointer.h>
#include <Z/macros/member.h>
#include <Z/macros/array.h>
#include <Z80.h>
#include "z80driver.h"
#define VERSION "1.0"
#define AUTHOR "P.D.Smart"
#define COPYRIGHT "(c) 2018-22"
// Getopt_long is buggy so we use optparse.
#define OPTPARSE_IMPLEMENTATION
#define OPTPARSE_API static
#include "optparse.h"
// Device driver name.
#define DEVICE_FILENAME "/dev/z80drv"
// Constants for the Sharp MZ80A MZF file format.
#define MZF_HEADER_SIZE 128 // Size of the MZF header.
#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code.
#define MZF_FILENAME 0x01 // Title/Name (17 bytes).
#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed.
#define MZF_FILESIZE 0x12 // Size of program.
#define MZF_LOADADDR 0x14 // Load address of program.
#define MZF_EXECADDR 0x16 // Exec address of program.
#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code.
#define MZF_COMMENT_LEN 104 // Length of the comment field.
#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object.
#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program.
#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program.
#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0.
#define CMT_TYPE_TZOBJCD1 0x0F9
#define CMT_TYPE_TZOBJCD2 0x0FA
#define CMT_TYPE_TZOBJCD3 0x0FB
#define CMT_TYPE_TZOBJCD4 0x0FC
#define CMT_TYPE_TZOBJCD5 0x0FD
#define CMT_TYPE_TZOBJCD6 0x0FE
#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7.
#define MZ_CMT_ADDR 0x10F0
// Structure to define a Sharp MZ80A MZF directory structure. This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images.
//
typedef struct __attribute__((__packed__)) {
uint8_t attr; // MZF attribute describing the file.
uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename.
uint16_t fileSize; // Size of file.
uint16_t loadAddr; // Load address for the file.
uint16_t execAddr; // Execution address where the Z80 starts processing.
uint8_t comment[MZF_COMMENT_LEN]; // Text comment field but often contains a startup machine code program.
} t_svcDirEnt;
// Possible commands to be issued to the Z80 driver.
enum CTRL_COMMANDS {
Z80_CMD_STOP = 0,
Z80_CMD_START = 1,
Z80_CMD_PAUSE = 2,
Z80_CMD_CONTINUE = 3,
Z80_CMD_RESET = 4,
Z80_CMD_SPEED = 5,
Z80_CMD_HOST_RAM = 6,
Z80_CMD_VIRTUAL_RAM = 7,
Z80_CMD_DUMP_MEMORY = 8,
Z80_CMD_MEMORY_TEST = 9,
CPLD_CMD_SEND_CMD = 10,
CPLD_CMD_SPI_TEST = 11,
CPLD_CMD_PRL_TEST = 12
};
// Shared memory between this process and the Z80 driver.
static t_Z80Ctrl *Z80Ctrl = NULL;
// Method to obtain and return the output screen width.
//
uint8_t getScreenWidth(void)
{
return(MAX_SCREEN_WIDTH);
}
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch(uint8_t wait)
{
int r;
unsigned char c;
if(wait != 0 || (wait == 0 && kbhit()))
{
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
return 0;
}
void delay(int number_of_seconds)
{
// Converting time into milli_seconds
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
}
// Function to dump out a given section of memory via the UART.
//
int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryFlag, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth)
{
uint8_t displayWidth = dispwidth;;
uint32_t pnt = memaddr;
uint32_t endAddr = memaddr + memsize;
uint32_t addr = dispaddr;
uint32_t i = 0;
//uint32_t data;
int8_t keyIn;
int result = -1;
char c = 0;
// Sanity check. memoryFlag == 0 required kernel driver to dump so we exit as it cannot be performed here.
if(memoryFlag == 0)
return(-1);
// Reconfigure terminal to allow non-blocking key input.
//
set_conio_terminal_mode();
// If not set, calculate output line width according to connected display width.
//
if(displayWidth == 0)
{
switch(getScreenWidth())
{
case 40:
displayWidth = 8;
break;
case 80:
displayWidth = 16;
break;
default:
displayWidth = 32;
break;
}
}
while (1)
{
printf("%08lX", addr); // print address
printf(": ");
// print hexadecimal data
for (i=0; i < displayWidth; )
{
switch(memwidth)
{
case 16:
if(pnt+i < endAddr)
printf("%04X", memoryFlag == 1 ? (uint16_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint16_t)Z80Ctrl->page[pnt+i] : (uint16_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 32:
if(pnt+i < endAddr)
printf("%08lX", memoryFlag == 1 ? (uint32_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint32_t)Z80Ctrl->page[pnt+i] : (uint32_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 8:
default:
if(pnt+i < endAddr)
printf("%02X", memoryFlag == 1 ? (uint8_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint8_t)Z80Ctrl->page[pnt+i] : (uint8_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
}
fputc((char)' ', stdout);
}
// print ascii data
printf(" |");
// print single ascii char
for (i=0; i < displayWidth; i++)
{
c = memoryFlag == 1 ? (char)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (char)Z80Ctrl->page[pnt+i] : (char)Z80Ctrl->iopage[pnt+i];
if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~'))
fputc((char)c, stdout);
else
fputc((char)' ', stdout);
}
printf("|\r\n");
fflush(stdout);
// Move on one row.
pnt += displayWidth;
addr += displayWidth;
// User abort (ESC), pause (Space) or all done?
//
keyIn = getch(0);
if(keyIn == ' ')
{
do {
keyIn = getch(0);
} while(keyIn != ' ' && keyIn != 0x1b);
}
// Escape key pressed, exit with 0 to indicate this to caller.
if (keyIn == 0x1b)
{
sleep(1);
result = 0;
goto memoryDumpExit;
}
// End of buffer, exit the loop.
if(pnt >= (memaddr + memsize))
{
break;
}
}
// Normal exit, return -1 to show no key pressed.
memoryDumpExit:
reset_terminal_mode();
return(result);
}
// Method to load a program or data file into the Z80 memory. First load into Virtual memory and then trigger a sync to bring Host RAM in line.
//
int z80load(int fdZ80, char *fileName)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
t_svcDirEnt mzfHeader;
// Pause the Z80.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Open the file and read directly into the Virtual memory via the share.
FILE *ptr;
ptr = fopen(fileName, "rb");
if(ptr)
{
// First the header.
fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr);
#if(TARGET_HOST_MZ700 == 1)
if(mzfHeader.loadAddr > 0x1000)
{
#endif
// Copy in the header.
memcpy((uint8_t *)&Z80Ctrl->memory[MZ_CMT_ADDR], (uint8_t *)&mzfHeader, MZF_HEADER_SIZE);
// Now read in the data.
fread(&Z80Ctrl->memory[mzfHeader.loadAddr], mzfHeader.fileSize, 1, ptr);
printf("Loaded %s, Size:%04x, Addr:%04x, Exec:%04x\n", fileName, mzfHeader.fileSize, mzfHeader.loadAddr, mzfHeader.execAddr);
#if(TARGET_HOST_MZ700 == 1)
}
#endif
// Sync the loaded image from Virtual memory to hard memory.
ioctlCmd.cmd = IOCTL_CMD_SYNC_TO_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#if(TARGET_HOST_MZ2000 == 1)
// Set PC to 2 (NST) which switches to RUN mode and executes at 0000H
ioctlCmd.z80.pc = 2;
#endif
#if(TARGET_HOST_MZ700 == 1)
// MZ-700 just use the MZF header exec address.
ioctlCmd.z80.pc = mzfHeader.execAddr;
#endif
// Set PC to required setting ready for run.
ioctlCmd.cmd = IOCTL_CMD_SETPC;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Resume Z80 processing.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
else
printf("Couldnt open file\n");
return ret;
}
// Method to request basic Z80 operations.
//
int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long param3)
{
// Locals.
struct ioctlCmd ioctlCmd;
uint32_t idx;
int ret = 0;
switch(cmd)
{
case Z80_CMD_STOP:
// Use IOCTL to request Z80 to Stop (power off) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_START:
// Use IOCTL to request Z80 to Start (power on) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_PAUSE:
// Use IOCTL to request Z80 to pause processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_CONTINUE:
// Use IOCTL to request Z80 continue processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_RESET:
// Use IOCTL to request Z80 reset.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_SPEED:
// Check value is in range.
for(idx=1; idx < 256; idx+=idx)
{
if((uint32_t)param1 == idx) break;
}
if(idx == 256)
{
printf("Speed factor is illegal. It must be a multiple value of the original CPU clock, ie. 1x, 2x, 4x etc\n");
ret = -1;
} else
{
// Use IOCTL to request Z80 cpu freq change.
ioctlCmd.speed.speedMultiplier = (uint32_t)param1;
ioctlCmd.cmd = IOCTL_CMD_Z80_CPU_FREQ;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case CPLD_CMD_SEND_CMD:
// Build up the IOCTL command to request the given data is sent to the CPLD.
ioctlCmd.cmd = IOCTL_CMD_CPLD_CMD;
ioctlCmd.cpld.cmd = (uint32_t)param1;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_DUMP_MEMORY:
// If virtual memory, we can dump it via the shared memory segment.
if((uint8_t)param1)
{
memoryDump((uint32_t)param2, (uint32_t)param3, (uint8_t)param1, (uint8_t)param1 == 2 || (uint8_t)param1 == 3 ? 32 : 8, (uint32_t)param2, 0);
} else
{
// Build an IOCTL command to get the driver to dump the memory.
ioctlCmd.cmd = IOCTL_CMD_DUMP_MEMORY;
ioctlCmd.addr.start = (uint32_t)param2;
ioctlCmd.addr.end = (uint32_t)param2+(uint32_t)param3;
ioctlCmd.addr.size = (uint32_t)param3;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case Z80_CMD_HOST_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_VIRTUAL_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_VIRTUAL_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_MEMORY_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_Z80_MEMTEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_PRL_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_PRL_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_SPI_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_SPI_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
default:
printf("Command not supported!\n");
ret = -1;
break;
}
return ret;
}
// Method to perform some simple tests on the Z80 emulator.
//
int z80test(int fdZ80)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
// Stop the Z80.
//
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
FILE *ptr;
ptr = fopen("/customer/mz700.rom", "rb");
if(ptr)
{
fread(&Z80Ctrl->memory, 65536, 1, ptr);
} else printf("Couldnt open file\n");
// Configure the Z80.
//
printf("Send SETPC\n");
ioctlCmd.z80.pc = 0;
ioctl(fdZ80, IOCTL_CMD_SETPC, &ioctlCmd);
memoryDump(0 , 65536, 1, 8, 0, 0);
// Start the Z80.
//
printf("Send START\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(10);
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
memoryDump(0, 65536, 1, 8, 0, 0);
out:
return ret;
}
// Output usage screen. So mamy commands you do need to be prompted!!
void showArgs(char *progName, struct optparse *options)
{
printf("%s %s %s %s\n\n", progName, VERSION, COPYRIGHT, AUTHOR);
printf("Synopsis:\n");
printf("%s --help # This help screen.\n", progName);
printf(" --cmd <command> = RESET # Reset the Z80\n");
printf(" = STOP # Stop and power off the Z80\n");
printf(" = START # Power on and start the Z80\n");
printf(" = PAUSE # Pause running Z80\n");
printf(" = CONTINUE # Continue Z80 execution\n");
printf(" = HOSTRAM # Use HOST DRAM\n");
printf(" = VIRTRAM # Use Virtual RAM\n");
printf(" = SPEED --speed <1, 2, 4, 8, 16, 32, 64, 128> # In Virtual RAM mode, set CPU speed to base clock x factor.\n");
printf(" = LOADMZF --file <mzf filename> # Load MZF file into memory.\n");
printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>]--virtual <0 - Host RAM, 1 = Virtual RAM, 2 = PageTable, 3 = IOPageTable>\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
printf(" = Z80TEST # Perform various debugging tests\n");
printf(" = SPITEST # Perform SPI testing\n");
printf(" = PRLTEST # Perform Parallel Bus testing\n");
printf(" = Z80MEMTEST # Perform HOST memory tests.\n");
printf(" --<cmd> # Some commands can be abbreviated.\n");
}
int main(int argc, char *argv[])
{
int fdZ80;
char buff[64];
char cmd[64] = { 0 };
char fileName[256] = { 0 };
int opt;
uint32_t hexData = 0;
long speedMultiplier = 1;
long startAddr = 0x0000;
long endAddr = 0x1000;
int virtualMemory = 0;
int helpFlag = 0;
int verboseFlag = 0;
// Define parameters to be processed.
struct optparse options;
static struct optparse_long long_options[] =
{
{"help", 'h', OPTPARSE_NONE},
{"cmd", 'c', OPTPARSE_REQUIRED},
{"file", 'f', OPTPARSE_REQUIRED},
{"data", 'd', OPTPARSE_REQUIRED},
{"speed", 'S', OPTPARSE_REQUIRED},
{"virtual", 'V', OPTPARSE_REQUIRED},
{"addr", 'a', OPTPARSE_REQUIRED},
{"end", 'e', OPTPARSE_REQUIRED},
{"size", 's', OPTPARSE_REQUIRED},
{"verbose", 'v', OPTPARSE_NONE},
{"dump", '1', OPTPARSE_NONE},
{"loadmzf", '2', OPTPARSE_NONE},
{"reset", '3', OPTPARSE_NONE},
{"stop", '4', OPTPARSE_NONE},
{"start", '5', OPTPARSE_NONE},
{"pause", '6', OPTPARSE_NONE},
{"continue", '7', OPTPARSE_NONE},
{"speed", '8', OPTPARSE_NONE},
{"cpldcmd", '9', OPTPARSE_NONE},
{0}
};
// Parse the command line options.
//
optparse_init(&options, argv);
while((opt = optparse_long(&options, long_options, NULL)) != -1)
{
switch(opt)
{
// Hex data.
case 'd':
// hexData = (uint32_t)strtol(options.optarg, NULL, 0);
sscanf(options.optarg, "0x%08x", &hexData);
printf("Hex data:%08x\n", hexData);
break;
// Start address for memory operations.
case 'a':
startAddr = strtol(options.optarg, NULL, 0);
//printf("Start Addr:%04x\n", startAddr);
break;
// Speed multiplication factor for CPU governor when running in virtual memory.
case 'S':
speedMultiplier = strtol(options.optarg, NULL, 0);
//printf("Speed = base freq x %d\n", speedFactor);
break;
// End address for memory operations.
case 'e':
endAddr = strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Size instead of end address for memory operations.
case 's':
endAddr = startAddr + strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Virtual memory flag, 0 = host, 1 = virtual memory, 2 = page table, 3 = iopage table.
case 'V':
virtualMemory = atoi(options.optarg);
break;
// Filename.
case 'f':
strcpy(fileName, options.optarg);
break;
// Command to execute.
case 'c':
strcpy(cmd, options.optarg);
break;
// Quick command flags.
case '1':
strcpy(cmd, "DUMP");
break;
case '2':
strcpy(cmd, "LOADMZF");
break;
case '3':
strcpy(cmd, "RESET");
break;
case '4':
strcpy(cmd, "STOP");
break;
case '5':
strcpy(cmd, "START");
break;
case '6':
strcpy(cmd, "PAUSE");
break;
case '7':
strcpy(cmd, "CONTINUE");
break;
case '8':
strcpy(cmd, "SPEED");
break;
case '9':
strcpy(cmd, "CPLDCMD");
break;
// Verbose mode.
case 'v':
verboseFlag = 1;
break;
// Command help needed.
case 'h':
helpFlag = 1;
showArgs(argv[0], &options);
break;
// Unrecognised, show synopsis.
case '?':
showArgs(argv[0], &options);
printf("%s: %s\n", argv[0], options.errmsg);
return(1);
}
}
// Open the z80drv driver and attach to its shared memory, basically the Z80 control structure which includes the virtual Z80 memory.
fdZ80 = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
if(fdZ80 >= 0)
{
Z80Ctrl = (t_Z80Ctrl *)mmap(0, sizeof(t_Z80Ctrl), PROT_READ | PROT_WRITE, MAP_SHARED, fdZ80, 0);
if(Z80Ctrl == (void *)-1)
{
printf("Failed to attach to the Z80 Control structure, cannot continue, exitting....\n");
close(fdZ80);
exit(1);
}
} else
{
printf("Failed to open the Z80 Driver, exitting...\n");
exit(1);
}
// Basic string to method mapping. Started off with just 1 or two but has grown, may need a table!
if(strcasecmp(cmd, "LOADMZF") == 0)
{
z80load(fdZ80, fileName);
} else
if(strcasecmp(cmd, "RESET") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_RESET, 0, 0, 0);
} else
if(strcasecmp(cmd, "STOP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_STOP, 0, 0, 0);
} else
if(strcasecmp(cmd, "START") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_START, 0, 0, 0);
} else
if(strcasecmp(cmd, "PAUSE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_PAUSE, 0, 0, 0);
} else
if(strcasecmp(cmd, "CONTINUE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_CONTINUE, 0, 0, 0);
} else
if(strcasecmp(cmd, "SPEED") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_SPEED, speedMultiplier, 0, 0);
} else
if(strcasecmp(cmd, "DUMP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_DUMP_MEMORY, virtualMemory, startAddr, (endAddr - startAddr));
} else
if(strcasecmp(cmd, "HOSTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_HOST_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "VIRTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_VIRTUAL_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "CPLDCMD") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SEND_CMD, hexData, 0, 0);
} else
// Test methods, if the code is built-in to the driver.
if(strcasecmp(cmd, "Z80TEST") == 0)
{
z80test(fdZ80);
} else
if(strcasecmp(cmd, "SPITEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SPI_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "PRLTEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_PRL_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "Z80MEMTEST") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_MEMORY_TEST, 0, 0, 0);
}
else
{
showArgs(argv[0], &options);
printf("No command given, nothing done!\n");
}
// Unmap shared memory and close the device.
munmap(Z80Ctrl, sizeof(t_Z80Ctrl));
close(fdZ80);
return(0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,326 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80driver.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Driver
// This file contains the declarations used in the z80drv device driver.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of this driver and in all ways, is compatible with
// the original Z80.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80DRIVER_H
#define Z80DRIVER_H
// Constants.
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ2000 1
#define Z80_VIRTUAL_ROM_SIZE 16384 // Sized to maximum ROM which is the MZ-800 ROM.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 8) // (PAGE_SIZE * 2) // max size mmaped to userspace
#define Z80_VIRTUAL_MEMORY_SIZE Z80_VIRTUAL_RAM_SIZE + Z80_VIRTUAL_ROM_SIZE
#define Z80_MEMORY_PAGE_SIZE 16
#define MAX_SCREEN_WIDTH 132
#define DEVICE_NAME "z80drv"
#define CLASS_NAME "mogu"
// Memory and IO page types. Used to create a memory page which maps type of address space to real address space on host or virtual memory.
#define MEMORY_TYPE_VIRTUAL_MASK 0x00FFFFFF
#define MEMORY_TYPE_REAL_MASK 0x0000FFFF
#define IO_TYPE_MASK 0x0000FFFF
#define MEMORY_TYPE_INHIBIT 0x00000000
#define MEMORY_TYPE_PHYSICAL_RAM 0x80000000
#define MEMORY_TYPE_PHYSICAL_ROM 0x40000000
#define MEMORY_TYPE_PHYSICAL_VRAM 0x20000000
#define MEMORY_TYPE_PHYSICAL_HW 0x10000000
#define MEMORY_TYPE_VIRTUAL_RAM 0x08000000
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_HW 0x02000000
#define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000
// Approximate governor delays to regulate emulated CPU speed.
// MZ-700
#if(TARGET_HOST_MZ700 == 1)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
// MZ-2000
#if(TARGET_HOST_MZ2000 == 1)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 243
#define INSTRUCTION_DELAY_ROM_7MHZ 122
#define INSTRUCTION_DELAY_ROM_14MHZ 61
#define INSTRUCTION_DELAY_ROM_28MHZ 30
#define INSTRUCTION_DELAY_ROM_56MHZ 15
#define INSTRUCTION_DELAY_ROM_112MHZ 7
#define INSTRUCTION_DELAY_ROM_224MHZ 3
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 218
#define INSTRUCTION_DELAY_RAM_7MHZ 112
#define INSTRUCTION_DELAY_RAM_14MHZ 56
#define INSTRUCTION_DELAY_RAM_28MHZ 28
#define INSTRUCTION_DELAY_RAM_56MHZ 14
#define INSTRUCTION_DELAY_RAM_112MHZ 7
#define INSTRUCTION_DELAY_RAM_224MHZ 3
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S'
#define IOCTL_CMD_Z80_PAUSE 'P'
#define IOCTL_CMD_Z80_RESET 'R'
#define IOCTL_CMD_Z80_CONTINUE 'C'
#define IOCTL_CMD_USE_HOST_RAM 'x'
#define IOCTL_CMD_USE_VIRTUAL_RAM 'X'
#define IOCTL_CMD_DUMP_MEMORY 'M'
#define IOCTL_CMD_Z80_CPU_FREQ 'F'
#define IOCTL_CMD_CPLD_CMD 'z'
#define IOCTL_CMD_SEND _IOW('c', 'c', int32_t *)
#define IOCTL_CMD_SETPC _IOW('p', 'p', int32_t *)
#define IOCTL_CMD_SYNC_TO_HOST_RAM 'V'
#define IOCTL_CMD_SPI_TEST '1'
#define IOCTL_CMD_PRL_TEST '2'
#define IOCTL_CMD_Z80_MEMTEST '3'
// Chip Select map MZ80K-MZ700.
//
// 0000 - 0FFF = CS_ROMni : R/W : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF = CS_RAMni : R/W : MZ80K/A/700 = RAM
// C000 - CFFF = CS_ROMni : R/W : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF = CS_VRAMni : R/W : MZ80K/A/700 = VRAM
// D800 - DFFF = CS_VRAMni : R/W : MZ700 = Colour VRAM (MZ700)
// E000 - E003 = CS_8255n : R/W : MZ80K/A/700 = 8255
// E004 - E007 = CS_8254n : R/W : MZ80K/A/700 = 8254
// E008 - E00B = CS_LS367n : R/W : MZ80K/A/700 = LS367
// E00C - E00F = CS_ESWPn : R : MZ80A = Memory Swap (MZ80A)
// E010 - E013 = CS_ESWPn : R : MZ80A = Reset Memory Swap (MZ80A)
// E014 = CS_E5n : R/W : MZ80A/700 = Normal CRT display (in Video Controller)
// E015 = CS_E6n : R/W : MZ80A/700 = Reverse CRT display (in Video Controller)
// E200 - E2FF = : R/W : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF = : R/W : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF = : R/W : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF = : R/W : MZ80K/A/700 = Floppy Disk interface.
//
// Chip Select map MZ800
//
// FC - FF = CS_PIOn : R/W : MZ800/MZ1500 = Z80 PIO Printer Interface
// F2 = CS_PSG0n : W : MZ800/MZ1500 = Programable Sound Generator, MZ-800 = Mono, MZ-1500 = Left Channel
// F3 = CS_PSG1n : W : MZ1500 = Programable Sound Generator, MZ-1500 = Right Channel
// E9 = CS_PSG(X)n: W : MZ1500 = Simultaneous write to both PSG's.
// F0 - F1 = CS_JOYSTK : R : MZ800 = Joystick 1 and 2
// CC = CS_GWF : W : MZ800 = CRTC GWF Write format Register
// CD = CS_GRF : W : MZ800 = CRTC GRF Read format Register
// CE = CS_GDMD : W : MZ800 = CRTC GDMD Mode Register
// CF = CS_GCRTC : W : MZ800 = CRTC GCRTC Control Register
// D4 - D7 = CS
// D000 - DFFF
// MZ700/MZ800 memory mode switch?
//
// MZ-700 MZ-800
// |0000:0FFF|1000:1FFF|1000:CFFF|C000:CFFF|D000:FFFF |0000:7FFF|1000:1FFF|2000:7FFF|8000:BFFF|C000:CFFF|C000:DFFF|E000:FFFF
// -------------------------------------------------- ----------------------------------------------------------------------
// OUT 0xE0 = |DRAM | | | | |DRAM | | | | | |
// OUT 0xE1 = | | | | |DRAM | | | | | | |DRAM
// OUT 0xE2 = |MONITOR | | | | |MONITOR | | | | | |
// OUT 0xE3 = | | | | |Memory Mapped I/O | | | | | | |Upper MONITOR ROM
// OUT 0xE4 = |MONITOR | |DRAM | |Memory Mapped I/O |MONITOR |CGROM |DRAM |VRAM | |DRAM |Upper MONITOR ROM
// OUT 0xE5 = | | | | |Inhibit | | | | | | |Inhibit
// OUT 0xE6 = | | | | |<return> | | | | | | |<return>
// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | |
// IN 0xE1 = | |DRAM | |DRAM | | |<return> | |DRAM | | |
//
// <return> = Return to the state prior to the complimentary command being invoked.
// * = MZ-800 host only.
// Macros to lookup and test to see if a given memory block or IO byte is of a given type. Also macros to read/write to the memory block and IO byte.
#define MEMORY_BLOCK_GRANULARITY 0x800
#define MEMORY_BLOCK_SLOTS (0x10000 / MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_MASK (0x10000 - MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_SHIFT 11
#define getPageData(a) (Z80Ctrl->page[(a & 0xF800) >> MEMORY_BLOCK_SHIFT])
#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF])
#define getPageType(a, mask) (getPageData(a) & mask)
#define getPageAddr(a, mask) ((getPageData(a) & mask) + (a & (MEMORY_BLOCK_GRANULARITY-1)))
#define getIOPageType(a, mask) (getIOPageData(a) & mask)
#define getIOPageAddr(a, mask) (getIOPageData(a) & mask)
#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)])
#define realPort(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_MASK)
#define isPhysicalRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_RAM))
#define isPhysicalVRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_VRAM))
#define isPhysicalROM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_ROM))
#define isPhysicalMemory(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))])
#define isPhysicalHW(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_HW))
#define isPhysical(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM)))
#define isPhysicalIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_PHYSICAL_HW)
#define isVirtualRAM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM))
#define isVirtualROM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_ROM))
#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM)))
#define isVirtualHW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_HW))
#define isVirtualIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_VIRTUAL_HW)
#define isHW(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_VIRTUAL_HW)))
#define readVirtualRAM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ])
#define readVirtualROM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE ])
#define writeVirtualRAM(a, d) { Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; }
#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; }
#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = Z80Ctrl->page[_block_]; }
#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; }
#define IO_ADDR_E0 0xE0
#define IO_ADDR_E1 0xE1
#define IO_ADDR_E2 0xE2
#define IO_ADDR_E3 0xE3
#define IO_ADDR_E4 0xE4
#define IO_ADDR_E5 0xE5
#define IO_ADDR_E6 0xE6
#define IO_ADDR_E7 0xE7
#define IO_ADDR_E8 0xE8
#define IO_ADDR_E9 0xE9
#define IO_ADDR_EA 0xEA
#define IO_ADDR_EB 0xEB
enum Z80_RUN_STATES {
Z80_STOP = 0x00,
Z80_STOPPED = 0x01,
Z80_PAUSE = 0x02,
Z80_PAUSED = 0x03,
Z80_CONTINUE = 0x04,
Z80_RUNNING = 0x05,
};
enum Z80_MEMORY_PROFILE {
USE_PHYSICAL_RAM = 0x00,
USE_VIRTUAL_RAM = 0x01
};
typedef struct {
// Main memory, linear but indexed as though it were banks in 1K pages.
uint8_t memory[Z80_VIRTUAL_MEMORY_SIZE];
// Page pointer map.
//
// Each pointer points to a byte or block of bytes in the Z80 Memory frame, 64K Real + Banked.
// This is currently set at a block of size 0x800 per memory pointer for the MZ-700.
// The LSB of the pointer is a direct memory index to a byte or block of bytes, the upper byte of the pointer indicates type of memory space.
// 0x80<FFFFFF> - physical host RAM
// 0x40<FFFFFF> - physical host ROM
// 0x20<FFFFFF> - physical host VRAM
// 0x10<FFFFFF> - physical host hardware
// 0x08<FFFFFF> - virtual host RAM
// 0x04<FFFFFF> - virtual host ROM
// 0x02<FFFFFF> - virtual host hardware
// 16bit Input Address -> map -> Pointer to 24bit memory address + type flag.
// -> Pointer+<low bits of address> to 24bit memory address + type flag.
uint32_t page[MEMORY_BLOCK_SLOTS];
uint32_t shadowPage[MEMORY_BLOCK_SLOTS];
// I/O Page map.
//
// This is a map to indicate the use of the I/O page and allow any required remapping.
// <0x80>FF<I/O Address> - physical host hardware
// <0x40>FF<I/O Address> - virtual host hardware
// 16bit Input Address -> map -> Actual 16bit address to use + type flag.
uint32_t iopage[65536];
// Default page mode configured. This value reflects the default page and iotable map.
uint8_t defaultPageMode;
// Refresh DRAM mode. 1 = Refresh, 0 = No refresh. Only applicable when running code in virtual Kernel RAM.
uint8_t refreshDRAM;
// Inhibit mode is where certain memory ranges are inhibitted. The memory page is set to inhibit and this flag
// blocks actions which arent allowed during inhibit.
uint8_t inhibitMode;
// Address caching. Used to minimise instruction length sent to CPLD.
uint16_t z80PrevAddr;
uint16_t z80PrevPort;
#if(TARGET_HOST_MZ2000 == 1)
uint8_t lowMemorySwap;
#endif
// Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe;
uint8_t keyportShiftCtrl;
uint8_t keyportHotKey;
// Governor is the delay in a 32bit loop per Z80 opcode, used to govern execution speed when using virtual memory.
// This mechanism will eventually be tied into the M/T-state calculation for a more precise delay, but at the moment,
// with the Z80 assigned to an isolated CPU, it allows time sensitive tasks such as the tape recorder to work.
// The lower the value the faster the CPU speed. Two values are present as the optimiser, seeing ROM code not changing
// is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made.
uint32_t cpuGovernorDelayROM;
uint32_t cpuGovernorDelayRAM;
} t_Z80Ctrl;
// IOCTL structure for passing data from user space to driver to perform commands.
//
struct z80_addr {
uint32_t start;
uint32_t end;
uint32_t size;
};
struct z80_ctrl {
uint16_t pc;
};
struct speed {
uint32_t speedMultiplier;
};
struct cpld_ctrl {
uint32_t cmd;
};
struct ioctlCmd {
int32_t cmd;
union {
struct z80_addr addr;
struct z80_ctrl z80;
struct speed speed;
struct cpld_ctrl cpld;
};
};
// Prototypes.
void setupMemory(enum Z80_MEMORY_PROFILE mode);
#endif

View File

@@ -1,428 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the methods used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
//-------------------------------------------------------------------------------------------------------------------------------
//
// User space driver access.
//
//-------------------------------------------------------------------------------------------------------------------------------
// Initialise the SOM hardware used to communicate with the z80 socket and host hardware.
// The SOM interfaces to a CPLD which provides voltage level translation and also encapsulates the Z80 timing cycles as recreating
// them within the SOM is much more tricky.
//
// As this is an embedded device and performance/latency are priorities, minimal structured code is used to keep call stack and
// generated code to a mimimum without relying on the optimiser.
int z80io_init(void)
{
// Locals.
int ret = 0;
// Initialise GPIO. We call the HAL api to minimise time but for actual bit set/reset and read we go directly to registers to save time, increase throughput and minimise latency.
// Initialise the HAL.
MHal_GPIO_Init();
// Set the pads as GPIO devices. The HAL takes care of allocating and deallocating the padmux resources.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_0); // Word (16bit) bidirectional bus. Default is read with data set.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_HIGH_BYTE);
//MHal_GPIO_Pad_Set(PAD_GPIO8); // SPIO 4wire control lines setup by the spidev driver but controlled directly in this driver.
//MHal_GPIO_Pad_Set(PAD_GPIO9);
//MHal_GPIO_Pad_Set(PAD_GPIO10);
//MHal_GPIO_Pad_Set(PAD_GPIO11);
MHal_GPIO_Pad_Set(PAD_Z80IO_READY);
MHal_GPIO_Pad_Set(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Set(PAD_Z80IO_INT);
MHal_GPIO_Pad_Set(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Set(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Set(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Set(PAD_Z80IO_RSV1);
#ifdef NOTNEEDED
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_WRITE);
#endif
// Set required input pads.
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_0);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Odn(PAD_Z80IO_READY);
MHal_GPIO_Pad_Odn(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Odn(PAD_Z80IO_INT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Odn(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RSV1);
// Set required output pads.
#ifdef NOTNEEDED
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Oen(PAD_Z80IO_WRITE);
MHal_GPIO_Pull_High(PAD_Z80IO_WRITE);
#endif
// Control signals.
MHal_GPIO_Pad_Oen(PAD_Z80IO_HIGH_BYTE);
MHal_GPIO_Pull_High(PAD_Z80IO_HIGH_BYTE);
// Setup the MSPI0 device.
//
// Setup control, interrupts are not used.
MSPI_WRITE(MSPI_CTRL_OFFSET, MSPI_CPU_CLOCK_1_2 | MSPI_CTRL_CPOL_LOW | MSPI_CTRL_CPHA_HIGH | MSPI_CTRL_RESET | MSPI_CTRL_ENABLE_SPI);
// Setup LSB First mode.
MSPI_WRITE(MSPI_LSB_FIRST_OFFSET, 0x0);
// Setup clock.
CLK_WRITE(MSPI0_CLK_CFG, 0x1100)
// Setup the frame size (all buffers to 8bits).
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET+1, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET+1, 0xfff);
// Setup Chip Selects to inactive.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Switch Video and Audio to host.
z80io_SPI_Send16(0x00f0, NULL);
return ret;
}
//--------------------------------------------------------
// Parallel bus Methods.
//--------------------------------------------------------
// Methods to read data from the parallel bus.
// The CPLD returns status and Z80 data on the 8bit bus as it is marginally quicker than retrieving it over the SPI bus.
//
inline uint8_t z80io_PRL_Read8(uint8_t dataFlag)
{
// Locals.
uint8_t result = 0;
// Byte according to flag.
if(dataFlag)
SET_CPLD_READ_DATA()
else
SET_CPLD_READ_STATUS()
// Read the input registers and set value accordingly.
result = READ_CPLD_DATA_IN();
// Return 16bit value read from CPLD.
return(result);
}
inline uint16_t z80io_PRL_Read16(void)
{
// Locals.
uint16_t result = 0;
// Low byte first.
CLEAR_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result = (uint16_t)READ_CPLD_DATA_IN();
// High byte next.
SET_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result |= (uint16_t)(READ_CPLD_DATA_IN() << 8);
// Return 16bit value read from CPLD.
return(result);
}
// Parallel Bus methods were tried and tested but due to the GPIO bits being controlled by individual registers per bit, the setup time was longer
// than the transmission time of SPI. These methods are thus deprecated and a fusion of SPI and 8bit parallel is now used.
#ifdef NOTNEEDED
inline uint8_t z80io_PRL_Send8(uint8_t txData)
{
// Locals.
//
// Low byte only.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
inline uint8_t z80io_PRL_Send16(uint16_t txData)
{
// Locals.
//
// Low byte first.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
// High byte next.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
// Setup high byte.
if(txData & 0x8000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x4000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x2000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x1000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0800) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0400) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0200) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0100) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
#endif
//--------------------------------------------------------
// SPI Methods.
//--------------------------------------------------------
// Methods to send 8,16 or 32 bits. Each method is seperate to minimise logic and execution time, 8bit being most sensitive.
// Macros have also been defined for inline inclusion which dont read back the response data.
//
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint8_t)MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)(txData >> 16));
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint32_t)(MSPI_READ(MSPI_FULL_DEPLUX_RD00) | (MSPI_READ(MSPI_FULL_DEPLUX_RD02) << 16));
// Done.
return(timeout == 0);
}
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)
{
pr_info("Z80 Test Memory functionality not built-in.\n");
return(0);
}
uint8_t z80io_SPI_Test(void)
{
pr_info("SPI Test functionality not built-in.\n");
return(0);
}
uint8_t z80io_PRL_Test(void)
{
pr_info("Parallel Bus Test functionality not built-in.\n");
return(0);
}
#endif

View File

@@ -1,483 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the declarations used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80IO_H
#define Z80IO_H
#ifdef __cplusplus
extern "C" {
#endif
// Definitions to control compilation.
#define INCLUDE_TEST_METHODS 1
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
#define CPLD_CMD_FETCH_ADDR_P1 0x11
#define CPLD_CMD_FETCH_ADDR_P2 0x12
#define CPLD_CMD_FETCH_ADDR_P3 0x13
#define CPLD_CMD_FETCH_ADDR_P4 0x14
#define CPLD_CMD_FETCH_ADDR_P5 0x15
#define CPLD_CMD_FETCH_ADDR_P6 0x16
#define CPLD_CMD_FETCH_ADDR_P7 0x17
#define CPLD_CMD_WRITE_ADDR 0x18
#define CPLD_CMD_WRITE_ADDR_P1 0x19
#define CPLD_CMD_WRITE_ADDR_P2 0x1A
#define CPLD_CMD_WRITE_ADDR_P3 0x1B
#define CPLD_CMD_WRITE_ADDR_P4 0x1C
#define CPLD_CMD_WRITE_ADDR_P5 0x1D
#define CPLD_CMD_WRITE_ADDR_P6 0x1E
#define CPLD_CMD_WRITE_ADDR_P7 0x1F
#define CPLD_CMD_READ_ADDR 0x20
#define CPLD_CMD_READ_ADDR_P1 0x21
#define CPLD_CMD_READ_ADDR_P2 0x22
#define CPLD_CMD_READ_ADDR_P3 0x23
#define CPLD_CMD_READ_ADDR_P4 0x24
#define CPLD_CMD_READ_ADDR_P5 0x25
#define CPLD_CMD_READ_ADDR_P6 0x26
#define CPLD_CMD_READ_ADDR_P7 0x27
#define CPLD_CMD_WRITEIO_ADDR 0x28
#define CPLD_CMD_WRITEIO_ADDR_P1 0x29
#define CPLD_CMD_WRITEIO_ADDR_P2 0x2A
#define CPLD_CMD_WRITEIO_ADDR_P3 0x2B
#define CPLD_CMD_WRITEIO_ADDR_P4 0x2C
#define CPLD_CMD_WRITEIO_ADDR_P5 0x2D
#define CPLD_CMD_WRITEIO_ADDR_P6 0x2E
#define CPLD_CMD_WRITEIO_ADDR_P7 0x2F
#define CPLD_CMD_READIO_ADDR 0x30
#define CPLD_CMD_READIO_ADDR_P1 0x31
#define CPLD_CMD_READIO_ADDR_P2 0x32
#define CPLD_CMD_READIO_ADDR_P3 0x33
#define CPLD_CMD_READIO_ADDR_P4 0x34
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
#define CPLD_CMD_SET_AUTO_REFRESH 0xF1
#define CPLD_CMD_CLEAR_AUTO_REFRESH 0xF2
#define CPLD_CMD_SET_SPI_LOOPBACK 0xFE
#define CPLD_CMD_NOP1 0x00
#define CPLD_CMD_NOP2 0xFF
// Pad numbers for using the MHal GPIO library.
#define PAD_Z80IO_IN_DATA_0 PAD_GPIO0
#define PAD_Z80IO_IN_DATA_1 PAD_GPIO1
#define PAD_Z80IO_IN_DATA_2 PAD_GPIO2
#define PAD_Z80IO_IN_DATA_3 PAD_GPIO3
#define PAD_Z80IO_IN_DATA_4 PAD_GPIO4
#define PAD_Z80IO_IN_DATA_5 PAD_GPIO5
#define PAD_Z80IO_IN_DATA_6 PAD_GPIO6
#define PAD_Z80IO_IN_DATA_7 PAD_GPIO7
#define PAD_SPIO_0 PAD_GPIO8
#define PAD_SPIO_1 PAD_GPIO9
#define PAD_SPIO_2 PAD_GPIO10
#define PAD_SPIO_3 PAD_GPIO11
#define PAD_Z80IO_HIGH_BYTE PAD_SAR_GPIO2 // Byte requiured, 0 = Low Byte, 1 = High Byte.
#define PAD_Z80IO_READY PAD_GPIO12
#define PAD_Z80IO_LTSTATE PAD_PM_IRIN // IRIN
#define PAD_Z80IO_BUSRQ PAD_GPIO13
#define PAD_Z80IO_BUSACK PAD_GPIO14
#define PAD_Z80IO_INT PAD_UART0_RX // GPIO47
#define PAD_Z80IO_NMI PAD_UART0_TX // GPIO48
#define PAD_Z80IO_WAIT PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_RESET PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_RSV1 PAD_SATA_GPIO // GPIO90
// Physical register addresses.
#define PAD_Z80IO_IN_DATA_0_ADDR 0x103C00
#define PAD_Z80IO_IN_DATA_1_ADDR 0x103C02
#define PAD_Z80IO_IN_DATA_2_ADDR 0x103C04
#define PAD_Z80IO_IN_DATA_3_ADDR 0x103C06
#define PAD_Z80IO_IN_DATA_4_ADDR 0x103C08
#define PAD_Z80IO_IN_DATA_5_ADDR 0x103C0A
#define PAD_Z80IO_IN_DATA_6_ADDR 0x103C0C
#define PAD_Z80IO_IN_DATA_7_ADDR 0x103C0E
#define PAD_SPIO_0_ADDR 0x103C10
#define PAD_SPIO_1_ADDR 0x103C12
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_LTSTATE_ADDR 0xF28 // IRIN
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
#define PAD_Z80IO_INT_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_NMI_ADDR 0x103C32 // GPIO48
#define PAD_Z80IO_WAIT_ADDR 0x103C80 // GPIO85
#define PAD_Z80IO_RESET_ADDR 0x103C82 // GPIO86
#define PAD_Z80IO_RSV1_ADDR 0x103C8A // GPIO90
#ifdef NOTNEEDED
#define PAD_Z80IO_OUT_DATA_0 PAD_GPIO12
#define PAD_Z80IO_OUT_DATA_1 PAD_GPIO13
#define PAD_Z80IO_OUT_DATA_2 PAD_GPIO14
#define PAD_Z80IO_OUT_DATA_3 PAD_UART0_RX // GPIO47
#define PAD_Z80IO_OUT_DATA_4 PAD_UART0_TX // GPIO48
#define PAD_Z80IO_OUT_DATA_5 PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_OUT_DATA_6 PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_OUT_DATA_7 PAD_SATA_GPIO // GPIO90
#define PAD_Z80IO_WRITE PAD_PM_IRIN // Write data clock.
#endif
//-------------------------------------------------------------------------------------------------
// The definitions below come from SigmaStar kernel drivers. No header file exists hence the
// duplication.
//-------------------------------------------------------------------------------------------------
#define SUPPORT_SPI_1 0
#define MAX_SUPPORT_BITS 16
#define BANK_TO_ADDR32(b) (b<<9)
#define BANK_SIZE 0x200
#define MS_BASE_REG_RIU_PA 0x1F000000
#define gChipBaseAddr 0xFD203C00
#define gPmSleepBaseAddr 0xFD001C00
#define gSarBaseAddr 0xFD002800
#define gRIUBaseAddr 0xFD000000
#define gMOVDMAAddr 0xFD201600
#define gClkBaseAddr 0xFD207000
#define gMspBaseAddr 0xfd222000
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MSPI0_BANK_ADDR 0x1110
#define MSPI1_BANK_ADDR 0x1111
#define CLK__BANK_ADDR 0x1038
#define CHIPTOP_BANK_ADDR 0x101E
#define MOVDMA_BANK_ADDR 0x100B
#define BASE_REG_MSPI0_ADDR MSPI0_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111000)
#define BASE_REG_MSPI1_ADDR MSPI1_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111100)
#define BASE_REG_CLK_ADDR CLK__BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x103800)
#define BASE_REG_CHIPTOP_ADDR CHIPTOP_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x101E00)
//-------------------------------------------------------------------------------------------------
// Hardware Register Capability
//-------------------------------------------------------------------------------------------------
#define MSPI_WRITE_BUF_OFFSET 0x40
#define MSPI_READ_BUF_OFFSET 0x44
#define MSPI_WBF_SIZE_OFFSET 0x48
#define MSPI_RBF_SIZE_OFFSET 0x48
// read/ write buffer size
#define MSPI_RWSIZE_MASK 0xFF
#define MSPI_RSIZE_BIT_OFFSET 0x8
#define MAX_READ_BUF_SIZE 0x8
#define MAX_WRITE_BUF_SIZE 0x8
// CLK config
#define MSPI_CTRL_OFFSET 0x49
#define MSPI_CLK_CLOCK_OFFSET 0x49
#define MSPI_CLK_CLOCK_BIT_OFFSET 0x08
#define MSPI_CLK_CLOCK_MASK 0xFF
#define MSPI_CLK_PHASE_MASK 0x40
#define MSPI_CLK_PHASE_BIT_OFFSET 0x06
#define MSPI_CLK_POLARITY_MASK 0x80
#define MSPI_CLK_POLARITY_BIT_OFFSET 0x07
#define MSPI_CLK_PHASE_MAX 0x1
#define MSPI_CLK_POLARITY_MAX 0x1
#define MSPI_CLK_CLOCK_MAX 0x7
#define MSPI_CTRL_CPOL_LOW 0x00
#define MSPI_CTRL_CPOL_HIGH 0x80
#define MSPI_CTRL_CPHA_LOW 0x00
#define MSPI_CTRL_CPHA_HIGH 0x40
#define MSPI_CTRL_3WIRE 0x10
#define MSPI_CTRL_INTEN 0x04
#define MSPI_CTRL_RESET 0x02
#define MSPI_CTRL_ENABLE_SPI 0x01
// DC config
#define MSPI_DC_MASK 0xFF
#define MSPI_DC_BIT_OFFSET 0x08
#define MSPI_DC_TR_START_OFFSET 0x4A
#define MSPI_DC_TRSTART_MAX 0xFF
#define MSPI_DC_TR_END_OFFSET 0x4A
#define MSPI_DC_TREND_MAX 0xFF
#define MSPI_DC_TB_OFFSET 0x4B
#define MSPI_DC_TB_MAX 0xFF
#define MSPI_DC_TRW_OFFSET 0x4B
#define MSPI_DC_TRW_MAX 0xFF
// Frame Config
#define MSPI_FRAME_WBIT_OFFSET 0x4C
#define MSPI_FRAME_RBIT_OFFSET 0x4E
#define MSPI_FRAME_BIT_MAX 0x07
#define MSPI_FRAME_BIT_MASK 0x07
#define MSPI_FRAME_BIT_FIELD 0x03
#define MSPI_LSB_FIRST_OFFSET 0x50
#define MSPI_TRIGGER_OFFSET 0x5A
#define MSPI_DONE_OFFSET 0x5B
#define MSPI_DONE_CLEAR_OFFSET 0x5C
#define MSPI_CHIP_SELECT_OFFSET 0x5F
#define MSPI_CS1_DISABLE 0x01
#define MSPI_CS1_ENABLE 0x00
#define MSPI_CS2_DISABLE 0x02
#define MSPI_CS2_ENABLE 0x00
#define MSPI_CS3_DISABLE 0x04
#define MSPI_CS3_ENABLE 0x00
#define MSPI_CS4_DISABLE 0x08
#define MSPI_CS4_ENABLE 0x00
#define MSPI_CS5_DISABLE 0x10
#define MSPI_CS5_ENABLE 0x00
#define MSPI_CS6_DISABLE 0x20
#define MSPI_CS6_ENABLE 0x00
#define MSPI_CS7_DISABLE 0x40
#define MSPI_CS7_ENABLE 0x00
#define MSPI_CS8_DISABLE 0x80
#define MSPI_CS8_ENABLE 0x00
#define MSPI_FULL_DEPLUX_RD_CNT (0x77)
#define MSPI_FULL_DEPLUX_RD00 (0x78)
#define MSPI_FULL_DEPLUX_RD01 (0x78)
#define MSPI_FULL_DEPLUX_RD02 (0x79)
#define MSPI_FULL_DEPLUX_RD03 (0x79)
#define MSPI_FULL_DEPLUX_RD04 (0x7a)
#define MSPI_FULL_DEPLUX_RD05 (0x7a)
#define MSPI_FULL_DEPLUX_RD06 (0x7b)
#define MSPI_FULL_DEPLUX_RD07 (0x7b)
#define MSPI_FULL_DEPLUX_RD08 (0x7c)
#define MSPI_FULL_DEPLUX_RD09 (0x7c)
#define MSPI_FULL_DEPLUX_RD10 (0x7d)
#define MSPI_FULL_DEPLUX_RD11 (0x7d)
#define MSPI_FULL_DEPLUX_RD12 (0x7e)
#define MSPI_FULL_DEPLUX_RD13 (0x7e)
#define MSPI_FULL_DEPLUX_RD14 (0x7f)
#define MSPI_FULL_DEPLUX_RD15 (0x7f)
//chip select bit map
#define MSPI_CHIP_SELECT_MAX 0x07
// control bit
#define MSPI_DONE_FLAG 0x01
#define MSPI_TRIGGER 0x01
#define MSPI_CLEAR_DONE 0x01
#define MSPI_INT_ENABLE 0x04
#define MSPI_RESET 0x02
#define MSPI_ENABLE 0x01
// clk_mspi0
#define MSPI0_CLK_CFG 0x33 //bit 2 ~bit 3
#define MSPI0_CLK_108M 0x00
#define MSPI0_CLK_54M 0x04
#define MSPI0_CLK_12M 0x08
#define MSPI0_CLK_MASK 0x0F
// clk_mspi1
#define MSPI1_CLK_CFG 0x33 //bit 10 ~bit 11
#define MSPI1_CLK_108M 0x0000
#define MSPI1_CLK_54M 0x0400
#define MSPI1_CLK_12M 0x0800
#define MSPI1_CLK_MASK 0x0F00
// clk_mspi
#define MSPI_CLK_CFG 0x33
#define MSPI_SELECT_0 0x0000
#define MSPI_SELECT_1 0x4000
#define MSPI_CLK_MASK 0xF000
// Clock settings
#define MSPI_CPU_CLOCK_1_2 0x0000
#define MSPI_CPU_CLOCK_1_4 0x0100
#define MSPI_CPU_CLOCK_1_8 0x0200
#define MSPI_CPU_CLOCK_1_16 0x0300
#define MSPI_CPU_CLOCK_1_32 0x0400
#define MSPI_CPU_CLOCK_1_64 0x0500
#define MSPI_CPU_CLOCK_1_128 0x0600
#define MSPI_CPU_CLOCK_1_256 0x0700
//CHITOP 101E mspi mode select
#define MSPI0_MODE 0x0C //bit0~bit1
#define MSPI0_MODE_MASK 0x07
#define MSPI1_MODE 0x0C //bit4~bit5
#define MSPI1_MODE_MASK 0x70
#define EJTAG_MODE 0xF
#define EJTAG_MODE_1 0x01
#define EJTAG_MODE_2 0x02
#define EJTAG_MODE_3 0x03
#define EJTAG_MODE_MASK 0x03
//MOVDMA 100B
#define MOV_DMA_SRC_ADDR_L 0x03
#define MOV_DMA_SRC_ADDR_H 0x04
#define MOV_DMA_DST_ADDR_L 0x05
#define MOV_DMA_DST_ADDR_H 0x06
#define MOV_DMA_BYTE_CNT_L 0x07
#define MOV_DMA_BYTE_CNT_H 0x08
#define DMA_MOVE0_IRQ_CLR 0x28
#define MOV_DMA_IRQ_FINAL_STATUS 0x2A
#define DMA_MOVE0_ENABLE 0x00
#define DMA_RW 0x50 //0 for dma write to device, 1 for dma read from device
#define DMA_READ 0x01
#define DMA_WRITE 0x00
#define DMA_DEVICE_MODE 0x51
#define DMA_DEVICE_SEL 0x52
//spi dma
#define MSPI_DMA_DATA_LENGTH_L 0x30
#define MSPI_DMA_DATA_LENGTH_H 0x31
#define MSPI_DMA_ENABLE 0x32
#define MSPI_DMA_RW_MODE 0x33
#define MSPI_DMA_WRITE 0x00
#define MSPI_DMA_READ 0x01
#define MSTAR_SPI_TIMEOUT_MS 30000
#define MSTAR_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA /*| SPI_CS_HIGH | SPI_NO_CS | SPI_LSB_FIRST*/)
//-------------------------------------------------------------------------------------------------
// Macros
//-------------------------------------------------------------------------------------------------
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define READ_BYTE(_reg) (*(volatile u8*)(_reg))
#define READ_WORD(_reg) (*(volatile u16*)(_reg))
#define READ_LONG(_reg) (*(volatile u32*)(_reg))
#define WRITE_BYTE(_reg, _val) {(*((volatile u8*)(_reg))) = (u8)(_val); }
#define WRITE_WORD(_reg, _val) {(*((volatile u16*)(_reg))) = (u16)(_val); }
#define WRITE_LONG(_reg, _val) {(*((volatile u32*)(_reg))) = (u32)(_val); }
#define WRITE_WORD_MASK(_reg, _val, _mask) {(*((volatile u16*)(_reg))) = ((*((volatile u16*)(_reg))) & ~(_mask)) | ((u16)(_val) & (_mask)); }
#define READ_CPLD_DATA_IN() ((MHal_RIU_REG(PAD_Z80IO_IN_DATA_7_ADDR) & 0x1) << 7 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_6_ADDR) & 0x1) << 6 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_5_ADDR) & 0x1) << 5 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_4_ADDR) & 0x1) << 4 |\
(MHal_RIU_REG(PAD_Z80IO_IN_DATA_3_ADDR) & 0x1) << 3 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_2_ADDR) & 0x1) << 2 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_1_ADDR) & 0x1) << 1 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_0_ADDR) & 0x1))
#define SET_CPLD_READ_DATA() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define SET_CPLD_READ_STATUS() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define SET_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define CLEAR_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define CPLD_READY() (MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1)
#define CPLD_RESET() (MHal_RIU_REG(PAD_Z80IO_RESET_ADDR) & 0x1)
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte
//#define MSPI_WRITE(_reg_, _val_) {pr_info("PDS: MSPI_WRITE(0x%x, 0x%x, 0x%x)\n", _reg_, _val_, gMspBaseAddr + ((_reg_)<<2)); WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_)); }
#define MSPI_WRITE(_reg_, _val_) WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_));
//write 2 byte mask
//#define MSPI_WRITE_MASK(_reg_, _val_, mask) {pr_info("PDS: WRITE_LONG(0x%x, 0x%x, mask=0x%x)\n", _reg_, _val_, mask); WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask)); }
#define MSPI_WRITE_MASK(_reg_, _val_, mask) WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask));
#define CLK_READ(_reg_) READ_WORD(gClkBaseAddr + ((_reg_)<<2))
//#define CLK_WRITE(_reg_, _val_) {pr_info("PDS: CLK_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_)); }
#define CLK_WRITE(_reg_, _val_) WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_));
#define CHIPTOP_READ(_reg_) READ_WORD(gChipBaseAddr + ((_reg_)<<2))
//#define CHIPTOP_WRITE(_reg_, _val_) {pr_info("PDS: CHIPTOP_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_)); }
#define CHIPTOP_WRITE(_reg_, _val_) WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_));
#define MOVDMA_READ(_reg_) READ_WORD(gMOVDMAAddr + ((_reg_)<<2))
//#define MOVDMA_WRITE(_reg_, _val_) {pr_info("PDS: MOVDMA_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_)); }
#define MOVDMA_WRITE(_reg_, _val_) WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_));
#define _HAL_MSPI_ClearDone() MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET,MSPI_CLEAR_DONE)
#define MAX_CHECK_CNT 2000
#define MSPI_READ_INDEX 0x0
#define MSPI_WRITE_INDEX 0x1
#define SPI_MIU0_BUS_BASE 0x20000000
#define SPI_MIU1_BUS_BASE 0xFFFFFFFF
// Function definitions.
//
int z80io_init(void);
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData);
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData);
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData);
#ifdef NOTNEEDED
uint8_t z80io_PRL_Send8(uint8_t txData);
uint8_t z680io_PRL_Send16(uint16_t txData);
#endif
uint8_t z80io_PRL_Read8(uint8_t dataFlag);
uint16_t z80io_PRL_Read16(void);
uint8_t z80io_SPI_Test(void);
uint8_t z80io_PRL_Test(void);
uint8_t z80io_Z80_TestMemory(void);
extern void MHal_GPIO_Init(void);
extern void MHal_GPIO_Pad_Set(uint8_t u8IndexGPIO);
extern int MHal_GPIO_PadGroupMode_Set(uint32_t u32PadMode);
extern int MHal_GPIO_PadVal_Set(uint8_t u8IndexGPIO, uint32_t u32PadMode);
extern void MHal_GPIO_Pad_Oen(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pad_Odn(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_Level(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_InOut(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_Low(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_Low(uint8_t u8IndexGPIO);
extern void MHal_Enable_GPIO_INT(uint8_t u8IndexGPIO);
extern int MHal_GPIO_To_Irq(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_POLARITY(uint8_t u8IndexGPIO, uint8_t reverse);
extern void MHal_GPIO_Set_Driving(uint8_t u8IndexGPIO, uint8_t setHigh);
extern void MHal_GPIO_PAD_32K_OUT(uint8_t u8Enable);
#ifdef __cplusplus
}
#endif
#endif // Z80IO_H

View File

@@ -1,541 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io_test.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface Test Methods
// This file contains the methods used to test the SOM to CPLD interface and evaluate
// it's performance. Production builds wont include these methods.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/sched.h>
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
uint8_t z80io_Z80_TestMemory(void)
{
// Locals.
//
uint32_t addr;
uint32_t fullCmd;
uint8_t cmd;
struct timeval start, stop;
uint32_t iterations = 100;
uint32_t errorCount;
uint32_t idx;
long totalTime;
long bytesMSec;
uint8_t result;
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E80000 | (0x82 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E20000 | (0x58 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E00000 | (0xF7 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E90000 | (0x0F << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xCF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xFF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
pr_info("Z80 Host Test - IO.\n");
// for(idx=0; idx < 1000000; idx++)
// {
// SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
// SPI_SEND32(0xD0000000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0100000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0200000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0300000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0400000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0500000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// }
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000| ((uint8_t)addr) << 8 | CPLD_CMD_WRITEIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Read performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible IO ports and write to it.
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000 | ((uint8_t)addr) << 8 | CPLD_CMD_READIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing RAM Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write performance (opt).\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Fetch performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Read performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Fetch performance.\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x10;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Read performance (opt).\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x20;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
// Go through all the accessible attribute VRAM and initialise it.
pr_info("Z80 Host Test - Testing VRAM Write performance.\n");
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
iterations = 256*10;
do_gettimeofday(&start);
for(addr=0xD800; addr < 0xE000; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD800)
{
fullCmd = (addr << 16) |(0x71 << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible VRAM and write to it.
for(addr=0xD000; addr < 0xD800; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD000)
{
fullCmd = (addr << 16) | ((uint8_t)idx << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)((1*iterations*0x800)+0x800)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
return(0);
}
// A simple test to verify the SOM to CPLD SPI connectivity and give an estimate of its performance.
// The performance is based on the SPI setup and transmit time along with the close and received data processing.
// In real use, the driver will just send a command and generally ignore received data so increased throughput can be achieved.
//
uint8_t z80io_SPI_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
uint16_t rxData16Last;
uint32_t rxData32;
uint32_t rxData32Last;
uint32_t errorCount;
long totalTime;
long bytesMSec;
// Place the CPLD into echo test mode.
z80io_SPI_Send8(0xfe, &rxData8);
// 1st. test, 8bit.
pr_info("SPI Test - Testing 8 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send8((uint8_t)idx, &rxData8);
if(idx > 1 && (uint8_t)(idx-1) != rxData8)
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint8_t)(idx-1), rxData8 );
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 16bit.
pr_info("SPI Test - Testing 16 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_SPI_Send16((uint16_t)idx, &rxData16);
if(idx > 0 && (uint16_t)(idx-1) != (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint16_t)(idx-1), (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)));
errorCount++;
}
rxData16Last = rxData16;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 3rd. test, 32bit.
pr_info("SPI Test - Testing 32 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send32((uint32_t)idx, &rxData32);
if(idx > 0 && (uint32_t)(idx-1) != (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint32_t)(idx-1), (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)));
errorCount++;
}
rxData32Last = rxData32;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(4*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}
// Method to test the parallel bus, verifying integrity and assessing performance.
uint8_t z80io_PRL_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
long totalTime;
long bytesMSec;
#ifdef NOTNEEDED
uint32_t errorCount;
#endif
// Place the CPLD into echo test mode.
// 1st. test, 8bit RW.
#ifdef NOTNEEDED
pr_info("Parallel Test - Testing 8 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte and readback to compare.
z80io_PRL_Send8((uint8_t)idx);
rxData8 = z80io_PRL_Read8();
if((uint8_t)idx != rxData8)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint8_t)(idx), rxData8);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 8bit Write.
pr_info("Parallel Test - Testing 8 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte.
z80io_PRL_Send8((uint8_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 3rd. test, 8bit Read.
pr_info("Parallel Test - Testing 8 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read byte.
rxData8 = z80io_PRL_Read8(0);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#ifdef NOTNEEDED
// 4th test, 16bit.
pr_info("Parallel Test - Testing 16 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_PRL_Send16((uint16_t)idx);
rxData16 = z80io_PRL_Read16();
if((uint16_t)idx != rxData16)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint16_t)(idx), rxData16);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 5th test, 16bit Write.
pr_info("Parallel Test - Testing 16 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write word.
z80io_PRL_Send16((uint16_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 6th test, 16bit Read.
pr_info("Parallel Test - Testing 16 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read word.
rxData16 = z80io_PRL_Read16();
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}

View File

@@ -1,403 +0,0 @@
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname && longopts[i].shortname < 127) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */

View File

@@ -1,734 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80ctrl.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Control Interface
// This file contains a command line utility tool for controlling the z80drv device
// driver. The tool allows manipulation of the emulated Z80, inspection of its
// memory and data, transmission of adhoc commands to the underlying CPLD-Z80
// gateway and loading/saving of programs and data to/from the Z80 virtual and
// host memory.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of the Z80 device driver.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <Z/constants/pointer.h>
#include <Z/macros/member.h>
#include <Z/macros/array.h>
#include <Z80.h>
#include "z80driver.h"
#define VERSION "1.0"
#define AUTHOR "P.D.Smart"
#define COPYRIGHT "(c) 2018-22"
// Getopt_long is buggy so we use optparse.
#define OPTPARSE_IMPLEMENTATION
#define OPTPARSE_API static
#include "optparse.h"
// Device driver name.
#define DEVICE_FILENAME "/dev/z80drv"
// Constants for the Sharp MZ80A MZF file format.
#define MZF_HEADER_SIZE 128 // Size of the MZF header.
#define MZF_ATTRIBUTE 0x00 // Code Type, 01 = Machine Code.
#define MZF_FILENAME 0x01 // Title/Name (17 bytes).
#define MZF_FILENAME_LEN 17 // Length of the filename, it is not NULL terminated, generally a CR can be taken as terminator but not guaranteed.
#define MZF_FILESIZE 0x12 // Size of program.
#define MZF_LOADADDR 0x14 // Load address of program.
#define MZF_EXECADDR 0x16 // Exec address of program.
#define MZF_COMMENT 0x18 // Comment, used for details of the file or startup code.
#define MZF_COMMENT_LEN 104 // Length of the comment field.
#define CMT_TYPE_OBJCD 0x001 // MZF contains a binary object.
#define CMT_TYPE_BTX1CD 0x002 // MZF contains a BASIC program.
#define CMT_TYPE_BTX2CD 0x005 // MZF contains a BASIC program.
#define CMT_TYPE_TZOBJCD0 0x0F8 // MZF contains a TZFS binary object for page 0.
#define CMT_TYPE_TZOBJCD1 0x0F9
#define CMT_TYPE_TZOBJCD2 0x0FA
#define CMT_TYPE_TZOBJCD3 0x0FB
#define CMT_TYPE_TZOBJCD4 0x0FC
#define CMT_TYPE_TZOBJCD5 0x0FD
#define CMT_TYPE_TZOBJCD6 0x0FE
#define CMT_TYPE_TZOBJCD7 0x0FF // MZF contains a TZFS binary object for page 7.
#define MZ_CMT_ADDR 0x10F0
// Structure to define a Sharp MZ80A MZF directory structure. This header appears at the beginning of every Sharp MZ80A tape (and more recently archived/emulator) images.
//
typedef struct __attribute__((__packed__)) {
uint8_t attr; // MZF attribute describing the file.
uint8_t fileName[MZF_FILENAME_LEN]; // Each directory entry is the size of an MZF filename.
uint16_t fileSize; // Size of file.
uint16_t loadAddr; // Load address for the file.
uint16_t execAddr; // Execution address where the Z80 starts processing.
uint8_t comment[MZF_COMMENT_LEN]; // Text comment field but often contains a startup machine code program.
} t_svcDirEnt;
// Possible commands to be issued to the Z80 driver.
enum CTRL_COMMANDS {
Z80_CMD_STOP = 0,
Z80_CMD_START = 1,
Z80_CMD_PAUSE = 2,
Z80_CMD_CONTINUE = 3,
Z80_CMD_RESET = 4,
Z80_CMD_SPEED = 5,
Z80_CMD_HOST_RAM = 6,
Z80_CMD_VIRTUAL_RAM = 7,
Z80_CMD_DUMP_MEMORY = 8,
Z80_CMD_MEMORY_TEST = 9,
CPLD_CMD_SEND_CMD = 10,
CPLD_CMD_SPI_TEST = 11,
CPLD_CMD_PRL_TEST = 12
};
// Shared memory between this process and the Z80 driver.
static t_Z80Ctrl *Z80Ctrl = NULL;
// Method to obtain and return the output screen width.
//
uint8_t getScreenWidth(void)
{
return(MAX_SCREEN_WIDTH);
}
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch(uint8_t wait)
{
int r;
unsigned char c;
if(wait != 0 || (wait == 0 && kbhit()))
{
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
return 0;
}
void delay(int number_of_seconds)
{
// Converting time into milli_seconds
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
}
// Function to dump out a given section of memory via the UART.
//
int memoryDump(uint32_t memaddr, uint32_t memsize, uint8_t memoryFlag, uint32_t memwidth, uint32_t dispaddr, uint8_t dispwidth)
{
uint8_t displayWidth = dispwidth;;
uint32_t pnt = memaddr;
uint32_t endAddr = memaddr + memsize;
uint32_t addr = dispaddr;
uint32_t i = 0;
//uint32_t data;
int8_t keyIn;
int result = -1;
char c = 0;
// Sanity check. memoryFlag == 0 required kernel driver to dump so we exit as it cannot be performed here.
if(memoryFlag == 0)
return(-1);
// Reconfigure terminal to allow non-blocking key input.
//
set_conio_terminal_mode();
// If not set, calculate output line width according to connected display width.
//
if(displayWidth == 0)
{
switch(getScreenWidth())
{
case 40:
displayWidth = 8;
break;
case 80:
displayWidth = 16;
break;
default:
displayWidth = 32;
break;
}
}
while (1)
{
printf("%08lX", addr); // print address
printf(": ");
// print hexadecimal data
for (i=0; i < displayWidth; )
{
switch(memwidth)
{
case 16:
if(pnt+i < endAddr)
printf("%04X", memoryFlag == 1 ? (uint16_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint16_t)Z80Ctrl->page[pnt+i] : (uint16_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 32:
if(pnt+i < endAddr)
printf("%08lX", memoryFlag == 1 ? (uint32_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint32_t)Z80Ctrl->page[pnt+i] : (uint32_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
case 8:
default:
if(pnt+i < endAddr)
printf("%02X", memoryFlag == 1 ? (uint8_t)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (uint8_t)Z80Ctrl->page[pnt+i] : (uint8_t)Z80Ctrl->iopage[pnt+i]);
else
printf(" ");
i++;
break;
}
fputc((char)' ', stdout);
}
// print ascii data
printf(" |");
// print single ascii char
for (i=0; i < displayWidth; i++)
{
c = memoryFlag == 1 ? (char)Z80Ctrl->memory[pnt+i] : memoryFlag == 2 ? (char)Z80Ctrl->page[pnt+i] : (char)Z80Ctrl->iopage[pnt+i];
if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~'))
fputc((char)c, stdout);
else
fputc((char)' ', stdout);
}
printf("|\r\n");
fflush(stdout);
// Move on one row.
pnt += displayWidth;
addr += displayWidth;
// User abort (ESC), pause (Space) or all done?
//
keyIn = getch(0);
if(keyIn == ' ')
{
do {
keyIn = getch(0);
} while(keyIn != ' ' && keyIn != 0x1b);
}
// Escape key pressed, exit with 0 to indicate this to caller.
if (keyIn == 0x1b)
{
sleep(1);
result = 0;
goto memoryDumpExit;
}
// End of buffer, exit the loop.
if(pnt >= (memaddr + memsize))
{
break;
}
}
// Normal exit, return -1 to show no key pressed.
memoryDumpExit:
reset_terminal_mode();
return(result);
}
// Method to load a program or data file into the Z80 memory. First load into Virtual memory and then trigger a sync to bring Host RAM in line.
//
int z80load(int fdZ80, char *fileName)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
t_svcDirEnt mzfHeader;
// Pause the Z80.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Open the file and read directly into the Virtual memory via the share.
FILE *ptr;
ptr = fopen(fileName, "rb");
if(ptr)
{
printf("File:%s\n", fileName);
// First the header.
fread((uint8_t *)&mzfHeader, MZF_HEADER_SIZE, 1, ptr);
printf("Load:%x\n", mzfHeader.loadAddr);
if(mzfHeader.loadAddr > 0x1000)
{
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
// Copy in the header.
memcpy((uint8_t *)&Z80Ctrl->memory[MZ_CMT_ADDR], (uint8_t *)&mzfHeader, MZF_HEADER_SIZE);
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
// Now read in the data.
fread(&Z80Ctrl->memory[mzfHeader.loadAddr], mzfHeader.fileSize, 1, ptr);
printf("Memcpy:%x,%x\n", mzfHeader.loadAddr, mzfHeader.fileSize);
printf("Loaded %s, Size:%04x, Addr:%04x, Exec:%04x\n", fileName, mzfHeader.fileSize, mzfHeader.loadAddr, mzfHeader.execAddr);
}
// Sync the loaded image from Virtual memory to hard memory.
ioctlCmd.cmd = IOCTL_CMD_SYNC_TO_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Resume Z80 processing.
//
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
else
printf("Couldnt open file\n");
return ret;
}
// Method to request basic Z80 operations.
//
int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long param3)
{
// Locals.
struct ioctlCmd ioctlCmd;
uint32_t idx;
int ret = 0;
switch(cmd)
{
case Z80_CMD_STOP:
// Use IOCTL to request Z80 to Stop (power off) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_START:
// Use IOCTL to request Z80 to Start (power on) processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_PAUSE:
// Use IOCTL to request Z80 to pause processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_PAUSE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_CONTINUE:
// Use IOCTL to request Z80 continue processing.
ioctlCmd.cmd = IOCTL_CMD_Z80_CONTINUE;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_RESET:
// Use IOCTL to request Z80 reset.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_SPEED:
// Check value is in range.
for(idx=1; idx < 256; idx+=idx)
{
if((uint32_t)param1 == idx) break;
}
if(idx == 256)
{
printf("Speed factor is illegal. It must be a multiple value of the original CPU clock, ie. 1x, 2x, 4x etc\n");
ret = -1;
} else
{
// Use IOCTL to request Z80 cpu freq change.
ioctlCmd.speed.speedMultiplier = (uint32_t)param1;
ioctlCmd.cmd = IOCTL_CMD_Z80_CPU_FREQ;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case CPLD_CMD_SEND_CMD:
// Build up the IOCTL command to request the given data is sent to the CPLD.
ioctlCmd.cmd = IOCTL_CMD_CPLD_CMD;
ioctlCmd.cpld.cmd = (uint32_t)param1;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_DUMP_MEMORY:
// If virtual memory, we can dump it via the shared memory segment.
if((uint8_t)param1)
{
memoryDump((uint32_t)param2, (uint32_t)param3, (uint8_t)param1, (uint8_t)param1 == 2 || (uint8_t)param1 == 3 ? 32 : 8, (uint32_t)param2, 0);
} else
{
// Build an IOCTL command to get the driver to dump the memory.
ioctlCmd.cmd = IOCTL_CMD_DUMP_MEMORY;
ioctlCmd.addr.start = (uint32_t)param2;
ioctlCmd.addr.end = (uint32_t)param2+(uint32_t)param3;
ioctlCmd.addr.size = (uint32_t)param3;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
}
break;
case Z80_CMD_HOST_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_HOST_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_VIRTUAL_RAM:
// Use IOCTL to request change to host RAM.
ioctlCmd.cmd = IOCTL_CMD_USE_VIRTUAL_RAM;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case Z80_CMD_MEMORY_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_Z80_MEMTEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_PRL_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_PRL_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
case CPLD_CMD_SPI_TEST:
// Send command to test the SPI.
ioctlCmd.cmd = IOCTL_CMD_SPI_TEST;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break;
default:
printf("Command not supported!\n");
ret = -1;
break;
}
return ret;
}
// Method to perform some simple tests on the Z80 emulator.
//
int z80test(int fdZ80)
{
// Locals.
struct ioctlCmd ioctlCmd;
int ret = 0;
// Stop the Z80.
//
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
FILE *ptr;
ptr = fopen("/customer/mz700.rom", "rb");
if(ptr)
{
fread(&Z80Ctrl->memory, 65536, 1, ptr);
} else printf("Couldnt open file\n");
// Configure the Z80.
//
printf("Send SETPC\n");
ioctlCmd.z80.pc = 0;
ioctl(fdZ80, IOCTL_CMD_SETPC, &ioctlCmd);
memoryDump(0 , 65536, 1, 8, 0, 0);
// Start the Z80.
//
printf("Send START\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
delay(10);
printf("Send STOP\n");
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP;
ioctl(fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
memoryDump(0, 65536, 1, 8, 0, 0);
out:
return ret;
}
// Output usage screen. So mamy commands you do need to be prompted!!
void showArgs(char *progName, struct optparse *options)
{
printf("%s %s %s %s\n\n", progName, VERSION, COPYRIGHT, AUTHOR);
printf("Synopsis:\n");
printf("%s --help # This help screen.\n", progName);
printf(" --cmd <command> = RESET # Reset the Z80\n");
printf(" = STOP # Stop and power off the Z80\n");
printf(" = START # Power on and start the Z80\n");
printf(" = PAUSE # Pause running Z80\n");
printf(" = CONTINUE # Continue Z80 execution\n");
printf(" = HOSTRAM # Use HOST DRAM\n");
printf(" = VIRTRAM # Use Virtual RAM\n");
printf(" = SPEED --speed <1, 2, 4, 8, 16, 32, 64, 128> # In Virtual RAM mode, set CPU speed to base clock x factor.\n");
printf(" = LOADMZF --file <mzf filename> # Load MZF file into memory.\n");
printf(" = DUMP --start <24bit addr> --end <24bit addr> --virtual <0 - Host RAM, 1 = Virtual RAM, 2 = PageTable, 3 = IOPageTable>\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
printf(" = Z80TEST # Perform various debugging tests\n");
printf(" = SPITEST # Perform SPI testing\n");
printf(" = PRLTEST # Perform Parallel Bus testing\n");
printf(" = Z80MEMTEST # Perform HOST memory tests.\n");
}
int main(int argc, char *argv[])
{
int fdZ80;
char buff[64];
char cmd[64] = { 0 };
char fileName[256] = { 0 };
int opt;
long hexData = 0;
long speedMultiplier = 1;
long startAddr = 0x0000;
long endAddr = 0x1000;
int virtualMemory = 0;
int helpFlag = 0;
int verboseFlag = 0;
// Define parameters to be processed.
struct optparse options;
static struct optparse_long long_options[] =
{
{"help", 'h', OPTPARSE_NONE},
{"cmd", 'c', OPTPARSE_REQUIRED},
{"file", 'f', OPTPARSE_REQUIRED},
{"data", 'd', OPTPARSE_REQUIRED},
{"speed", 'S', OPTPARSE_REQUIRED},
{"virtual", 'V', OPTPARSE_REQUIRED},
{"start", 's', OPTPARSE_REQUIRED},
{"end", 'e', OPTPARSE_REQUIRED},
{"verbose", 'v', OPTPARSE_NONE},
{0}
};
// Parse the command line options.
//
optparse_init(&options, argv);
while((opt = optparse_long(&options, long_options, NULL)) != -1)
{
switch(opt)
{
// Hex data.
case 'd':
hexData = strtol(options.optarg, NULL, 0);
//printf("Hex data:%08x\n", hexData);
break;
// Start address for memory operations.
case 's':
startAddr = strtol(options.optarg, NULL, 0);
//printf("Start Addr:%04x\n", startAddr);
break;
// Speed multiplication factor for CPU governor when running in virtual memory.
case 'S':
speedMultiplier = strtol(options.optarg, NULL, 0);
//printf("Speed = base freq x %d\n", speedFactor);
break;
// End address for memory operations.
case 'e':
endAddr = strtol(options.optarg, NULL, 0);
//printf("End Addr:%04x\n", endAddr);
break;
// Virtual memory flag, 0 = host, 1 = virtual memory, 2 = page table, 3 = iopage table.
case 'V':
virtualMemory = atoi(options.optarg);
break;
// Filename.
case 'f':
strcpy(fileName, options.optarg);
break;
// Command to execute.
case 'c':
strcpy(cmd, options.optarg);
break;
// Verbose mode.
case 'v':
verboseFlag = 1;
break;
// Command help needed.
case 'h':
helpFlag = 1;
showArgs(argv[0], &options);
break;
// Unrecognised, show synopsis.
case '?':
showArgs(argv[0], &options);
printf("%s: %s\n", argv[0], options.errmsg);
return(1);
}
}
// Open the z80drv driver and attach to its shared memory, basically the Z80 control structure which includes the virtual Z80 memory.
fdZ80 = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);
if(fdZ80 >= 0)
{
Z80Ctrl = (t_Z80Ctrl *)mmap(0, sizeof(t_Z80Ctrl), PROT_READ | PROT_WRITE, MAP_SHARED, fdZ80, 0);
if(Z80Ctrl == (void *)-1)
{
printf("Failed to attach to the Z80 Control structure, cannot continue, exitting....\n");
close(fdZ80);
exit(1);
}
} else
{
printf("Failed to open the Z80 Driver, exitting...\n");
exit(1);
}
// Basic string to method mapping. Started off with just 1 or two but has grown, may need a table!
if(strcasecmp(cmd, "LOADMZF") == 0)
{
z80load(fdZ80, fileName);
} else
if(strcasecmp(cmd, "RESET") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_RESET, 0, 0, 0);
} else
if(strcasecmp(cmd, "STOP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_STOP, 0, 0, 0);
} else
if(strcasecmp(cmd, "START") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_START, 0, 0, 0);
} else
if(strcasecmp(cmd, "PAUSE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_PAUSE, 0, 0, 0);
} else
if(strcasecmp(cmd, "CONTINUE") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_CONTINUE, 0, 0, 0);
} else
if(strcasecmp(cmd, "SPEED") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_SPEED, speedMultiplier, 0, 0);
} else
if(strcasecmp(cmd, "DUMP") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_DUMP_MEMORY, virtualMemory, startAddr, (endAddr - startAddr));
} else
if(strcasecmp(cmd, "HOSTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_HOST_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "VIRTRAM") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_VIRTUAL_RAM, 0, 0, 0);
} else
if(strcasecmp(cmd, "CPLDCMD") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SEND_CMD, hexData, 0, 0);
} else
// Test methods, if the code is built-in to the driver.
if(strcasecmp(cmd, "Z80TEST") == 0)
{
z80test(fdZ80);
} else
if(strcasecmp(cmd, "SPITEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_SPI_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "PRLTEST") == 0)
{
ctrlCmd(fdZ80, CPLD_CMD_PRL_TEST, 0, 0, 0);
} else
if(strcasecmp(cmd, "Z80MEMTEST") == 0)
{
ctrlCmd(fdZ80, Z80_CMD_MEMORY_TEST, 0, 0, 0);
}
else
{
showArgs(argv[0], &options);
printf("No command given, nothing done!\n");
}
// Unmap shared memory and close the device.
munmap(Z80Ctrl, sizeof(t_Z80Ctrl));
close(fdZ80);
return(0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,284 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80driver.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Driver
// This file contains the declarations used in the z80drv device driver.
//
// Credits: Zilog Z80 CPU Emulator v0.2 written by Manuel Sainz de Baranda y Goñi
// The Z80 CPU Emulator is the heart of this driver and in all ways, is compatible with
// the original Z80.
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
// (c) 1999-2022 Manuel Sainz de Baranda y Goñi
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80DRIVER_H
#define Z80DRIVER_H
// Constants.
#define Z80_VIRTUAL_ROM_SIZE 16384 // Sized to maximum ROM which is the MZ-800 ROM.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 8) // (PAGE_SIZE * 2) // max size mmaped to userspace
#define Z80_VIRTUAL_MEMORY_SIZE Z80_VIRTUAL_RAM_SIZE + Z80_VIRTUAL_ROM_SIZE
#define Z80_MEMORY_PAGE_SIZE 16
#define MAX_SCREEN_WIDTH 132
#define DEVICE_NAME "z80drv"
#define CLASS_NAME "mogu"
// Memory and IO page types. Used to create a memory page which maps type of address space to real address space on host or virtual memory.
#define MEMORY_TYPE_VIRTUAL_MASK 0x00FFFFFF
#define MEMORY_TYPE_REAL_MASK 0x0000FFFF
#define IO_TYPE_MASK 0x0000FFFF
#define MEMORY_TYPE_INHIBIT 0x00000000
#define MEMORY_TYPE_PHYSICAL_RAM 0x80000000
#define MEMORY_TYPE_PHYSICAL_ROM 0x40000000
#define MEMORY_TYPE_PHYSICAL_VRAM 0x20000000
#define MEMORY_TYPE_PHYSICAL_HW 0x10000000
#define MEMORY_TYPE_VIRTUAL_RAM 0x08000000
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_HW 0x02000000
#define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000
// Approximate governor delays to regulate emulated CPU speed.
#define MZ700_INSTRUCTION_DELAY_3_54MHZ 253
#define MZ700_INSTRUCTION_DELAY_7MHZ 126
#define MZ700_INSTRUCTION_DELAY_14MHZ 63
#define MZ700_INSTRUCTION_DELAY_28MHZ 32
#define MZ700_INSTRUCTION_DELAY_56MHZ 16
#define MZ700_INSTRUCTION_DELAY_112MHZ 8
#define MZ700_INSTRUCTION_DELAY_224MHZ 4
#define MZ700_INSTRUCTION_DELAY_448MHZ 1
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S'
#define IOCTL_CMD_Z80_PAUSE 'P'
#define IOCTL_CMD_Z80_RESET 'R'
#define IOCTL_CMD_Z80_CONTINUE 'C'
#define IOCTL_CMD_USE_HOST_RAM 'x'
#define IOCTL_CMD_USE_VIRTUAL_RAM 'X'
#define IOCTL_CMD_DUMP_MEMORY 'M'
#define IOCTL_CMD_Z80_CPU_FREQ 'F'
#define IOCTL_CMD_CPLD_CMD 'z'
#define IOCTL_CMD_SEND _IOW('c', 'c', int32_t *)
#define IOCTL_CMD_SETPC _IOW('p', 'p', int32_t *)
#define IOCTL_CMD_SYNC_TO_HOST_RAM 'V'
#define IOCTL_CMD_SPI_TEST '1'
#define IOCTL_CMD_PRL_TEST '2'
#define IOCTL_CMD_Z80_MEMTEST '3'
// Chip Select map MZ80K-MZ700.
//
// 0000 - 0FFF = CS_ROMni : R/W : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF = CS_RAMni : R/W : MZ80K/A/700 = RAM
// C000 - CFFF = CS_ROMni : R/W : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF = CS_VRAMni : R/W : MZ80K/A/700 = VRAM
// D800 - DFFF = CS_VRAMni : R/W : MZ700 = Colour VRAM (MZ700)
// E000 - E003 = CS_8255n : R/W : MZ80K/A/700 = 8255
// E004 - E007 = CS_8254n : R/W : MZ80K/A/700 = 8254
// E008 - E00B = CS_LS367n : R/W : MZ80K/A/700 = LS367
// E00C - E00F = CS_ESWPn : R : MZ80A = Memory Swap (MZ80A)
// E010 - E013 = CS_ESWPn : R : MZ80A = Reset Memory Swap (MZ80A)
// E014 = CS_E5n : R/W : MZ80A/700 = Normal CRT display (in Video Controller)
// E015 = CS_E6n : R/W : MZ80A/700 = Reverse CRT display (in Video Controller)
// E200 - E2FF = : R/W : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF = : R/W : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF = : R/W : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF = : R/W : MZ80K/A/700 = Floppy Disk interface.
//
// Chip Select map MZ800
//
// FC - FF = CS_PIOn : R/W : MZ800/MZ1500 = Z80 PIO Printer Interface
// F2 = CS_PSG0n : W : MZ800/MZ1500 = Programable Sound Generator, MZ-800 = Mono, MZ-1500 = Left Channel
// F3 = CS_PSG1n : W : MZ1500 = Programable Sound Generator, MZ-1500 = Right Channel
// E9 = CS_PSG(X)n: W : MZ1500 = Simultaneous write to both PSG's.
// F0 - F1 = CS_JOYSTK : R : MZ800 = Joystick 1 and 2
// CC = CS_GWF : W : MZ800 = CRTC GWF Write format Register
// CD = CS_GRF : W : MZ800 = CRTC GRF Read format Register
// CE = CS_GDMD : W : MZ800 = CRTC GDMD Mode Register
// CF = CS_GCRTC : W : MZ800 = CRTC GCRTC Control Register
// D4 - D7 = CS
// D000 - DFFF
// MZ700/MZ800 memory mode switch?
//
// MZ-700 MZ-800
// |0000:0FFF|1000:1FFF|1000:CFFF|C000:CFFF|D000:FFFF |0000:7FFF|1000:1FFF|2000:7FFF|8000:BFFF|C000:CFFF|C000:DFFF|E000:FFFF
// -------------------------------------------------- ----------------------------------------------------------------------
// OUT 0xE0 = |DRAM | | | | |DRAM | | | | | |
// OUT 0xE1 = | | | | |DRAM | | | | | | |DRAM
// OUT 0xE2 = |MONITOR | | | | |MONITOR | | | | | |
// OUT 0xE3 = | | | | |Memory Mapped I/O | | | | | | |Upper MONITOR ROM
// OUT 0xE4 = |MONITOR | |DRAM | |Memory Mapped I/O |MONITOR |CGROM |DRAM |VRAM | |DRAM |Upper MONITOR ROM
// OUT 0xE5 = | | | | |Inhibit | | | | | | |Inhibit
// OUT 0xE6 = | | | | |<return> | | | | | | |<return>
// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | |
// IN 0xE1 = | |DRAM | |DRAM | | |<return> | |DRAM | | |
//
// <return> = Return to the state prior to the complimentary command being invoked.
// * = MZ-800 host only.
// Macros to lookup and test to see if a given memory block or IO byte is of a given type. Also macros to read/write to the memory block and IO byte.
#define MEMORY_BLOCK_GRANULARITY 0x800
#define MEMORY_BLOCK_SLOTS (0x10000 / MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_MASK (0x10000 - MEMORY_BLOCK_GRANULARITY)
#define MEMORY_BLOCK_SHIFT 11
#define getPageData(a) (Z80Ctrl->page[(a & 0xF800) >> MEMORY_BLOCK_SHIFT])
#define getIOPageData(a) (Z80Ctrl->iopage[(a & 0xFFFF])
#define getPageType(a, mask) (getPageData(a) & mask)
#define getPageAddr(a, mask) ((getPageData(a) & mask) + (a & (MEMORY_BLOCK_GRANULARITY-1)))
#define getIOPageType(a, mask) (getIOPageData(a) & mask)
#define getIOPageAddr(a, mask) (getIOPageData(a) & mask)
#define realAddress(a) (Z80Ctrl->page[getPageAddr(a, MEMORY_TYPE_REAL_MASK)])
#define realPort(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_MASK)
#define isPhysicalRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_RAM))
#define isPhysicalVRAM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_VRAM))
#define isPhysicalROM(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_ROM))
#define isPhysicalMemory(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM))])
#define isPhysicalHW(a) (getPageType(a, MEMORY_TYPE_PHYSICAL_HW))
#define isPhysical(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_PHYSICAL_ROM | MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_PHYSICAL_VRAM)))
#define isPhysicalIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_PHYSICAL_HW)
#define isVirtualRAM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_RAM))
#define isVirtualROM(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_ROM))
#define isVirtualMemory(a) (getPageType(a, (MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_RAM)))
#define isVirtualHW(a) (getPageType(a, MEMORY_TYPE_VIRTUAL_HW))
#define isVirtualIO(a) (Z80Ctrl->iopage[a & 0xFFFF] & IO_TYPE_VIRTUAL_HW)
#define isHW(a) (getPageType(a, (MEMORY_TYPE_PHYSICAL_HW | MEMORY_TYPE_VIRTUAL_HW)))
#define readVirtualRAM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ])
#define readVirtualROM(a) (Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE ])
#define writeVirtualRAM(a, d) { Z80Ctrl->memory[ getPageAddr(a, MEMORY_TYPE_VIRTUAL_MASK) ] = d; }
#define setMemoryType(_block_,_type_,_addr_) { Z80Ctrl->page[_block_] = _type_ | _addr_; }
#define backupMemoryType(_block_) { Z80Ctrl->shadowPage[_block_] = Z80Ctrl->page[_block_]; }
#define restoreMemoryType(_block_) { Z80Ctrl->page[_block_] = Z80Ctrl->shadowPage[_block_]; }
#define IO_ADDR_E0 0xE0
#define IO_ADDR_E1 0xE1
#define IO_ADDR_E2 0xE2
#define IO_ADDR_E3 0xE3
#define IO_ADDR_E4 0xE4
#define IO_ADDR_E5 0xE5
#define IO_ADDR_E6 0xE6
#define IO_ADDR_E7 0xE7
enum Z80_RUN_STATES {
Z80_STOP = 0x00,
Z80_STOPPED = 0x01,
Z80_PAUSE = 0x02,
Z80_PAUSED = 0x03,
Z80_CONTINUE = 0x04,
Z80_RUNNING = 0x05,
};
enum Z80_MEMORY_PROFILE {
USE_PHYSICAL_RAM = 0x00,
USE_VIRTUAL_RAM = 0x01
};
typedef struct {
// Main memory, linear but indexed as though it were banks in 1K pages.
uint8_t memory[Z80_VIRTUAL_MEMORY_SIZE];
// Page pointer map.
//
// Each pointer points to a byte or block of bytes in the Z80 Memory frame, 64K Real + Banked.
// This is currently set at a block of size 0x800 per memory pointer for the MZ-700.
// The LSB of the pointer is a direct memory index to a byte or block of bytes, the upper byte of the pointer indicates type of memory space.
// 0x80<FFFFFF> - physical host RAM
// 0x40<FFFFFF> - physical host ROM
// 0x20<FFFFFF> - physical host VRAM
// 0x10<FFFFFF> - physical host hardware
// 0x08<FFFFFF> - virtual host RAM
// 0x04<FFFFFF> - virtual host ROM
// 0x02<FFFFFF> - virtual host hardware
// 16bit Input Address -> map -> Pointer to 24bit memory address + type flag.
// -> Pointer+<low bits of address> to 24bit memory address + type flag.
uint32_t page[MEMORY_BLOCK_SLOTS];
uint32_t shadowPage[MEMORY_BLOCK_SLOTS];
// I/O Page map.
//
// This is a map to indicate the use of the I/O page and allow any required remapping.
// <0x80>FF<I/O Address> - physical host hardware
// <0x40>FF<I/O Address> - virtual host hardware
// 16bit Input Address -> map -> Actual 16bit address to use + type flag.
uint32_t iopage[65536];
// Default page mode configured. This value reflects the default page and iotable map.
uint8_t defaultPageMode;
// Refresh DRAM mode. 1 = Refresh, 0 = No refresh. Only applicable when running code in virtual Kernel RAM.
uint8_t refreshDRAM;
// Inhibit mode is where certain memory ranges are inhibitted. The memory page is set to inhibit and this flag
// blocks actions which arent allowed during inhibit.
uint8_t inhibitMode;
// Address caching. Used to minimise instruction length sent to CPLD.
uint16_t z80PrevAddr;
uint16_t z80PrevPort;
// Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe;
uint8_t keyportShiftCtrl;
uint8_t keyportHotKey;
// Governor is the delay in a 32bit loop per Z80 opcode, used to govern execution speed when using virtual memory.
// This mechanism will eventually be tied into the M/T-state calculation for a more precise delay, but at the moment,
// with the Z80 assigned to an isolated CPU, it allows time sensitive tasks such as the tape recorder to work.
// The lower the value the faster the CPU speed.
uint32_t cpuGovernorDelay;
} t_Z80Ctrl;
// IOCTL structure for passing data from user space to driver to perform commands.
//
struct z80_addr {
uint32_t start;
uint32_t end;
uint32_t size;
};
struct z80_ctrl {
uint16_t pc;
};
struct speed {
uint32_t speedMultiplier;
};
struct cpld_ctrl {
uint32_t cmd;
};
struct ioctlCmd {
int32_t cmd;
union {
struct z80_addr addr;
struct z80_ctrl z80;
struct speed speed;
struct cpld_ctrl cpld;
};
};
// Prototypes.
void setupMemory(enum Z80_MEMORY_PROFILE mode);
#endif

View File

@@ -1,428 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the methods used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
//-------------------------------------------------------------------------------------------------------------------------------
//
// User space driver access.
//
//-------------------------------------------------------------------------------------------------------------------------------
// Initialise the SOM hardware used to communicate with the z80 socket and host hardware.
// The SOM interfaces to a CPLD which provides voltage level translation and also encapsulates the Z80 timing cycles as recreating
// them within the SOM is much more tricky.
//
// As this is an embedded device and performance/latency are priorities, minimal structured code is used to keep call stack and
// generated code to a mimimum without relying on the optimiser.
int z80io_init(void)
{
// Locals.
int ret = 0;
// Initialise GPIO. We call the HAL api to minimise time but for actual bit set/reset and read we go directly to registers to save time, increase throughput and minimise latency.
// Initialise the HAL.
MHal_GPIO_Init();
// Set the pads as GPIO devices. The HAL takes care of allocating and deallocating the padmux resources.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_0); // Word (16bit) bidirectional bus. Default is read with data set.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_HIGH_BYTE);
//MHal_GPIO_Pad_Set(PAD_GPIO8); // SPIO 4wire control lines setup by the spidev driver but controlled directly in this driver.
//MHal_GPIO_Pad_Set(PAD_GPIO9);
//MHal_GPIO_Pad_Set(PAD_GPIO10);
//MHal_GPIO_Pad_Set(PAD_GPIO11);
MHal_GPIO_Pad_Set(PAD_Z80IO_READY);
MHal_GPIO_Pad_Set(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Set(PAD_Z80IO_INT);
MHal_GPIO_Pad_Set(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Set(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Set(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Set(PAD_Z80IO_RSV1);
#ifdef NOTNEEDED
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_WRITE);
#endif
// Set required input pads.
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_0);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Odn(PAD_Z80IO_READY);
MHal_GPIO_Pad_Odn(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Odn(PAD_Z80IO_INT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Odn(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RSV1);
// Set required output pads.
#ifdef NOTNEEDED
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Oen(PAD_Z80IO_WRITE);
MHal_GPIO_Pull_High(PAD_Z80IO_WRITE);
#endif
// Control signals.
MHal_GPIO_Pad_Oen(PAD_Z80IO_HIGH_BYTE);
MHal_GPIO_Pull_High(PAD_Z80IO_HIGH_BYTE);
// Setup the MSPI0 device.
//
// Setup control, interrupts are not used.
MSPI_WRITE(MSPI_CTRL_OFFSET, MSPI_CPU_CLOCK_1_2 | MSPI_CTRL_CPOL_LOW | MSPI_CTRL_CPHA_HIGH | MSPI_CTRL_RESET | MSPI_CTRL_ENABLE_SPI);
// Setup LSB First mode.
MSPI_WRITE(MSPI_LSB_FIRST_OFFSET, 0x0);
// Setup clock.
CLK_WRITE(MSPI0_CLK_CFG, 0x1100)
// Setup the frame size (all buffers to 8bits).
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET+1, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET+1, 0xfff);
// Setup Chip Selects to inactive.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Switch Video and Audio to host.
z80io_SPI_Send16(0x00f0, NULL);
return ret;
}
//--------------------------------------------------------
// Parallel bus Methods.
//--------------------------------------------------------
// Methods to read data from the parallel bus.
// The CPLD returns status and Z80 data on the 8bit bus as it is marginally quicker than retrieving it over the SPI bus.
//
inline uint8_t z80io_PRL_Read8(uint8_t dataFlag)
{
// Locals.
uint8_t result = 0;
// Byte according to flag.
if(dataFlag)
SET_CPLD_READ_DATA()
else
SET_CPLD_READ_STATUS()
// Read the input registers and set value accordingly.
result = READ_CPLD_DATA_IN();
// Return 16bit value read from CPLD.
return(result);
}
inline uint16_t z80io_PRL_Read16(void)
{
// Locals.
uint16_t result = 0;
// Low byte first.
CLEAR_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result = (uint16_t)READ_CPLD_DATA_IN();
// High byte next.
SET_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result |= (uint16_t)(READ_CPLD_DATA_IN() << 8);
// Return 16bit value read from CPLD.
return(result);
}
// Parallel Bus methods were tried and tested but due to the GPIO bits being controlled by individual registers per bit, the setup time was longer
// than the transmission time of SPI. These methods are thus deprecated and a fusion of SPI and 8bit parallel is now used.
#ifdef NOTNEEDED
inline uint8_t z80io_PRL_Send8(uint8_t txData)
{
// Locals.
//
// Low byte only.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
inline uint8_t z80io_PRL_Send16(uint16_t txData)
{
// Locals.
//
// Low byte first.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
// High byte next.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
// Setup high byte.
if(txData & 0x8000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x4000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x2000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x1000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0800) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0400) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0200) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0100) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
#endif
//--------------------------------------------------------
// SPI Methods.
//--------------------------------------------------------
// Methods to send 8,16 or 32 bits. Each method is seperate to minimise logic and execution time, 8bit being most sensitive.
// Macros have also been defined for inline inclusion which dont read back the response data.
//
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint8_t)MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)(txData >> 16));
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint32_t)(MSPI_READ(MSPI_FULL_DEPLUX_RD00) | (MSPI_READ(MSPI_FULL_DEPLUX_RD02) << 16));
// Done.
return(timeout == 0);
}
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)
{
pr_info("Z80 Test Memory functionality not built-in.\n");
return(0);
}
uint8_t z80io_SPI_Test(void)
{
pr_info("SPI Test functionality not built-in.\n");
return(0);
}
uint8_t z80io_PRL_Test(void)
{
pr_info("Parallel Bus Test functionality not built-in.\n");
return(0);
}
#endif

View File

@@ -1,483 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the declarations used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80IO_H
#define Z80IO_H
#ifdef __cplusplus
extern "C" {
#endif
// Definitions to control compilation.
#define INCLUDE_TEST_METHODS 1
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
#define CPLD_CMD_FETCH_ADDR_P1 0x11
#define CPLD_CMD_FETCH_ADDR_P2 0x12
#define CPLD_CMD_FETCH_ADDR_P3 0x13
#define CPLD_CMD_FETCH_ADDR_P4 0x14
#define CPLD_CMD_FETCH_ADDR_P5 0x15
#define CPLD_CMD_FETCH_ADDR_P6 0x16
#define CPLD_CMD_FETCH_ADDR_P7 0x17
#define CPLD_CMD_WRITE_ADDR 0x18
#define CPLD_CMD_WRITE_ADDR_P1 0x19
#define CPLD_CMD_WRITE_ADDR_P2 0x1A
#define CPLD_CMD_WRITE_ADDR_P3 0x1B
#define CPLD_CMD_WRITE_ADDR_P4 0x1C
#define CPLD_CMD_WRITE_ADDR_P5 0x1D
#define CPLD_CMD_WRITE_ADDR_P6 0x1E
#define CPLD_CMD_WRITE_ADDR_P7 0x1F
#define CPLD_CMD_READ_ADDR 0x20
#define CPLD_CMD_READ_ADDR_P1 0x21
#define CPLD_CMD_READ_ADDR_P2 0x22
#define CPLD_CMD_READ_ADDR_P3 0x23
#define CPLD_CMD_READ_ADDR_P4 0x24
#define CPLD_CMD_READ_ADDR_P5 0x25
#define CPLD_CMD_READ_ADDR_P6 0x26
#define CPLD_CMD_READ_ADDR_P7 0x27
#define CPLD_CMD_WRITEIO_ADDR 0x28
#define CPLD_CMD_WRITEIO_ADDR_P1 0x29
#define CPLD_CMD_WRITEIO_ADDR_P2 0x2A
#define CPLD_CMD_WRITEIO_ADDR_P3 0x2B
#define CPLD_CMD_WRITEIO_ADDR_P4 0x2C
#define CPLD_CMD_WRITEIO_ADDR_P5 0x2D
#define CPLD_CMD_WRITEIO_ADDR_P6 0x2E
#define CPLD_CMD_WRITEIO_ADDR_P7 0x2F
#define CPLD_CMD_READIO_ADDR 0x30
#define CPLD_CMD_READIO_ADDR_P1 0x31
#define CPLD_CMD_READIO_ADDR_P2 0x32
#define CPLD_CMD_READIO_ADDR_P3 0x33
#define CPLD_CMD_READIO_ADDR_P4 0x34
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
#define CPLD_CMD_SET_AUTO_REFRESH 0xF1
#define CPLD_CMD_CLEAR_AUTO_REFRESH 0xF2
#define CPLD_CMD_SET_SPI_LOOPBACK 0xFE
#define CPLD_CMD_NOP1 0x00
#define CPLD_CMD_NOP2 0xFF
// Pad numbers for using the MHal GPIO library.
#define PAD_Z80IO_IN_DATA_0 PAD_GPIO0
#define PAD_Z80IO_IN_DATA_1 PAD_GPIO1
#define PAD_Z80IO_IN_DATA_2 PAD_GPIO2
#define PAD_Z80IO_IN_DATA_3 PAD_GPIO3
#define PAD_Z80IO_IN_DATA_4 PAD_GPIO4
#define PAD_Z80IO_IN_DATA_5 PAD_GPIO5
#define PAD_Z80IO_IN_DATA_6 PAD_GPIO6
#define PAD_Z80IO_IN_DATA_7 PAD_GPIO7
#define PAD_SPIO_0 PAD_GPIO8
#define PAD_SPIO_1 PAD_GPIO9
#define PAD_SPIO_2 PAD_GPIO10
#define PAD_SPIO_3 PAD_GPIO11
#define PAD_Z80IO_HIGH_BYTE PAD_SAR_GPIO2 // Byte requiured, 0 = Low Byte, 1 = High Byte.
#define PAD_Z80IO_READY PAD_GPIO12
#define PAD_Z80IO_LTSTATE PAD_PM_IRIN // IRIN
#define PAD_Z80IO_BUSRQ PAD_GPIO13
#define PAD_Z80IO_BUSACK PAD_GPIO14
#define PAD_Z80IO_INT PAD_UART0_RX // GPIO47
#define PAD_Z80IO_NMI PAD_UART0_TX // GPIO48
#define PAD_Z80IO_WAIT PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_RESET PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_RSV1 PAD_SATA_GPIO // GPIO90
// Physical register addresses.
#define PAD_Z80IO_IN_DATA_0_ADDR 0x103C00
#define PAD_Z80IO_IN_DATA_1_ADDR 0x103C02
#define PAD_Z80IO_IN_DATA_2_ADDR 0x103C04
#define PAD_Z80IO_IN_DATA_3_ADDR 0x103C06
#define PAD_Z80IO_IN_DATA_4_ADDR 0x103C08
#define PAD_Z80IO_IN_DATA_5_ADDR 0x103C0A
#define PAD_Z80IO_IN_DATA_6_ADDR 0x103C0C
#define PAD_Z80IO_IN_DATA_7_ADDR 0x103C0E
#define PAD_SPIO_0_ADDR 0x103C10
#define PAD_SPIO_1_ADDR 0x103C12
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_LTSTATE_ADDR 0xF28 // IRIN
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
#define PAD_Z80IO_INT_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_NMI_ADDR 0x103C32 // GPIO48
#define PAD_Z80IO_WAIT_ADDR 0x103C80 // GPIO85
#define PAD_Z80IO_RESET_ADDR 0x103C82 // GPIO86
#define PAD_Z80IO_RSV1_ADDR 0x103C8A // GPIO90
#ifdef NOTNEEDED
#define PAD_Z80IO_OUT_DATA_0 PAD_GPIO12
#define PAD_Z80IO_OUT_DATA_1 PAD_GPIO13
#define PAD_Z80IO_OUT_DATA_2 PAD_GPIO14
#define PAD_Z80IO_OUT_DATA_3 PAD_UART0_RX // GPIO47
#define PAD_Z80IO_OUT_DATA_4 PAD_UART0_TX // GPIO48
#define PAD_Z80IO_OUT_DATA_5 PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_OUT_DATA_6 PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_OUT_DATA_7 PAD_SATA_GPIO // GPIO90
#define PAD_Z80IO_WRITE PAD_PM_IRIN // Write data clock.
#endif
//-------------------------------------------------------------------------------------------------
// The definitions below come from SigmaStar kernel drivers. No header file exists hence the
// duplication.
//-------------------------------------------------------------------------------------------------
#define SUPPORT_SPI_1 0
#define MAX_SUPPORT_BITS 16
#define BANK_TO_ADDR32(b) (b<<9)
#define BANK_SIZE 0x200
#define MS_BASE_REG_RIU_PA 0x1F000000
#define gChipBaseAddr 0xFD203C00
#define gPmSleepBaseAddr 0xFD001C00
#define gSarBaseAddr 0xFD002800
#define gRIUBaseAddr 0xFD000000
#define gMOVDMAAddr 0xFD201600
#define gClkBaseAddr 0xFD207000
#define gMspBaseAddr 0xfd222000
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MSPI0_BANK_ADDR 0x1110
#define MSPI1_BANK_ADDR 0x1111
#define CLK__BANK_ADDR 0x1038
#define CHIPTOP_BANK_ADDR 0x101E
#define MOVDMA_BANK_ADDR 0x100B
#define BASE_REG_MSPI0_ADDR MSPI0_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111000)
#define BASE_REG_MSPI1_ADDR MSPI1_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111100)
#define BASE_REG_CLK_ADDR CLK__BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x103800)
#define BASE_REG_CHIPTOP_ADDR CHIPTOP_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x101E00)
//-------------------------------------------------------------------------------------------------
// Hardware Register Capability
//-------------------------------------------------------------------------------------------------
#define MSPI_WRITE_BUF_OFFSET 0x40
#define MSPI_READ_BUF_OFFSET 0x44
#define MSPI_WBF_SIZE_OFFSET 0x48
#define MSPI_RBF_SIZE_OFFSET 0x48
// read/ write buffer size
#define MSPI_RWSIZE_MASK 0xFF
#define MSPI_RSIZE_BIT_OFFSET 0x8
#define MAX_READ_BUF_SIZE 0x8
#define MAX_WRITE_BUF_SIZE 0x8
// CLK config
#define MSPI_CTRL_OFFSET 0x49
#define MSPI_CLK_CLOCK_OFFSET 0x49
#define MSPI_CLK_CLOCK_BIT_OFFSET 0x08
#define MSPI_CLK_CLOCK_MASK 0xFF
#define MSPI_CLK_PHASE_MASK 0x40
#define MSPI_CLK_PHASE_BIT_OFFSET 0x06
#define MSPI_CLK_POLARITY_MASK 0x80
#define MSPI_CLK_POLARITY_BIT_OFFSET 0x07
#define MSPI_CLK_PHASE_MAX 0x1
#define MSPI_CLK_POLARITY_MAX 0x1
#define MSPI_CLK_CLOCK_MAX 0x7
#define MSPI_CTRL_CPOL_LOW 0x00
#define MSPI_CTRL_CPOL_HIGH 0x80
#define MSPI_CTRL_CPHA_LOW 0x00
#define MSPI_CTRL_CPHA_HIGH 0x40
#define MSPI_CTRL_3WIRE 0x10
#define MSPI_CTRL_INTEN 0x04
#define MSPI_CTRL_RESET 0x02
#define MSPI_CTRL_ENABLE_SPI 0x01
// DC config
#define MSPI_DC_MASK 0xFF
#define MSPI_DC_BIT_OFFSET 0x08
#define MSPI_DC_TR_START_OFFSET 0x4A
#define MSPI_DC_TRSTART_MAX 0xFF
#define MSPI_DC_TR_END_OFFSET 0x4A
#define MSPI_DC_TREND_MAX 0xFF
#define MSPI_DC_TB_OFFSET 0x4B
#define MSPI_DC_TB_MAX 0xFF
#define MSPI_DC_TRW_OFFSET 0x4B
#define MSPI_DC_TRW_MAX 0xFF
// Frame Config
#define MSPI_FRAME_WBIT_OFFSET 0x4C
#define MSPI_FRAME_RBIT_OFFSET 0x4E
#define MSPI_FRAME_BIT_MAX 0x07
#define MSPI_FRAME_BIT_MASK 0x07
#define MSPI_FRAME_BIT_FIELD 0x03
#define MSPI_LSB_FIRST_OFFSET 0x50
#define MSPI_TRIGGER_OFFSET 0x5A
#define MSPI_DONE_OFFSET 0x5B
#define MSPI_DONE_CLEAR_OFFSET 0x5C
#define MSPI_CHIP_SELECT_OFFSET 0x5F
#define MSPI_CS1_DISABLE 0x01
#define MSPI_CS1_ENABLE 0x00
#define MSPI_CS2_DISABLE 0x02
#define MSPI_CS2_ENABLE 0x00
#define MSPI_CS3_DISABLE 0x04
#define MSPI_CS3_ENABLE 0x00
#define MSPI_CS4_DISABLE 0x08
#define MSPI_CS4_ENABLE 0x00
#define MSPI_CS5_DISABLE 0x10
#define MSPI_CS5_ENABLE 0x00
#define MSPI_CS6_DISABLE 0x20
#define MSPI_CS6_ENABLE 0x00
#define MSPI_CS7_DISABLE 0x40
#define MSPI_CS7_ENABLE 0x00
#define MSPI_CS8_DISABLE 0x80
#define MSPI_CS8_ENABLE 0x00
#define MSPI_FULL_DEPLUX_RD_CNT (0x77)
#define MSPI_FULL_DEPLUX_RD00 (0x78)
#define MSPI_FULL_DEPLUX_RD01 (0x78)
#define MSPI_FULL_DEPLUX_RD02 (0x79)
#define MSPI_FULL_DEPLUX_RD03 (0x79)
#define MSPI_FULL_DEPLUX_RD04 (0x7a)
#define MSPI_FULL_DEPLUX_RD05 (0x7a)
#define MSPI_FULL_DEPLUX_RD06 (0x7b)
#define MSPI_FULL_DEPLUX_RD07 (0x7b)
#define MSPI_FULL_DEPLUX_RD08 (0x7c)
#define MSPI_FULL_DEPLUX_RD09 (0x7c)
#define MSPI_FULL_DEPLUX_RD10 (0x7d)
#define MSPI_FULL_DEPLUX_RD11 (0x7d)
#define MSPI_FULL_DEPLUX_RD12 (0x7e)
#define MSPI_FULL_DEPLUX_RD13 (0x7e)
#define MSPI_FULL_DEPLUX_RD14 (0x7f)
#define MSPI_FULL_DEPLUX_RD15 (0x7f)
//chip select bit map
#define MSPI_CHIP_SELECT_MAX 0x07
// control bit
#define MSPI_DONE_FLAG 0x01
#define MSPI_TRIGGER 0x01
#define MSPI_CLEAR_DONE 0x01
#define MSPI_INT_ENABLE 0x04
#define MSPI_RESET 0x02
#define MSPI_ENABLE 0x01
// clk_mspi0
#define MSPI0_CLK_CFG 0x33 //bit 2 ~bit 3
#define MSPI0_CLK_108M 0x00
#define MSPI0_CLK_54M 0x04
#define MSPI0_CLK_12M 0x08
#define MSPI0_CLK_MASK 0x0F
// clk_mspi1
#define MSPI1_CLK_CFG 0x33 //bit 10 ~bit 11
#define MSPI1_CLK_108M 0x0000
#define MSPI1_CLK_54M 0x0400
#define MSPI1_CLK_12M 0x0800
#define MSPI1_CLK_MASK 0x0F00
// clk_mspi
#define MSPI_CLK_CFG 0x33
#define MSPI_SELECT_0 0x0000
#define MSPI_SELECT_1 0x4000
#define MSPI_CLK_MASK 0xF000
// Clock settings
#define MSPI_CPU_CLOCK_1_2 0x0000
#define MSPI_CPU_CLOCK_1_4 0x0100
#define MSPI_CPU_CLOCK_1_8 0x0200
#define MSPI_CPU_CLOCK_1_16 0x0300
#define MSPI_CPU_CLOCK_1_32 0x0400
#define MSPI_CPU_CLOCK_1_64 0x0500
#define MSPI_CPU_CLOCK_1_128 0x0600
#define MSPI_CPU_CLOCK_1_256 0x0700
//CHITOP 101E mspi mode select
#define MSPI0_MODE 0x0C //bit0~bit1
#define MSPI0_MODE_MASK 0x07
#define MSPI1_MODE 0x0C //bit4~bit5
#define MSPI1_MODE_MASK 0x70
#define EJTAG_MODE 0xF
#define EJTAG_MODE_1 0x01
#define EJTAG_MODE_2 0x02
#define EJTAG_MODE_3 0x03
#define EJTAG_MODE_MASK 0x03
//MOVDMA 100B
#define MOV_DMA_SRC_ADDR_L 0x03
#define MOV_DMA_SRC_ADDR_H 0x04
#define MOV_DMA_DST_ADDR_L 0x05
#define MOV_DMA_DST_ADDR_H 0x06
#define MOV_DMA_BYTE_CNT_L 0x07
#define MOV_DMA_BYTE_CNT_H 0x08
#define DMA_MOVE0_IRQ_CLR 0x28
#define MOV_DMA_IRQ_FINAL_STATUS 0x2A
#define DMA_MOVE0_ENABLE 0x00
#define DMA_RW 0x50 //0 for dma write to device, 1 for dma read from device
#define DMA_READ 0x01
#define DMA_WRITE 0x00
#define DMA_DEVICE_MODE 0x51
#define DMA_DEVICE_SEL 0x52
//spi dma
#define MSPI_DMA_DATA_LENGTH_L 0x30
#define MSPI_DMA_DATA_LENGTH_H 0x31
#define MSPI_DMA_ENABLE 0x32
#define MSPI_DMA_RW_MODE 0x33
#define MSPI_DMA_WRITE 0x00
#define MSPI_DMA_READ 0x01
#define MSTAR_SPI_TIMEOUT_MS 30000
#define MSTAR_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA /*| SPI_CS_HIGH | SPI_NO_CS | SPI_LSB_FIRST*/)
//-------------------------------------------------------------------------------------------------
// Macros
//-------------------------------------------------------------------------------------------------
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define READ_BYTE(_reg) (*(volatile u8*)(_reg))
#define READ_WORD(_reg) (*(volatile u16*)(_reg))
#define READ_LONG(_reg) (*(volatile u32*)(_reg))
#define WRITE_BYTE(_reg, _val) {(*((volatile u8*)(_reg))) = (u8)(_val); }
#define WRITE_WORD(_reg, _val) {(*((volatile u16*)(_reg))) = (u16)(_val); }
#define WRITE_LONG(_reg, _val) {(*((volatile u32*)(_reg))) = (u32)(_val); }
#define WRITE_WORD_MASK(_reg, _val, _mask) {(*((volatile u16*)(_reg))) = ((*((volatile u16*)(_reg))) & ~(_mask)) | ((u16)(_val) & (_mask)); }
#define READ_CPLD_DATA_IN() ((MHal_RIU_REG(PAD_Z80IO_IN_DATA_7_ADDR) & 0x1) << 7 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_6_ADDR) & 0x1) << 6 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_5_ADDR) & 0x1) << 5 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_4_ADDR) & 0x1) << 4 |\
(MHal_RIU_REG(PAD_Z80IO_IN_DATA_3_ADDR) & 0x1) << 3 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_2_ADDR) & 0x1) << 2 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_1_ADDR) & 0x1) << 1 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_0_ADDR) & 0x1))
#define SET_CPLD_READ_DATA() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define SET_CPLD_READ_STATUS() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define SET_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define CLEAR_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define CPLD_READY() (MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1)
#define CPLD_RESET() (MHal_RIU_REG(PAD_Z80IO_RESET_ADDR) & 0x1)
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte
//#define MSPI_WRITE(_reg_, _val_) {pr_info("PDS: MSPI_WRITE(0x%x, 0x%x, 0x%x)\n", _reg_, _val_, gMspBaseAddr + ((_reg_)<<2)); WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_)); }
#define MSPI_WRITE(_reg_, _val_) WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_));
//write 2 byte mask
//#define MSPI_WRITE_MASK(_reg_, _val_, mask) {pr_info("PDS: WRITE_LONG(0x%x, 0x%x, mask=0x%x)\n", _reg_, _val_, mask); WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask)); }
#define MSPI_WRITE_MASK(_reg_, _val_, mask) WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask));
#define CLK_READ(_reg_) READ_WORD(gClkBaseAddr + ((_reg_)<<2))
//#define CLK_WRITE(_reg_, _val_) {pr_info("PDS: CLK_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_)); }
#define CLK_WRITE(_reg_, _val_) WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_));
#define CHIPTOP_READ(_reg_) READ_WORD(gChipBaseAddr + ((_reg_)<<2))
//#define CHIPTOP_WRITE(_reg_, _val_) {pr_info("PDS: CHIPTOP_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_)); }
#define CHIPTOP_WRITE(_reg_, _val_) WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_));
#define MOVDMA_READ(_reg_) READ_WORD(gMOVDMAAddr + ((_reg_)<<2))
//#define MOVDMA_WRITE(_reg_, _val_) {pr_info("PDS: MOVDMA_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_)); }
#define MOVDMA_WRITE(_reg_, _val_) WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_));
#define _HAL_MSPI_ClearDone() MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET,MSPI_CLEAR_DONE)
#define MAX_CHECK_CNT 2000
#define MSPI_READ_INDEX 0x0
#define MSPI_WRITE_INDEX 0x1
#define SPI_MIU0_BUS_BASE 0x20000000
#define SPI_MIU1_BUS_BASE 0xFFFFFFFF
// Function definitions.
//
int z80io_init(void);
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData);
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData);
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData);
#ifdef NOTNEEDED
uint8_t z80io_PRL_Send8(uint8_t txData);
uint8_t z680io_PRL_Send16(uint16_t txData);
#endif
uint8_t z80io_PRL_Read8(uint8_t dataFlag);
uint16_t z80io_PRL_Read16(void);
uint8_t z80io_SPI_Test(void);
uint8_t z80io_PRL_Test(void);
uint8_t z80io_Z80_TestMemory(void);
extern void MHal_GPIO_Init(void);
extern void MHal_GPIO_Pad_Set(uint8_t u8IndexGPIO);
extern int MHal_GPIO_PadGroupMode_Set(uint32_t u32PadMode);
extern int MHal_GPIO_PadVal_Set(uint8_t u8IndexGPIO, uint32_t u32PadMode);
extern void MHal_GPIO_Pad_Oen(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pad_Odn(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_Level(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_InOut(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_Low(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_Low(uint8_t u8IndexGPIO);
extern void MHal_Enable_GPIO_INT(uint8_t u8IndexGPIO);
extern int MHal_GPIO_To_Irq(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_POLARITY(uint8_t u8IndexGPIO, uint8_t reverse);
extern void MHal_GPIO_Set_Driving(uint8_t u8IndexGPIO, uint8_t setHigh);
extern void MHal_GPIO_PAD_32K_OUT(uint8_t u8Enable);
#ifdef __cplusplus
}
#endif
#endif // Z80IO_H

View File

@@ -1,541 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io_test.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface Test Methods
// This file contains the methods used to test the SOM to CPLD interface and evaluate
// it's performance. Production builds wont include these methods.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/sched.h>
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
uint8_t z80io_Z80_TestMemory(void)
{
// Locals.
//
uint32_t addr;
uint32_t fullCmd;
uint8_t cmd;
struct timeval start, stop;
uint32_t iterations = 100;
uint32_t errorCount;
uint32_t idx;
long totalTime;
long bytesMSec;
uint8_t result;
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E80000 | (0x82 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E20000 | (0x58 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E00000 | (0xF7 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E90000 | (0x0F << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xCF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xFF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
pr_info("Z80 Host Test - IO.\n");
for(idx=0; idx < 1000000; idx++)
{
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
SPI_SEND32(0xD0000000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0100000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0200000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0300000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0400000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
SPI_SEND32(0xD0500000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
}
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000| ((uint8_t)addr) << 8 | CPLD_CMD_WRITEIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Read performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible IO ports and write to it.
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000 | ((uint8_t)addr) << 8 | CPLD_CMD_READIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing RAM Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write performance (opt).\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Fetch performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Read performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Fetch performance.\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x10;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Read performance (opt).\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x20;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
// Go through all the accessible attribute VRAM and initialise it.
pr_info("Z80 Host Test - Testing VRAM Write performance.\n");
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
iterations = 256*10;
do_gettimeofday(&start);
for(addr=0xD800; addr < 0xE000; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD800)
{
fullCmd = (addr << 16) |(0x71 << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible VRAM and write to it.
for(addr=0xD000; addr < 0xD800; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD000)
{
fullCmd = (addr << 16) | ((uint8_t)idx << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)((1*iterations*0x800)+0x800)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
return(0);
}
// A simple test to verify the SOM to CPLD SPI connectivity and give an estimate of its performance.
// The performance is based on the SPI setup and transmit time along with the close and received data processing.
// In real use, the driver will just send a command and generally ignore received data so increased throughput can be achieved.
//
uint8_t z80io_SPI_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
uint16_t rxData16Last;
uint32_t rxData32;
uint32_t rxData32Last;
uint32_t errorCount;
long totalTime;
long bytesMSec;
// Place the CPLD into echo test mode.
z80io_SPI_Send8(0xfe, &rxData8);
// 1st. test, 8bit.
pr_info("SPI Test - Testing 8 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send8((uint8_t)idx, &rxData8);
if(idx > 1 && (uint8_t)(idx-1) != rxData8)
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint8_t)(idx-1), rxData8 );
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 16bit.
pr_info("SPI Test - Testing 16 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_SPI_Send16((uint16_t)idx, &rxData16);
if(idx > 0 && (uint16_t)(idx-1) != (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint16_t)(idx-1), (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)));
errorCount++;
}
rxData16Last = rxData16;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 3rd. test, 32bit.
pr_info("SPI Test - Testing 32 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send32((uint32_t)idx, &rxData32);
if(idx > 0 && (uint32_t)(idx-1) != (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint32_t)(idx-1), (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)));
errorCount++;
}
rxData32Last = rxData32;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(4*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}
// Method to test the parallel bus, verifying integrity and assessing performance.
uint8_t z80io_PRL_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
long totalTime;
long bytesMSec;
#ifdef NOTNEEDED
uint32_t errorCount;
#endif
// Place the CPLD into echo test mode.
// 1st. test, 8bit RW.
#ifdef NOTNEEDED
pr_info("Parallel Test - Testing 8 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte and readback to compare.
z80io_PRL_Send8((uint8_t)idx);
rxData8 = z80io_PRL_Read8();
if((uint8_t)idx != rxData8)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint8_t)(idx), rxData8);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 8bit Write.
pr_info("Parallel Test - Testing 8 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte.
z80io_PRL_Send8((uint8_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 3rd. test, 8bit Read.
pr_info("Parallel Test - Testing 8 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read byte.
rxData8 = z80io_PRL_Read8(0);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#ifdef NOTNEEDED
// 4th test, 16bit.
pr_info("Parallel Test - Testing 16 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_PRL_Send16((uint16_t)idx);
rxData16 = z80io_PRL_Read16();
if((uint16_t)idx != rxData16)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint16_t)(idx), rxData16);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 5th test, 16bit Write.
pr_info("Parallel Test - Testing 16 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write word.
z80io_PRL_Send16((uint16_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 6th test, 16bit Read.
pr_info("Parallel Test - Testing 16 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read word.
rxData16 = z80io_PRL_Read16();
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}

View File

@@ -1,57 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Menu
// This file contains the methods used to present a menu of options to a user to aid
// in configuration and load/save of applications and data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include "z80menu.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
void z80menu(void)
{
// Locals.
}

View File

@@ -1,44 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Interface Menu
// This file contains the declarations required to provide a menu system allowing a
// user to configure and load/save applications/data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80MENU_H
#define Z80MENU_H
#ifdef __cplusplus
extern "C" {
#endif
// Function definitions.
//
void z80menu(void);
#ifdef __cplusplus
}
#endif
#endif // Z80MENU_H

View File

@@ -1,403 +0,0 @@
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname && longopts[i].shortname < 127) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */

View File

@@ -1,57 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Menu
// This file contains the methods used to present a menu of options to a user to aid
// in configuration and load/save of applications and data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include "z80menu.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
void z80menu(void)
{
// Locals.
}

View File

@@ -1,44 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80menu.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 User Interface Menu
// This file contains the declarations required to provide a menu system allowing a
// user to configure and load/save applications/data.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80MENU_H
#define Z80MENU_H
#ifdef __cplusplus
extern "C" {
#endif
// Function definitions.
//
void z80menu(void);
#ifdef __cplusplus
}
#endif
#endif // Z80MENU_H

View File

@@ -1,14 +1,16 @@
#MODEL := MZ2000 #MODEL := MZ2000
#MODEL := MZ700 #MODEL := MZ700
MODEL := MZ80A #MODEL := MZ80A
#MODEL := PCW8XXX
#MODEL := PCW9XXX
KERNEL := $(PWD)/../../../linux/kernel KERNEL := $(PWD)/../../../linux/kernel
FUSIONX := $(PWD)/../.. FUSIONX := $(PWD)/../..
CROSS := arm-linux-gnueabihf- CROSS := arm-linux-gnueabihf-
ccflags-y += -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ ccflags-y = -O2 -I${src}/Zeta/API -I${src}/Z80/API -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__ -DTARGET_HOST_$(MODEL)=1
CTRLINC += -IZeta/API -IZ80/API CTRLINC = -IZeta/API -IZ80/API -DTARGET_HOST_$(MODEL)=1
obj-m += z80drv.o obj-m += z80drv.o
z80drv-objs += $(MODEL)/z80driver.o Z80.o $(MODEL)/z80io.o $(MODEL)/z80menu.o # emumz.o sharpmz.o osd.o z80drv-objs += src/z80driver.o Z80.o src/z80io.o src/z80menu.o # emumz.o sharpmz.o osd.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/gpio_table.o z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/gpio_table.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.o z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.o
z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o
@@ -16,25 +18,68 @@ z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables
all: all:
@echo "Specify target host, ie. make <host>"
@echo "Supported hosts: MZ80A, MZ700, MZ2000, PCW8XXX, PCW9XXX"
MZ80A: MODEL_MZ80A
MZ700: MODEL_MZ700
MZ2000: MODEL_MZ2000
PCW8XXX: MODEL_PCW8XXX
PCW9XXX: MODEL_PCW9XXX
MODEL_MZ80A:
$(MAKE) MODEL=MZ80A BUILD_MZ80A
MODEL_MZ700:
$(MAKE) MODEL=MZ700 BUILD_MZ700
MODEL_MZ2000:
$(MAKE) MODEL=MZ2000 BUILD_MZ2000
MODEL_PCW8XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW8XXX
MODEL_PCW9XXX:
$(MAKE) MODEL=PCW8XXX BUILD_PCW9XXX
BUILD_MZ80A: sharpbiter k64fcpu kmod z80ctrl
BUILD_MZ700: sharpbiter k64fcpu kmod z80ctrl
BUILD_MZ2000: sharpbiter k64fcpu kmod z80ctrl
BUILD_PCW8XXX: kmod z80ctrl
BUILD_PCW9XXX: kmod z80ctrl
sharpbiter:
@echo "" @echo ""
@echo "Build Sharp MZ Arbiter for host: $(MODEL)" @echo "Build Sharp MZ Arbiter for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/sharpbiter.c -o sharpbiter $(CROSS)gcc $(CTRLINC) src/sharpbiter.c -o sharpbiter
k64fcpu:
@echo "" @echo ""
@echo "Build K64F Daemon for host: $(MODEL)" @echo "Build K64F Daemon for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/k64fcpu.c -o k64fcpu $(CROSS)gcc $(CTRLINC) src/k64fcpu.c -o k64fcpu
kmod:
@echo "" @echo ""
@echo "Build driver for host: $(MODEL)" @echo "Build driver for host: $(MODEL)"
make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules
z80ctrl:
@echo "" @echo ""
@echo "Build z80ctrl tool for host: $(MODEL)" @echo "Build z80ctrl tool for host: $(MODEL)"
$(CROSS)gcc $(CTRLINC) $(MODEL)/z80ctrl.c -o z80ctrl $(CROSS)gcc $(CTRLINC) src/z80ctrl.c -o z80ctrl
install: install:
@echo "Copy kernel driver..." @echo "Copy kernel driver..."
@cp z80drv.ko $(FUSIONX)/modules/ @cp z80drv.ko $(FUSIONX)/modules/
@echo "Copy z80ctrl app..." @echo "Copy z80ctrl app..."
@cp z80ctrl $(FUSIONX)/bin/ @cp z80ctrl $(FUSIONX)/bin/
@if [ -f sharpbiter ]; then\
echo "Copy sharpbiter app...";\
cp sharpbiter $(FUSIONX)/bin/;\
fi
@if [ -f k64fcpu ]; then\
echo "Copy k64fcpu app...";\
cp k64fcpu $(FUSIONX)/bin/;\
fi
clean: clean:
make -C $(KERNEL) M=$(PWD) clean make -C $(KERNEL) M=$(PWD) clean
@rm -f sharpbiter k64fcpu z80ctrl

File diff suppressed because it is too large Load Diff

View File

@@ -183,16 +183,10 @@ int getch(uint8_t wait)
return 0; return 0;
} }
void delay(int number_of_seconds) // Millisecond delay routine.
void delay(int ms_delay)
{ {
// Converting time into milli_seconds usleep(1000 * ms_delay);
int milli_seconds = 1000 * number_of_seconds;
// Storing start time
clock_t start_time = clock();
// looping till required time is not achieved
while (clock() < start_time + milli_seconds);
} }
// Function to dump out a given section of memory via the UART. // Function to dump out a given section of memory via the UART.
@@ -2799,6 +2793,7 @@ void z80ResetRequest(int signalNo)
svcCacheDir(TZSVC_DEFAULT_MZF_DIR, MZF, 1); svcCacheDir(TZSVC_DEFAULT_MZF_DIR, MZF, 1);
// Start the Z80 after initialisation of the memory. // Start the Z80 after initialisation of the memory.
delay(500);
startZ80(TZMM_BOOT); startZ80(TZMM_BOOT);
return; return;
} }
@@ -2926,6 +2921,18 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
// Setup host type, at the moment this is static but as the FusionX progresses and it can be hosted in multiple hosts without firmware change, then we can pull the
// host type from the detected hardware.
#if (TARGET_HOST_MZ80A == 1)
z80Control.hostType = HW_MZ80A;
#elif (TARGET_HOST_MZ700 == 1)
z80Control.hostType = HW_MZ700;
#elif (TARGET_HOST_MZ2000 == 1)
z80Control.hostType = HW_MZ2000;
#else
#error "Unknown host, update code to accommodate."
#endif
// Register the service request handler. // Register the service request handler.
signal(SIGIO, z80ServiceRequest); signal(SIGIO, z80ServiceRequest);

View File

@@ -101,9 +101,15 @@ static t_Z80Ctrl *Z80Ctrl = NULL;
static uint8_t *Z80RAM = NULL; static uint8_t *Z80RAM = NULL;
static uint8_t *Z80ROM = NULL; static uint8_t *Z80ROM = NULL;
// Millisecond delay routine.
void delay(int ms_delay)
{
usleep(1000 * ms_delay);
}
// Method to reset the Z80 CPU. // Method to reset the Z80 CPU.
// //
void reqResetZ80(uint8_t memoryMode) void reqResetZ80(void)
{ {
// Locals. // Locals.
// //
@@ -116,7 +122,7 @@ void reqResetZ80(uint8_t memoryMode)
// Method to start the Z80 CPU. // Method to start the Z80 CPU.
// //
void startZ80(uint8_t memoryMode) void startZ80(void)
{ {
// Locals. // Locals.
// //
@@ -129,7 +135,7 @@ void startZ80(uint8_t memoryMode)
// Method to stop the Z80 CPU. // Method to stop the Z80 CPU.
// //
void stopZ80(uint8_t memoryMode) void stopZ80(void)
{ {
// Locals. // Locals.
// //
@@ -323,12 +329,21 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80. // Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; stopZ80();
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Remove drivers. Remove all because an external event could have changed last configured driver. // Remove drivers. Remove all because an external event could have changed last configured driver.
// //
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE; ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
@@ -336,11 +351,20 @@ int main(int argc, char *argv[])
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Add in the required driver.
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
#endif
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80. // Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET; delay(500);
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); reqResetZ80();
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break; break;
case HOTKEY_RFS80: case HOTKEY_RFS80:
@@ -349,12 +373,21 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80. // Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; stopZ80();
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Remove drivers. Remove all because an external event could have changed last configured driver. // Remove drivers. Remove all because an external event could have changed last configured driver.
// //
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE; ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
@@ -368,10 +401,8 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80. // Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET; delay(500);
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); reqResetZ80();
ioctlCmd.cmd = IOCTL_CMD_Z80_START;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break; break;
case HOTKEY_TZFS: case HOTKEY_TZFS:
@@ -379,12 +410,21 @@ int main(int argc, char *argv[])
ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result);
// Stop the Z80. // Stop the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; stopZ80();
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Remove drivers. Remove all because an external event could have changed last configured driver. // Remove drivers. Remove all because an external event could have changed last configured driver.
// //
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE; ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;
@@ -396,12 +436,6 @@ int main(int argc, char *argv[])
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE; ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// Reset and start the Z80.
ioctlCmd.cmd = IOCTL_CMD_Z80_RESET;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
// ioctlCmd.cmd = IOCTL_CMD_Z80_START;
// ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
break; break;
case HOTKEY_LINUX: case HOTKEY_LINUX:
@@ -412,6 +446,16 @@ int main(int argc, char *argv[])
// Remove drivers. Remove all because an external event could have changed last configured driver. // Remove drivers. Remove all because an external event could have changed last configured driver.
// //
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE; ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
#if(TARGET_HOST_MZ80A == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ2000 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#elif(TARGET_HOST_MZ700 == 1)
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
#endif
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS80;
ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40;

View File

@@ -46,7 +46,13 @@
#define REFRESH_BYTE_COUNT 8 // This constant controls the number of bytes read/written to the z80 bus before a refresh cycle is needed. #define REFRESH_BYTE_COUNT 8 // This constant controls the number of bytes read/written to the z80 bus before a refresh cycle is needed.
#define RFSH_BYTE_CNT 256 // Number of bytes we can write before needing a full refresh for the DRAM. #define RFSH_BYTE_CNT 256 // Number of bytes we can write before needing a full refresh for the DRAM.
#define HOST_MON_TEST_VECTOR 0x4 // Address in the host monitor to test to identify host type. #define HOST_MON_TEST_VECTOR 0x4 // Address in the host monitor to test to identify host type.
#if (TARGET_HOST_MZ80A == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-80A/" // Linux base directory where all the files are stored. On a real tranZPUter this would be the SD card root dir. #define OS_BASE_DIR "/apps/FusionX/host/MZ-80A/" // Linux base directory where all the files are stored. On a real tranZPUter this would be the SD card root dir.
#elif (TARGET_HOST_MZ700 == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-700/"
#elif (TARGET_HOST_MZ2000 == 1)
#define OS_BASE_DIR "/apps/FusionX/host/MZ-2000/"
#endif
#define TZFS_AUTOBOOT_FLAG OS_BASE_DIR "/TZFSBOOT.FLG" // Filename used as a flag, if this file exists in the base directory then TZFS is booted automatically. #define TZFS_AUTOBOOT_FLAG OS_BASE_DIR "/TZFSBOOT.FLG" // Filename used as a flag, if this file exists in the base directory then TZFS is booted automatically.
#define TZ_MAX_Z80_MEM 0x100000 // Maximum Z80 memory available on the tranZPUter board. #define TZ_MAX_Z80_MEM 0x100000 // Maximum Z80 memory available on the tranZPUter board.

View File

@@ -593,6 +593,23 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa
{ {
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
} }
else if(strcasecmp((char *)param1, "MZ80A") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
}
else if(strcasecmp((char *)param1, "MZ700") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
}
else if(strcasecmp((char *)param1, "MZ2000") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
}
else if(strcasecmp((char *)param1, "PCW") == 0)
{
printf("Add device:%s\n", (char *)param1);
ioctlCmd.vdev.device = VIRTUAL_DEVICE_PCW;
}
if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE) if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE)
{ {
ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE; ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE;
@@ -613,6 +630,22 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa
{ {
ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU;
} }
else if(strcasecmp((char *)param1, "MZ80A") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ80A;
}
else if(strcasecmp((char *)param1, "MZ700") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ700;
}
else if(strcasecmp((char *)param1, "MZ2000") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_MZ2000;
}
else if(strcasecmp((char *)param1, "PCW") == 0)
{
ioctlCmd.vdev.device = VIRTUAL_DEVICE_PCW;
}
if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE) if(ioctlCmd.vdev.device != VIRTUAL_DEVICE_NONE)
{ {
ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE; ioctlCmd.cmd = IOCTL_CMD_DEL_DEVICE;
@@ -749,7 +782,7 @@ void showArgs(char *progName, struct optparse *options)
printf(" # Load contents of binary file into memory at address. default = 0x000000.\n"); printf(" # Load contents of binary file into memory at address. default = 0x000000.\n");
printf(" = LOADMEM --file <binary filename> --addr <24 bit addr> --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM> [--offset <offset> --len <length>]\n"); printf(" = LOADMEM --file <binary filename> --addr <24 bit addr> --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM> [--offset <offset> --len <length>]\n");
printf(" = SAVE --file <filename> --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = PageTable, 4 = IOPageTable>\n"); printf(" = SAVE --file <filename> --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = PageTable, 4 = IOPageTable>\n");
printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = MemoryPageTable, 4 = IOPageTable> [--memorypage <0..31>]\n"); printf(" = DUMP --addr <24bit addr> --end <24bit addr> [--size <24bit>] --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM, 3 = MemoryPageTable, 4 = IOPageTable> [--memorypage <0..41>]\n");
printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n"); printf(" = CPLDCMD --data <32bit command> # Send adhoc 32bit command to CPLD.\n");
#if(DEBUG_ENABLED != 0) #if(DEBUG_ENABLED != 0)
printf(" = DEBUG --level <level> # 0 = off, 1..15 debug level, 15 is very verbose.\n"); printf(" = DEBUG --level <level> # 0 = off, 1..15 debug level, 15 is very verbose.\n");

View File

@@ -35,16 +35,41 @@
#ifndef Z80DRIVER_H #ifndef Z80DRIVER_H
#define Z80DRIVER_H #define Z80DRIVER_H
// Build time target. Overrides if compile time definition given.
#if defined(TARGET_HOST_MZ700)
#define TARGET_HOST_MZ700 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ2000)
#define TARGET_HOST_MZ2000 1
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_MZ80A)
#define TARGET_HOST_MZ80A 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_PCW 0
#elif defined(TARGET_HOST_PCW8XXX) || defined(TARGET_HOST_PCW9XXX)
#define TARGET_HOST_PCW 1
#define TARGET_HOST_MZ2000 0
#define TARGET_HOST_MZ700 0
#define TARGET_HOST_MZ80A 0
#else
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 0 // MZ80A
#define TARGET_HOST_PCW 0 // Amstrad PCW8XXX/9XXX
#endif
// Constants. // Constants.
#define DRIVER_LICENSE "GPL" #define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Philip D Smart" #define DRIVER_AUTHOR "Philip D Smart"
#define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver" #define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver"
#define DRIVER_VERSION "v1.3" #define DRIVER_VERSION "v1.4"
#define DRIVER_VERSION_DATE "Feb 2023" #define DRIVER_VERSION_DATE "Apr 2023"
#define DRIVER_COPYRIGHT "(C) 2018-2023" #define DRIVER_COPYRIGHT "(C) 2018-2023"
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 1 // MZ80A
#define Z80_VIRTUAL_ROM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M which is 4x512K ROMS. #define Z80_VIRTUAL_ROM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M which is 4x512K ROMS.
#define Z80_VIRTUAL_RAM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M. #define Z80_VIRTUAL_RAM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M.
#define Z80_MEMORY_PAGE_SIZE 16 #define Z80_MEMORY_PAGE_SIZE 16
@@ -69,13 +94,14 @@
#define MEMORY_TYPE_VIRTUAL_ROM 0x04000000 #define MEMORY_TYPE_VIRTUAL_ROM 0x04000000
#define MEMORY_TYPE_VIRTUAL_RAM_RO 0x02000000 #define MEMORY_TYPE_VIRTUAL_RAM_RO 0x02000000
#define MEMORY_TYPE_VIRTUAL_HW 0x01000000 #define MEMORY_TYPE_VIRTUAL_HW 0x01000000
#define MEMORY_TYPE_PHYSICAL_RAM_WT MEMORY_TYPE_PHYSICAL_RAM | MEMORY_TYPE_VIRTUAL_RAM
#define IO_TYPE_PHYSICAL_HW 0x80000000 #define IO_TYPE_PHYSICAL_HW 0x80000000
#define IO_TYPE_VIRTUAL_HW 0x40000000 #define IO_TYPE_VIRTUAL_HW 0x40000000
// Hotkeys handled. // Hotkeys handled.
#define HOTKEY_ORIGINAL 0xE8 #define HOTKEY_ORIGINAL 0xE8
#define HOTKEY_RFS80 0xE9 #define HOTKEY_RFS40 0xE9
#define HOTKEY_RFS40 0xEA #define HOTKEY_RFS80 0xEA
#define HOTKEY_TZFS 0xEB #define HOTKEY_TZFS 0xEB
#define HOTKEY_LINUX 0xEC #define HOTKEY_LINUX 0xEC
@@ -100,6 +126,7 @@
// Approximate governor delays to regulate emulated CPU speed. // Approximate governor delays to regulate emulated CPU speed.
// MZ-700 // MZ-700
#if(TARGET_HOST_MZ700 == 1) #if(TARGET_HOST_MZ700 == 1)
#if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253 #define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126 #define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63 #define INSTRUCTION_DELAY_ROM_14MHZ 63
@@ -116,6 +143,25 @@
#define INSTRUCTION_DELAY_RAM_112MHZ 8 #define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4 #define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1 #define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_3_54MHZ 253
#define INSTRUCTION_DELAY_ROM_7MHZ 126
#define INSTRUCTION_DELAY_ROM_14MHZ 63
#define INSTRUCTION_DELAY_ROM_28MHZ 32
#define INSTRUCTION_DELAY_ROM_56MHZ 16
#define INSTRUCTION_DELAY_ROM_112MHZ 8
#define INSTRUCTION_DELAY_ROM_224MHZ 4
#define INSTRUCTION_DELAY_ROM_448MHZ 1
#define INSTRUCTION_DELAY_RAM_3_54MHZ 253
#define INSTRUCTION_DELAY_RAM_7MHZ 126
#define INSTRUCTION_DELAY_RAM_14MHZ 63
#define INSTRUCTION_DELAY_RAM_28MHZ 32
#define INSTRUCTION_DELAY_RAM_56MHZ 16
#define INSTRUCTION_DELAY_RAM_112MHZ 8
#define INSTRUCTION_DELAY_RAM_224MHZ 4
#define INSTRUCTION_DELAY_RAM_448MHZ 1
#endif
#define INSTRUCTION_EQUIV_FREQ_3_54MHZ 3540000 #define INSTRUCTION_EQUIV_FREQ_3_54MHZ 3540000
#define INSTRUCTION_EQUIV_FREQ_7MHZ 7000000 #define INSTRUCTION_EQUIV_FREQ_7MHZ 7000000
#define INSTRUCTION_EQUIV_FREQ_14MHZ 14000000 #define INSTRUCTION_EQUIV_FREQ_14MHZ 14000000
@@ -124,6 +170,7 @@
#define INSTRUCTION_EQUIV_FREQ_112MHZ 112000000 #define INSTRUCTION_EQUIV_FREQ_112MHZ 112000000
#define INSTRUCTION_EQUIV_FREQ_224MHZ 224000000 #define INSTRUCTION_EQUIV_FREQ_224MHZ 224000000
#define INSTRUCTION_EQUIV_FREQ_448MHZ 448000000 #define INSTRUCTION_EQUIV_FREQ_448MHZ 448000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 10
enum Z80_INSTRUCTION_DELAY { enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_3_54MHZ, ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_3_54MHZ,
@@ -141,7 +188,7 @@ enum Z80_INSTRUCTION_DELAY {
RAM_DELAY_X16 = INSTRUCTION_DELAY_RAM_56MHZ, RAM_DELAY_X16 = INSTRUCTION_DELAY_RAM_56MHZ,
RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_112MHZ, RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_112MHZ,
RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_224MHZ, RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_224MHZ,
RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_448MHZ RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_448MHZ,
CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_3_54MHZ, CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_3_54MHZ,
CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_7MHZ, CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_7MHZ,
CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_14MHZ, CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_14MHZ,
@@ -155,22 +202,43 @@ enum Z80_INSTRUCTION_DELAY {
// MZ-2000 // MZ-2000
#if(TARGET_HOST_MZ2000 == 1) #if(TARGET_HOST_MZ2000 == 1)
#define INSTRUCTION_DELAY_ROM_4MHZ 243
#define INSTRUCTION_DELAY_ROM_8MHZ 122 #if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_16MHZ 61 #define INSTRUCTION_DELAY_ROM_4MHZ 213
#define INSTRUCTION_DELAY_ROM_32MHZ 30 #define INSTRUCTION_DELAY_ROM_8MHZ 109
#define INSTRUCTION_DELAY_ROM_64MHZ 15 #define INSTRUCTION_DELAY_ROM_16MHZ 54
#define INSTRUCTION_DELAY_ROM_32MHZ 27
#define INSTRUCTION_DELAY_ROM_64MHZ 14
#define INSTRUCTION_DELAY_ROM_128MHZ 7 #define INSTRUCTION_DELAY_ROM_128MHZ 7
#define INSTRUCTION_DELAY_ROM_256MHZ 3 #define INSTRUCTION_DELAY_ROM_256MHZ 3
#define INSTRUCTION_DELAY_ROM_512MHZ 1 #define INSTRUCTION_DELAY_ROM_512MHZ 1
#define INSTRUCTION_DELAY_RAM_4MHZ 218 #define INSTRUCTION_DELAY_RAM_4MHZ 212
#define INSTRUCTION_DELAY_RAM_8MHZ 112 #define INSTRUCTION_DELAY_RAM_8MHZ 106
#define INSTRUCTION_DELAY_RAM_16MHZ 56 #define INSTRUCTION_DELAY_RAM_16MHZ 53
#define INSTRUCTION_DELAY_RAM_32MHZ 28 #define INSTRUCTION_DELAY_RAM_32MHZ 26
#define INSTRUCTION_DELAY_RAM_64MHZ 14 #define INSTRUCTION_DELAY_RAM_64MHZ 13
#define INSTRUCTION_DELAY_RAM_128MHZ 7 #define INSTRUCTION_DELAY_RAM_128MHZ 7
#define INSTRUCTION_DELAY_RAM_256MHZ 3 #define INSTRUCTION_DELAY_RAM_256MHZ 3
#define INSTRUCTION_DELAY_RAM_512MHZ 1 #define INSTRUCTION_DELAY_RAM_512MHZ 1
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240 // These values are smaller than the ROM as Rom has 1 wait state added per cycle.
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000 #define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000
#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000 #define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000
#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000 #define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000
@@ -179,6 +247,7 @@ enum Z80_INSTRUCTION_DELAY {
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000 #define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 #define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000 #define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 4
enum Z80_INSTRUCTION_DELAY { enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ, ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ,
@@ -258,6 +327,7 @@ enum Z80_INSTRUCTION_DELAY {
#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000 #define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000 #define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 #define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 2
// Table of governor delays to be used to control run frequency, // Table of governor delays to be used to control run frequency,
enum Z80_INSTRUCTION_DELAY { enum Z80_INSTRUCTION_DELAY {
@@ -288,6 +358,82 @@ enum Z80_INSTRUCTION_DELAY {
}; };
#endif #endif
// Amstrad PCW-8256
#if(TARGET_HOST_PCW == 1)
#if(DEBUG_ENABLED > 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#if(DEBUG_ENABLED == 0)
#define INSTRUCTION_DELAY_ROM_4MHZ 295
#define INSTRUCTION_DELAY_ROM_8MHZ 148
#define INSTRUCTION_DELAY_ROM_16MHZ 74
#define INSTRUCTION_DELAY_ROM_32MHZ 37
#define INSTRUCTION_DELAY_ROM_64MHZ 19
#define INSTRUCTION_DELAY_ROM_128MHZ 10
#define INSTRUCTION_DELAY_ROM_256MHZ 5
#define INSTRUCTION_DELAY_ROM_512MHZ 3
#define INSTRUCTION_DELAY_RAM_4MHZ 240
#define INSTRUCTION_DELAY_RAM_8MHZ 148
#define INSTRUCTION_DELAY_RAM_16MHZ 74
#define INSTRUCTION_DELAY_RAM_32MHZ 37
#define INSTRUCTION_DELAY_RAM_64MHZ 19
#define INSTRUCTION_DELAY_RAM_128MHZ 10
#define INSTRUCTION_DELAY_RAM_256MHZ 5
#define INSTRUCTION_DELAY_RAM_512MHZ 3
#endif
#define INSTRUCTION_EQUIV_FREQ_4MHZ 4000000
#define INSTRUCTION_EQUIV_FREQ_8MHZ 8000000
#define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000
#define INSTRUCTION_EQUIV_FREQ_32MHZ 32000000
#define INSTRUCTION_EQUIV_FREQ_64MHZ 64000000
#define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000
#define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000
#define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000
#define INSTRUCTION_GOVERNOR_IO_SKIP 5
enum Z80_INSTRUCTION_DELAY {
ROM_DELAY_NORMAL = INSTRUCTION_DELAY_ROM_4MHZ,
ROM_DELAY_X2 = INSTRUCTION_DELAY_ROM_8MHZ,
ROM_DELAY_X4 = INSTRUCTION_DELAY_ROM_16MHZ,
ROM_DELAY_X8 = INSTRUCTION_DELAY_ROM_32MHZ,
ROM_DELAY_X16 = INSTRUCTION_DELAY_ROM_64MHZ,
ROM_DELAY_X32 = INSTRUCTION_DELAY_ROM_128MHZ,
ROM_DELAY_X64 = INSTRUCTION_DELAY_ROM_256MHZ,
ROM_DELAY_X128 = INSTRUCTION_DELAY_ROM_512MHZ,
RAM_DELAY_NORMAL = INSTRUCTION_DELAY_RAM_4MHZ,
RAM_DELAY_X2 = INSTRUCTION_DELAY_RAM_8MHZ,
RAM_DELAY_X4 = INSTRUCTION_DELAY_RAM_16MHZ,
RAM_DELAY_X8 = INSTRUCTION_DELAY_RAM_32MHZ,
RAM_DELAY_X16 = INSTRUCTION_DELAY_RAM_64MHZ,
RAM_DELAY_X32 = INSTRUCTION_DELAY_RAM_128MHZ,
RAM_DELAY_X64 = INSTRUCTION_DELAY_RAM_256MHZ,
RAM_DELAY_X128 = INSTRUCTION_DELAY_RAM_512MHZ,
CPU_FREQUENCY_NORMAL = INSTRUCTION_EQUIV_FREQ_4MHZ,
CPU_FREQUENCY_X2 = INSTRUCTION_EQUIV_FREQ_8MHZ,
CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_16MHZ,
CPU_FREQUENCY_X8 = INSTRUCTION_EQUIV_FREQ_32MHZ,
CPU_FREQUENCY_X16 = INSTRUCTION_EQUIV_FREQ_64MHZ,
CPU_FREQUENCY_X32 = INSTRUCTION_EQUIV_FREQ_128MHZ,
CPU_FREQUENCY_X64 = INSTRUCTION_EQUIV_FREQ_256MHZ,
CPU_FREQUENCY_X128 = INSTRUCTION_EQUIV_FREQ_512MHZ,
};
#endif
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action. // IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_Z80_STOP 's' #define IOCTL_CMD_Z80_STOP 's'
#define IOCTL_CMD_Z80_START 'S' #define IOCTL_CMD_Z80_START 'S'
@@ -426,9 +572,12 @@ enum Z80_INSTRUCTION_DELAY {
}\ }\
} }
#define resetZ80() {\ #define resetZ80() {\
if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\
sendSignal(Z80Ctrl->ioTask, SIGUSR1); \
setupMemory(Z80Ctrl->defaultPageMode);\ setupMemory(Z80Ctrl->defaultPageMode);\
if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\
{\
sendSignal(Z80Ctrl->ioTask, SIGUSR1); \
udelay(2000);\
}\
z80_instant_reset(&Z80CPU);\ z80_instant_reset(&Z80CPU);\
} }
@@ -460,10 +609,14 @@ enum Z80_MEMORY_PROFILE {
}; };
enum VIRTUAL_DEVICE { enum VIRTUAL_DEVICE {
VIRTUAL_DEVICE_NONE = 0x00000000, VIRTUAL_DEVICE_NONE = 0x00000000,
VIRTUAL_DEVICE_MZ80A = 0x00000001,
VIRTUAL_DEVICE_MZ700 = 0x00000002,
VIRTUAL_DEVICE_MZ2000 = 0x00000004,
VIRTUAL_DEVICE_PCW = 0x00000008,
VIRTUAL_DEVICE_RFS40 = 0x01000000, VIRTUAL_DEVICE_RFS40 = 0x01000000,
VIRTUAL_DEVICE_RFS80 = 0x02000000, VIRTUAL_DEVICE_RFS80 = 0x02000000,
VIRTUAL_DEVICE_RFS = 0x03000000, VIRTUAL_DEVICE_RFS = 0x03000000,
VIRTUAL_DEVICE_TZPU = 0x04000000 VIRTUAL_DEVICE_TZPU = 0x04000000,
}; };
typedef struct { typedef struct {
@@ -524,14 +677,6 @@ typedef struct {
uint8_t ioReadAhead; uint8_t ioReadAhead;
uint8_t ioWriteAhead; uint8_t ioWriteAhead;
#if(TARGET_HOST_MZ2000 == 1)
uint8_t lowMemorySwap;
#endif
#if(TARGET_HOST_MZ80A == 1)
// MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
uint8_t memSwitch;
#endif
// Keyboard strobe and data. Required to detect hotkey press. // Keyboard strobe and data. Required to detect hotkey press.
uint8_t keyportStrobe; uint8_t keyportStrobe;
uint8_t keyportShiftCtrl; uint8_t keyportShiftCtrl;
@@ -546,6 +691,7 @@ typedef struct {
// is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made. // is quicker than RAM (both are in the same kernel memory) as a pointer calculation needs to be made.
uint32_t cpuGovernorDelayROM; uint32_t cpuGovernorDelayROM;
uint32_t cpuGovernorDelayRAM; uint32_t cpuGovernorDelayRAM;
uint8_t governorSkip;
// An I/O processor, running as a User Space daemon, can register to receive signals and events. // An I/O processor, running as a User Space daemon, can register to receive signals and events.
struct task_struct *ioTask; struct task_struct *ioTask;

View File

@@ -82,6 +82,14 @@
#define CPLD_CMD_READIO_ADDR_P5 0x35 #define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36 #define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37 #define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_READIO_WRITE_ADDR 0x38
#define CPLD_CMD_READIO_WRITE_ADDR_P1 0x39
#define CPLD_CMD_READIO_WRITE_ADDR_P2 0x3A
#define CPLD_CMD_READIO_WRITE_ADDR_P3 0x3B
#define CPLD_CMD_READIO_WRITE_ADDR_P4 0x3C
#define CPLD_CMD_READIO_WRITE_ADDR_P5 0x3D
#define CPLD_CMD_READIO_WRITE_ADDR_P6 0x3E
#define CPLD_CMD_READIO_WRITE_ADDR_P7 0x3F
#define CPLD_CMD_HALT 0x50 #define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51 #define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0 #define CPLD_CMD_SET_SIGROUP1 0xF0
@@ -130,7 +138,7 @@
#define PAD_SPIO_2_ADDR 0x103C14 #define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16 #define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425 #define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18 #define PAD_Z80IO_READY_ADDR 0x103C18 // GPIO12
#define PAD_Z80IO_LTSTATE_ADDR 0x103C30 // GPIO47 #define PAD_Z80IO_LTSTATE_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A #define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C #define PAD_Z80IO_BUSACK_ADDR 0x103C1C
@@ -386,8 +394,8 @@
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4) #define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4) #define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4)
#define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4) #define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
@@ -396,9 +404,18 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
} }
#define SPI_SEND_I_8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -406,10 +423,9 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
@@ -417,24 +433,63 @@
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
#define SPI_SEND32i(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ #define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \ MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \ while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) != 0);\
pr_info("Stage 0");\
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
pr_info("Stage 1");\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \ MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
pr_info("Stage 2");\ while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
timeout = MAX_CHECK_CNT; \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
pr_info("Stage 3");\ }
#define SPI_SET_FRAME_SIZE() { MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
}
#define SPI_SEND_I_32(_d1_, _d2_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND_48(_d1_, _d2_, _d3_) { uint32_t timeout = MAX_CHECK_CNT*2; \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 6); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d3_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)_d2_); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+2, (uint16_t)_d1_); \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };\
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \ MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
} }
// while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };
// read 2 byte // read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2)) #define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte // write 2 byte

View File

@@ -54,7 +54,7 @@ uint8_t z80io_Z80_TestMemory(void)
spinlock_t spinLock; spinlock_t spinLock;
unsigned long flags; unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH); SPI_SEND_8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR); SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100); udelay(100);
@@ -185,7 +185,7 @@ uint8_t z80io_Z80_TestMemory(void)
// Read back the same byte. // Read back the same byte.
cmd = 0x10; cmd = 0x10;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
while(CPLD_READY() == 0); while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN(); result = READ_CPLD_DATA_IN();
@@ -223,7 +223,7 @@ uint8_t z80io_Z80_TestMemory(void)
// Read back the same byte. // Read back the same byte.
cmd = 0x20; cmd = 0x20;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
while(CPLD_READY() == 0); while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN(); result = READ_CPLD_DATA_IN();
@@ -254,7 +254,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else } else
{ {
cmd = 0x11; cmd = 0x11;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
} }
while(CPLD_READY() == 0); while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN(); result = READ_CPLD_DATA_IN();
@@ -280,7 +280,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else } else
{ {
cmd = 0x21; cmd = 0x21;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
} }
while(CPLD_READY() == 0); while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN(); result = READ_CPLD_DATA_IN();
@@ -306,7 +306,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else } else
{ {
cmd = 0x19; cmd = 0x19;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
} }
} }
for(idx=0; idx < iterations; idx++) for(idx=0; idx < iterations; idx++)
@@ -322,7 +322,7 @@ uint8_t z80io_Z80_TestMemory(void)
} else } else
{ {
cmd = 0x19; cmd = 0x19;
SPI_SEND8(cmd); SPI_SEND_8(cmd);
} }
} }
} }

View File

@@ -0,0 +1,436 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz2000.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-2000
// This file contains the methods used to emulate the original Sharp MZ-2000 without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// System ROM's, either use the host machine ROM or preload a ROM image.
#define ROM_DIR "/apps/FusionX/host/MZ-2000/ROMS/"
#define ROM_IPL_ORIG_FILENAME ROM_DIR "mz2000_ipl.orig"
// Boot ROM rom load and size definitions.
#define ROM_BOOT_LOAD_ADDR 0x000000
#define ROM_BOOT_SIZE 0x800
// PCW control.
typedef struct {
uint8_t lowMemorySwap; // Boot mode lower memory is swapped to 0x8000:0xFFFF
uint8_t highMemoryVRAM; // Flag to indicate high memory range 0xD000:0xFFFF is assigned to VRAM.
uint8_t graphicsVRAM; // Flag to indicate graphics VRAM selected, default is character VRAM (0).
uint8_t regCtrl; // Control register.
} t_MZ2000Ctrl;
// RFS Board control.
static t_MZ2000Ctrl MZ2000Ctrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz2000SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
MZ2000Ctrl.lowMemorySwap = 0x01; // Set memory swap flag to swapped, ie. IPL mode sees DRAM 0x0000:0x7FFF swapped to 0x8000:0xFFFF and ROM pages into 0x0000.
MZ2000Ctrl.highMemoryVRAM = 0x00;
MZ2000Ctrl.graphicsVRAM = 0x00;
MZ2000Ctrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x8000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else //if(idx >= 0x8000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
// Video RAM labelled as HW as we dont want to cache it.
//else if(idx >= 0xD000 && idx < 0xE000)
// {
// setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
//} else
// {
// setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
// }
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
// MZ-2000 comes up in IPL mode where lower 32K is ROM and upper 32K is RAM remapped from 0x0000.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x8000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (MZ2000Ctrl.lowMemorySwap ? idx - 0x8000 : idx));
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
pr_info("MZ-2000 Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz2000LoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz2000Init(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
// Lower memory is actually upper on startup, but ROM paged in, so set to zero.
if(idx >= 0x0000 && idx < 0x8000)
{
Z80Ctrl->ram[idx+0x8000] = 0x00;
} else
// Lower memory is paged in at 0x8000:0xFFFF
if(idx >= 0x8000 && idx < 0x10000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx-0x8000] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
if(idx >= 0x0000 && idx < 0x8000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
// Initial memory config.
mz2000SetupMemory(Z80Ctrl->defaultPageMode);
// mz2000LoadROM(ROM_IPL_ORIG_FILENAME, ROM_BOOT_LOAD_ADDR, ROM_BOOT_SIZE);
pr_info("Enabling MZ-2000 driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz2000Remove(void)
{
pr_info("Removing MZ-2000 driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz2000DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
switch(address)
{
default:
break;
}
} else
{
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// 8255 - Port A
case IO_ADDR_E0:
break;
// 8255 - Port B
case IO_ADDR_E1:
break;
// 8255 - Port C
// Bit 3 - L = Reset and enter IPL mode.
// Bit 1 - H = Set memory to normal state and reset cpu, RAM 0x0000:0xFFFF, L = no change.
case IO_ADDR_E2:
if(data & 0x01)
data = 0x03;
else if((data & 0x08) == 0)
data = 0x06;
else
break;
// 8255 - Control Port
// Bit 7 - H = Control word, L 3:1 define port C bit, bit 0 defines its state.
case IO_ADDR_E3:
//pr_info("E3:%02x\n", data);
// Program control register.
if(data & 0x80)
{
// Do nothing, this is the register which sets the 8255 mode.
} else
{
switch((data >> 1) & 0x07)
{
// NST toggle.
case 1:
// NST pages in all RAM and resets cpu.
if(data & 0x01)
{
MZ2000Ctrl.lowMemorySwap = 0;
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(Z80Ctrl->defaultPageMode == USE_PHYSICAL_RAM)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
//resetZ80();
}
break;
// IPL start.
case 3:
// If IPL is active (L), reconfigure memory for power on state.
if((data & 0x01) == 0)
{
mz2000SetupMemory(Z80Ctrl->defaultPageMode);
}
break;
default:
break;
}
}
break;
// Port A - Z80 PIO, contains control bits affecting memory mapping.
// Bit
// 7 - Assign address range 0xD000:0xFFFF to V-RAM when H, when L assign RAM
// 6 - Character VRAM (H), Graphics VRAM (L)
// 4 - Change screen to 80 Char (H), 40 Char (L)
// NB. When the VRAM is paged in, if Character VRAM is selected, range 0xD000:0xD7FF is VRAM, 0xC000:0xCFFF, 0xE000:0xFFFF is RAM.
case IO_ADDR_E8:
// High memory being assigned to VRAM or reverting?
if(MZ2000Ctrl.highMemoryVRAM && (data & 0x80) == 0)
{
for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(Z80Ctrl->defaultPageMode == USE_PHYSICAL_RAM)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (MZ2000Ctrl.lowMemorySwap ? idx - 0x8000 : idx));
}
}
MZ2000Ctrl.highMemoryVRAM = 0;
} else
// If this is the first activation of the VRAM or the state of it changes, ie. character <-> graphics, then update the memory mapping.
if( (!MZ2000Ctrl.highMemoryVRAM && (data & 0x80) != 0) || (MZ2000Ctrl.highMemoryVRAM && (MZ2000Ctrl.graphicsVRAM >> 6) != (data & 0x40)) )
{
for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// Graphics RAM see's the entire range set to PHYSICAL, Character RAM only see's 0xD000:0xD7FF set to PHYSICAL.
if( ((data & 0x40) && (idx >= 0xD000 && idx < 0xD800)) || ((data & 0x40) == 0) )
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
}
MZ2000Ctrl.highMemoryVRAM = 1;
}
MZ2000Ctrl.graphicsVRAM = (data & 0x040) ? 1 : 0;
break;
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz2000Read(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz2000Write(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,481 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz700.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-700
// This file contains the methods used to emulate the original Sharp MZ-700 without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
// Apr 2023 v1.1 - Updates from the PCW/MZ2000 changes.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// PCW control.
typedef struct {
uint8_t regCtrl; // Control register.
} t_MZ700Ctrl;
// RFS Board control.
static t_MZ700Ctrl MZ700Ctrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz700SetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
MZ700Ctrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0x10000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0xF000 && idx < 0x10000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
pr_info("MZ-700 Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz700LoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz700Init(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
if(idx >= 0x1000 && idx < 0xD000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
// Copy BIOS and any add-on ROMS.
if((idx >= 0x0000 && idx < 0x1000) || (idx >= 0xE800 && idx < 0x10000))
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
// Add in a test program to guage execution speed.
#if(TARGET_HOST_MZ700 == 1)
Z80Ctrl->ram[0x1200] = 0x01;
Z80Ctrl->ram[0x1201] = 0x86;
Z80Ctrl->ram[0x1202] = 0xf2;
Z80Ctrl->ram[0x1203] = 0x3e;
Z80Ctrl->ram[0x1204] = 0x15;
Z80Ctrl->ram[0x1205] = 0x3d;
Z80Ctrl->ram[0x1206] = 0x20;
Z80Ctrl->ram[0x1207] = 0xfd;
Z80Ctrl->ram[0x1208] = 0x0b;
Z80Ctrl->ram[0x1209] = 0x78;
Z80Ctrl->ram[0x120a] = 0xb1;
Z80Ctrl->ram[0x120b] = 0x20;
Z80Ctrl->ram[0x120c] = 0xf6;
Z80Ctrl->ram[0x120d] = 0xc3;
Z80Ctrl->ram[0x120e] = 0x00;
Z80Ctrl->ram[0x120f] = 0x00;
#endif
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
pr_info("Enabling MZ-700 driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz700Remove(void)
{
pr_info("Removing MZ-700 driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz700DecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("MEM:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{
default:
break;
}
} else
{
// #if(DEBUG_ENABLED & 1)
// if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM | |
// OUT 0xE1 = | | |DRAM
// OUT 0xE2 = |MONITOR | |
// OUT 0xE3 = | | |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
break;
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
case IO_ADDR_E1:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// MZ-700 mode we only work in first 64K block.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
break;
// Enable MOnitor ROM in lower 4K block
case IO_ADDR_E2:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
break;
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
case IO_ADDR_E3:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Reset to power on condition memory map.
case IO_ADDR_E4:
// Lower 4K set to Monitor ROM.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
if(!Z80Ctrl->inhibitMode)
{
// Upper 12K to hardware.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
case IO_ADDR_E5:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
backupMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
Z80Ctrl->inhibitMode = 1;
break;
// Restore D000-FFFF to its original state.
case IO_ADDR_E6:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
restoreMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
}
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz700Read(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz700Write(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,382 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_mz80a.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - MZ-80A
// This file contains the methods used to emulate the original Sharp MZ-80A without
// any additions, such as the RFS or TZFS boards.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// 40/80 Video Module control registers.
//
#define DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
// PCW control.
typedef struct {
// MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
uint8_t memSwitch;
uint8_t regCtrl; // Control register.
} t_MZ80ACtrl;
// RFS Board control.
static t_MZ80ACtrl MZ80ACtrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void mz80aSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// Setup defaults.
MZ80ACtrl.memSwitch = 0x00;
MZ80ACtrl.regCtrl = 0x00;
// Setup default mode according to run mode, ie. Physical run or Virtual run.
//
if(mode == USE_PHYSICAL_RAM)
{
// Initialise the page pointers and memory to use physical RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0x10000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_ROM, idx);
} else
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Cancel refresh as using physical RAM for program automatically refreshes DRAM.
Z80Ctrl->refreshDRAM = 0;
}
else if(mode == USE_VIRTUAL_RAM)
{
// Initialise the page pointers and memory to use virtual RAM.
for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0 && idx < 0x1000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_HW, idx);
}
else if(idx >= 0xF000 && idx < 0x10000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
}
// Original mode, ie. no virtual devices active, copy the host BIOS into the Virtual ROM and initialise remainder of ROM memory
// such that the host behaves as per original spec.
pr_info("Sync Host BIOS to virtual ROM.\n");
for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++)
{
if((idx >= 0x0000 && idx < 0x1000) || (idx >= 0xF000 && idx < 0x10000))
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->rom[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->rom[idx] = 0x00;
}
}
pr_info("MZ-80A Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t mz80aLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void mz80aInit(uint8_t mode)
{
// Locals.
uint32_t idx;
// Initialise the virtual RAM from the HOST DRAM. This is to maintain compatibility as some applications (in my experience) have
// bugs, which Im putting down to not initialising variables. The host DRAM is in a pattern of 0x00..0x00, 0xFF..0xFF repeating
// when first powered on.
pr_info("Sync Host RAM to virtual RAM.\n");
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
if(idx >= 0x1000 && idx < 0xD000)
{
SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
Z80Ctrl->ram[idx] = z80io_PRL_Read8(1);
} else
{
Z80Ctrl->ram[idx] = 0x00;
}
}
MZ80ACtrl.memSwitch = 0;
// If the 40/80 Video Module board is installed, ensure 40 character mode is selected.
SPI_SEND_32(DSPCTL, CPLD_CMD_READ_ADDR);
while(CPLD_READY() == 0);
SPI_SEND_32(DSPCTL, CPLD_CMD_WRITE_ADDR);
pr_info("Enabling MZ-80A driver.\n");
return;
}
// Perform any de-initialisation when the driver is removed.
void mz80aRemove(void)
{
pr_info("Removing MZ-80A driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void mz80aDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// Decoding memory address or I/O address?
if(ioFlag == 0)
{
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{
// Memory map switch.
case 0xE00C: case 0xE00D: case 0xE00E: case 0xE00F:
if(readFlag)
{
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
}
MZ80ACtrl.memSwitch = 1;
break;
// Reset memory map switch.
case 0xE010: case 0xE011: case 0xE012: case 0xE013:
if(readFlag)
{
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (idx+0xC000));
}
}
MZ80ACtrl.memSwitch = 0;
break;
default:
break;
}
} else
{
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{
// Port is not a memory management port.
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t mz80aRead(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualMemory(address))
{
// Retrieve data from virtual memory.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
}
break;
}
}
return(data);
}
// Method to handle writes.
static inline void mz80aWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
if(isVirtualRAM(address))
{
// Update virtual memory.
writeVirtualRAM(address, data);
}
}
}
return;
}

View File

@@ -0,0 +1,379 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80vhw_pcw.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 Virtual Hardware Driver - Amstrad PCW-8xxx/PCW-9xxx
// This file contains the methods used to emulate the Amstrad PCW specific
// hardware.
//
// These drivers are intended to be instantiated inline to reduce overhead of a call
// and as such, they are included like header files rather than C linked object files.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Mar 2023 v1.0 - Initial write based on the RFS hardware module.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
// Device constants.
#define RAM_BASE_ADDR 0x00000 // Base address of the 512K RAM.
// IO Ports.
#define IO_FDC_STATUS 0x00 // NEC765 FDC Status Register.
#define IO_FDC_DATA 0x01 // NEC765 FDC Data Register.
#define IO_MEMBNK0 0xF0 // Memory bank 0000:3FFF register.
#define IO_MEMBNK1 0xF1 // Memory bank 4000:7FFF register.
#define IO_MEMBNK2 0xF2 // Memory bank 8000:BFFF register.
#define IO_MEMBNK3 0xF3 // Memory bank C000:FFFF register.
#define IO_MEMLOCK 0xF4 // CPC mode memory lock range.
#define IO_ROLLERRAM 0xF5 // Set the Roller RAM address.
#define IO_VORIGIN 0xF6 // Set screen vertical origin.
#define IO_SCREENATTR 0xF7 // Set screen attributes.
#define IO_GACMD 0xF8 // Gatearray command register.
#define IO_GASTATUS 0xF8 // Gatearray status register.
// The boot code for the PCW-8256 is located within the printer controller. To avoid special hardware within the CPLD, this code is incorporated
// into this module for rapid loading into RAM.
#define ROM_DIR "/apps/FusionX/host/PCW/roms/"
#define ROM_PCW8_BOOT_FILENAME ROM_DIR "PCW8256_boot.bin"
#define ROM_PCW9_BOOT_FILENAME ROM_DIR "PCW9256_boot.bin"
// Boot ROM rom load and size definitions.
#define ROM_BOOT_LOAD_ADDR 0x000000
#define ROM_BOOT_SIZE 275
// PCW control.
typedef struct {
uint8_t regMemBank0; // Mirror of register F0, memory block select 0x0000-0x3FFF.
uint8_t regMemBank1; // Mirror of register F1, memory block select 0x4000-0x7FFF.
uint8_t regMemBank2; // Mirror of register F2, memory block select 0x8000-0xBFFF.
uint8_t regMemBank3; // Mirror of register F3, memory block select 0xC000-0xFFFF.
uint8_t regCPCPageMode; // Mirror of the CPC paging lock register F4.
uint8_t regRollerRAM; // Mirror of Roller-RAM address register.
uint8_t regCtrl; // Control register.
} t_PCWCtrl;
// RFS Board control.
static t_PCWCtrl PCWCtrl;
//-------------------------------------------------------------------------------------------------------------------------------
//
//
//-------------------------------------------------------------------------------------------------------------------------------
// Method to setup the memory page config to reflect the PCW configuration.
void pcwSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The PCW contains upto 512KB of standard RAM which can be expnded to a physical max of 2MB. The kernel malloc limit is 2MB so the whole virtual
// memory can be mapped into the PCW memory address range.
// Setup defaults.
PCWCtrl.regMemBank0 = 0x00;
PCWCtrl.regMemBank1 = 0x01;
PCWCtrl.regMemBank2 = 0x02;
PCWCtrl.regMemBank3 = 0x03; // Keyboard is in locations 0x3FF0 - 0x3FFF of this memory block.
PCWCtrl.regCPCPageMode = 0x00;
PCWCtrl.regRollerRAM = 0x00;
PCWCtrl.regCtrl = 0x00;
// Initialise the page pointers and memory to reflect a PCW, lower 128K is used by video logic so must always be accessed in hardware.
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx >= 0x0000 && idx < 0xFFF0)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+idx));
}
if(idx >= 0xFFF0 && idx < 0x10000)
{
// The keyboard is memory mapped into upper bytes of block 3.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, (RAM_BASE_ADDR+idx));
}
}
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
// No I/O Ports on the RFS board.
pr_info("PCW Memory Setup complete.\n");
}
// Method to load a ROM image into the RAM memory.
//
uint8_t loadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->ram[loadAddr], loadSize);
if(noBytes < loadSize)
{
pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module.
void pcwInit(uint8_t mode)
{
// Locals.
//
uint32_t idx;
// Clear memory as previous use or malloc can leave it randomly set.
for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++)
{
Z80Ctrl->ram[idx] = 0x00;
}
// Disable boot mode, we dont need to fetch the boot rom as we preload it.
SPI_SEND32( (0x00F8 << 16) | (0x00 << 8) | CPLD_CMD_WRITEIO_ADDR);
// Load boot ROM.
loadROM(mode == 0 ? ROM_PCW8_BOOT_FILENAME : ROM_PCW9_BOOT_FILENAME, ROM_BOOT_LOAD_ADDR, ROM_BOOT_SIZE);
// Reset.
//SPI_SEND32( (0x00F8 << 16) | (0x01 << 8) | CPLD_CMD_WRITEIO_ADDR);
// First two bytes to NULL as were not using the bootstrap and normal operations after bootstrap would disable the mode.
Z80Ctrl->ram[0] = 0x00;
Z80Ctrl->ram[1] = 0x00;
pr_info("Enabling PCW-%s driver.\n", mode == 0 ? "8256" : "9256");
return;
}
// Perform any de-initialisation when the driver is removed.
void pcwRemove(void)
{
pr_info("Removing PCW driver.\n");
return;
}
// Method to decode an address and make any system memory map changes as required.
//
static inline void pcwDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{
// Locals.
uint32_t idx;
// IO Switch.
if(ioFlag)
{
switch(address&0xff)
{
case IO_FDC_STATUS:
//pr_info("FDC_STATUS:%02x\n", data);
break;
case IO_FDC_DATA:
//pr_info("FDC_DATA:%02x\n", data);
break;
case IO_MEMBNK0:
if(!readFlag)
{
PCWCtrl.regMemBank0 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank0;
pr_info("Setting Bank 0:%02x\n", PCWCtrl.regMemBank0);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x0000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank0 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank0*16384)+idx));
}
}
break;
case IO_MEMBNK1:
if(!readFlag)
{
PCWCtrl.regMemBank1 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank1;
pr_info("Setting Bank 1:%02x\n", PCWCtrl.regMemBank1);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x4000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank1 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank1*16384)+idx));
}
}
break;
case IO_MEMBNK2:
if(!readFlag)
{
PCWCtrl.regMemBank2 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank2;
pr_info("Setting Bank 2:%02x\n", PCWCtrl.regMemBank2);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType((idx+0x8000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank2 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank2*16384)+idx));
}
}
break;
case IO_MEMBNK3:
if(!readFlag)
{
PCWCtrl.regMemBank3 = (data & 0x80) ? data & 0x7f : PCWCtrl.regMemBank3;
pr_info("Setting Bank 3:%02x\n", PCWCtrl.regMemBank3);
for(idx=0x0000; idx < 0x4000; idx+=MEMORY_BLOCK_GRANULARITY)
{
if(idx < 0x3FF0)
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, PCWCtrl.regMemBank3 >= 8 ? MEMORY_TYPE_VIRTUAL_RAM : MEMORY_TYPE_PHYSICAL_RAM_WT, (RAM_BASE_ADDR+(PCWCtrl.regMemBank3*16384)+idx));
}
}
break;
case IO_MEMLOCK:
if(!readFlag)
{
pr_info("MEMLOCK:%02x\n", data);
PCWCtrl.regCPCPageMode = data;
}
break;
case IO_ROLLERRAM:
if(!readFlag)
{
pr_info("********RollerRAM********:%02x => %04x\n", data, (((data >> 5)&0x7) * 16384)+((data&0x1f)*512));
PCWCtrl.regRollerRAM = data;
}
break;
case IO_VORIGIN:
pr_info("VORIGIN:%02x\n", data);
break;
case IO_SCREENATTR:
pr_info("SCREENATTR:%02x\n", data);
break;
case IO_GACMD:
pr_info("GACMD:%02x\n", data);
break;
default:
pr_info("Unknown:ADDR:%02x,%02x\n", address&0xff, data);
break;
}
} else
// Memory map switch.
{
switch(address)
{
default:
break;
}
}
}
// Method to read from either the memory mapped registers if enabled else the RAM.
static inline uint8_t pcwRead(zuint16 address, uint8_t ioFlag)
{
// Locals.
uint8_t data = 0xFF;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
// Return the contents of the ROM at given address.
data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address);
break;
}
}
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3) pr_info("PCW-Read:%04x, BK0:%02x, BK1:%02x, BK2:%02x, BK3:%02x, CTRL:%02x\n", address, PCWCtrl.regMemBank0, PCWCtrl.regMemBank1, PCWCtrl.regMemBank2, PCWCtrl.regMemBank3, PCWCtrl.regCtrl);
#endif
return(data);
}
// Method to handle writes.
static inline void pcwWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
{
// Locals.
// uint32_t idx;
// I/O Operation?
if(ioFlag)
{
switch(address)
{
default:
break;
}
} else
{
switch(address)
{
default:
// Any unprocessed write is commited to RAM.
writeVirtualRAM(address, data);
break;
}
}
#if(DEBUG_ENABLED & 1)
if(Z80Ctrl->debug >= 3) pr_info("PCW-Write:%04x, BK0:%02x, BK1:%02x, BK2:%02x, BK3:%02x, CTRL:%02x\n", address, PCWCtrl.regMemBank0, PCWCtrl.regMemBank1, PCWCtrl.regMemBank2, PCWCtrl.regMemBank3, PCWCtrl.regCtrl);
#endif
return;
}

View File

@@ -67,6 +67,10 @@
#define BNKSELUSER 0xEFFE // Select RFS Bank2 (User ROM) #define BNKSELUSER 0xEFFE // Select RFS Bank2 (User ROM)
#define BNKCTRL 0xEFFF // Bank Control register (read/write). #define BNKCTRL 0xEFFF // Bank Control register (read/write).
// 40/80 Video Module control registers.
//
#define DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
// //
// RFS v2 Control Register constants. // RFS v2 Control Register constants.
// //
@@ -87,7 +91,13 @@
#define BNKCTRLDEF BBMOSI+SDCS+BBCLK // Default on startup for the Bank Control register. #define BNKCTRLDEF BBMOSI+SDCS+BBCLK // Default on startup for the Bank Control register.
// RFS Board ROM rom filename definitions. // RFS Board ROM rom filename definitions.
#if(TARGET_HOST_MZ80A == 1)
#define ROM_DIR "/apps/FusionX/host/MZ-80A/RFS/" #define ROM_DIR "/apps/FusionX/host/MZ-80A/RFS/"
#elif(TARGET_HOST_MZ700 == 1)
#define ROM_DIR "/apps/FusionX/host/MZ-700/RFS/"
#else
#error "Unknown host configured."
#endif
#define ROM_MROM_40C_FILENAME ROM_DIR "MROM_256_40c.bin" #define ROM_MROM_40C_FILENAME ROM_DIR "MROM_256_40c.bin"
#define ROM_USER_I_40C_FILENAME ROM_DIR "USER_ROM_256_40c.bin" #define ROM_USER_I_40C_FILENAME ROM_DIR "USER_ROM_256_40c.bin"
#define ROM_USER_II_40C_FILENAME ROM_DIR "USER_ROM_II_256_40c.bin" #define ROM_USER_II_40C_FILENAME ROM_DIR "USER_ROM_II_256_40c.bin"
@@ -167,6 +177,7 @@ typedef struct {
uint8_t upCntr; // Register enable up counter. uint8_t upCntr; // Register enable up counter.
uint32_t mromAddr; // Actual address in MROM of active bank. uint32_t mromAddr; // Actual address in MROM of active bank.
uint32_t uromAddr; // Actual address in UROM of active bank. uint32_t uromAddr; // Actual address in UROM of active bank.
uint8_t memSwitch; // MZ-80A can relocate the lower 4K ROM by swapping RAM at 0xC000.
t_SDCtrl sd; // SD Control. t_SDCtrl sd; // SD Control.
} t_RFSCtrl; } t_RFSCtrl;
@@ -194,7 +205,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
RFSCtrl.regCtrl = 0x00; RFSCtrl.regCtrl = 0x00;
RFSCtrl.mromAddr = MROM_ADDR; RFSCtrl.mromAddr = MROM_ADDR;
RFSCtrl.uromAddr = USER_ROM_I_ADDR; RFSCtrl.uromAddr = USER_ROM_I_ADDR;
Z80Ctrl->memSwitch = 0; RFSCtrl.memSwitch = 0;
RFSCtrl.sd.trainingCnt = 0; RFSCtrl.sd.trainingCnt = 0;
RFSCtrl.sd.initialised = 0; RFSCtrl.sd.initialised = 0;
RFSCtrl.sd.dataOutFlag = 0; RFSCtrl.sd.dataOutFlag = 0;
@@ -214,7 +225,19 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
{ {
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx)); setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));
} }
if(idx >= 0xE800 && idx < 0xF000) else if(idx >= 0x1000 && idx < 0xD000)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
else if(idx >= 0xD000 && idx < 0xE000)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
else if(idx >= 0xE000 && idx < 0xE800)
{
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_PHYSICAL_HW, idx);
}
else if(idx >= 0xE800 && idx < 0xF000)
{ {
// Memory is both ROM and hardware, the registers share the same address space. // Memory is both ROM and hardware, the registers share the same address space.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_HW, (RFSCtrl.uromAddr+(idx-0xE800))); setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM | MEMORY_TYPE_VIRTUAL_HW, (RFSCtrl.uromAddr+(idx-0xE800)));
@@ -224,6 +247,18 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, (idx+(Z80_VIRTUAL_ROM_SIZE - 0x10000))); setMemoryType((idx/MEMORY_BLOCK_GRANULARITY), MEMORY_TYPE_VIRTUAL_ROM, (idx+(Z80_VIRTUAL_ROM_SIZE - 0x10000)));
} }
} }
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// Enable refresh as using virtual RAM stops refresh of host DRAM.
Z80Ctrl->refreshDRAM = 2;
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// No I/O Ports on the RFS board. // No I/O Ports on the RFS board.
pr_info("RFS Memory Setup complete.\n"); pr_info("RFS Memory Setup complete.\n");
@@ -231,7 +266,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode)
// Method to load a ROM image into the ROM memory. // Method to load a ROM image into the ROM memory.
// //
uint8_t loadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize) uint8_t rfsLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{ {
// Locals. // Locals.
uint8_t result = 0; uint8_t result = 0;
@@ -264,10 +299,10 @@ void rfsInit(uint8_t mode80c)
uint32_t idx; uint32_t idx;
// Load ROMS according to the display configuration, 40 char = standard, 80 char = 40/80 board installed. // Load ROMS according to the display configuration, 40 char = standard, 80 char = 40/80 board installed.
loadROM(mode80c == 0 ? ROM_MROM_40C_FILENAME : ROM_MROM_80C_FILENAME, ROM_MROM_LOAD_ADDR, ROM_MROM_SIZE); rfsLoadROM(mode80c == 0 ? ROM_MROM_40C_FILENAME : ROM_MROM_80C_FILENAME, ROM_MROM_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_I_40C_FILENAME : ROM_USER_I_80C_FILENAME, ROM_USER_I_LOAD_ADDR, ROM_MROM_SIZE); rfsLoadROM(mode80c == 0 ? ROM_USER_I_40C_FILENAME : ROM_USER_I_80C_FILENAME, ROM_USER_I_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_II_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_II_LOAD_ADDR, ROM_MROM_SIZE); rfsLoadROM(mode80c == 0 ? ROM_USER_II_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_II_LOAD_ADDR, ROM_MROM_SIZE);
loadROM(mode80c == 0 ? ROM_USER_III_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_III_LOAD_ADDR, ROM_MROM_SIZE); rfsLoadROM(mode80c == 0 ? ROM_USER_III_40C_FILENAME : ROM_USER_II_80C_FILENAME, ROM_USER_III_LOAD_ADDR, ROM_MROM_SIZE);
// Copy the Floppy ROM to the top portion of ROM. USER III isnt normally used and if it is, 4K will be free. // Copy the Floppy ROM to the top portion of ROM. USER III isnt normally used and if it is, 4K will be free.
for(idx=0xF000; idx < 0x10000; idx++) for(idx=0xF000; idx < 0x10000; idx++)
@@ -276,7 +311,13 @@ void rfsInit(uint8_t mode80c)
while(CPLD_READY() == 0); while(CPLD_READY() == 0);
Z80Ctrl->rom[idx+(Z80_VIRTUAL_ROM_SIZE-0x10000)] = z80io_PRL_Read8(1); Z80Ctrl->rom[idx+(Z80_VIRTUAL_ROM_SIZE-0x10000)] = z80io_PRL_Read8(1);
} }
pr_info("Enabling RFS driver.\n");
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
pr_info("Enabling RFS(%d) driver.\n", mode80c == 1 ? 80 : 40);
return; return;
} }
@@ -304,7 +345,7 @@ static inline void rfsDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx)); setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx)); setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));
} }
Z80Ctrl->memSwitch = 0x01; RFSCtrl.memSwitch = 0x01;
} }
// Reset memory map switch. // Reset memory map switch.
@@ -318,7 +359,7 @@ static inline void rfsDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t
setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx)); setMemoryType((idx+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, (0xC000+idx));
} }
} }
Z80Ctrl->memSwitch = 0x00; RFSCtrl.memSwitch = 0x00;
} }
} }
} }
@@ -755,7 +796,7 @@ static inline void rfsWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
// Update memory map to reflect register change. // Update memory map to reflect register change.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY) for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{ {
if(Z80Ctrl->memSwitch) if(RFSCtrl.memSwitch)
{ {
// Monitor ROM is located at 0xC000. // Monitor ROM is located at 0xC000.
setMemoryType((0xC000+idx)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx)); setMemoryType((0xC000+idx)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx));

View File

@@ -17,6 +17,7 @@
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org> // Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
// //
// History: Feb 2023 v1.0 - Initial write based on the tranZPUter SW hardware. // History: Feb 2023 v1.0 - Initial write based on the tranZPUter SW hardware.
// Apr 2023 v1.1 - Updates & bug fixes.
// //
// Notes: See Makefile to enable/disable conditional components // Notes: See Makefile to enable/disable conditional components
// //
@@ -72,6 +73,9 @@ typedef struct {
// TZPU Board control. // TZPU Board control.
static t_TZPUCtrl TZPUCtrl; static t_TZPUCtrl TZPUCtrl;
// Forward prototypes.
static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag);
//------------------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------------------
// //
// //
@@ -115,15 +119,145 @@ static t_TZPUCtrl TZPUCtrl;
// 30 - All memory and IO are on the tranZPUter board, 64K block 6 selected. // 30 - All memory and IO are on the tranZPUter board, 64K block 6 selected.
// 31 - All memory and IO are on the tranZPUter board, 64K block 7 selected. // 31 - All memory and IO are on the tranZPUter board, 64K block 7 selected.
// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. This sets up the default
// as the memory map changes according to selection and handled in-situ.
void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The tranZPUter SW uses a CPLD to set a 4K Z80 memory range window into a 512K-1MB linear RAM block. The actual map required
// at any one time is governed by the Memory Config register at I/O port 0x60.
// This method sets the initial state, which is a normal Sharp operating mode, all memory and IO (except tranZPUter
// control IO block) are on the mainboard.
// Setup defaults.
TZPUCtrl.clkSrc = 0x00; // Clock defaults to host.
TZPUCtrl.regCmd = 0x00; // Default for the CPLD Command.
TZPUCtrl.regCmdStatus = 0x00; // Default for the CPLD Command Status.
TZPUCtrl.regCpuCfg = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
TZPUCtrl.regCpuInfo = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
// Setup the CPLD status value, this is used by the host for configuration of tzfs.
#if(TARGET_HOST_MZ80A == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ80A;
#endif
#if(TARGET_HOST_MZ700 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ700;
#endif
#if(TARGET_HOST_MZ2000 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ2000;
#endif
TZPUCtrl.regCpldCfg = 0x00; // Not used, as no CPLD available, but need to store/return value if addressed.
// Default memory mode, TZFS.
Z80Ctrl->memoryMode = TZMM_ORIG;
// Reset IO mapping.
for(idx=0x0000; idx < IO_PAGE_SIZE; idx++)
{
Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW;
}
// I/O Ports on the tranZPUter SW board. All hosts have the same ports for the tzpu board.
for(idx=0x0000; idx < IO_PAGE_SIZE; idx+=0x0100)
{
Z80Ctrl->iopage[idx+IO_TZ_CTRLLATCH] = IO_TZ_CTRLLATCH | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SETXMHZ] = IO_TZ_SETXMHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SET2MHZ] = IO_TZ_SET2MHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CLKSELRD] = IO_TZ_CLKSELRD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SVCREQ] = IO_TZ_SVCREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SYSREQ] = IO_TZ_SYSREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCMD] = IO_TZ_CPLDCMD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDSTATUS] = IO_TZ_CPLDSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUCFG] = IO_TZ_CPUCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUSTATUS] = IO_TZ_CPUSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUINFO] = IO_TZ_CPUINFO | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCFG] = IO_TZ_CPLDCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDINFO] = IO_TZ_CPLDINFO | IO_TYPE_VIRTUAL_HW;
}
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
pr_info("TZPU Memory Setup complete.\n");
}
// Method to load a ROM image into the ROM memory.
//
uint8_t tzpuLoadROM(const char* romFileName, uint32_t loadAddr, uint32_t loadSize)
{
// Locals.
uint8_t result = 0;
long noBytes;
struct file *fp;
fp = filp_open(romFileName, O_RDONLY, 0);
if(IS_ERR(fp))
{
pr_info("Error opening ROM Image:%s\n:", romFileName);
result = 1;
} else
{
vfs_llseek(fp, 0, SEEK_SET);
noBytes = kernel_read(fp, fp->f_pos, &Z80Ctrl->rom[loadAddr], loadSize);
if(noBytes < loadSize)
{
// pr_info("Short load, ROM Image:%s, bytes loaded:%08x\n:", romFileName, loadSize);
}
filp_close(fp,NULL);
}
return(result);
}
// Perform any setup operations, such as variable initialisation, to enable use of this module. // Perform any setup operations, such as variable initialisation, to enable use of this module.
void tzpuInit(void) void tzpuInit(void)
{ {
// Setup all initial TZFS memory modes
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_ORIG, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS2, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS3, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS4, 1);
// Ensure memory configuration is correct before requesting K64F to load Bios.
tzpuSetupMemory(USE_VIRTUAL_RAM);
// Default memory mode, TZFS.
Z80Ctrl->memoryMode = TZMM_TZFS;
#if (TARGET_HOST_MZ700 == 1)
// Reset memory paging to default.
SPI_SEND_32(0x00e4, 0x00 << 8 | CPLD_CMD_WRITEIO_ADDR);
#endif
// New memory maps setup, perform a reset so that the K64F CPU loads the required ROMS.
sendSignal(Z80Ctrl->ioTask, SIGUSR1);
pr_info("Enabling TZPU driver.\n"); pr_info("Enabling TZPU driver.\n");
} }
// Perform any de-initialisation when the driver is removed. // Perform any de-initialisation when the driver is removed.
void tzpuRemove(void) void tzpuRemove(void)
{ {
// Locals.
uint32_t idx;
// Go through and clear all memory maps, leave the original page in slot 0.
for(idx=1; idx < MEMORY_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{
kfree(Z80Ctrl->page[idx]);
Z80Ctrl->page[idx] = NULL;
}
}
// Default memory mode, ORIG.
Z80Ctrl->memoryMode = TZMM_ORIG;
pr_info("Removing TZPU driver.\n"); pr_info("Removing TZPU driver.\n");
return; return;
} }
@@ -133,27 +267,154 @@ void tzpuRemove(void)
static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag) static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag)
{ {
// Locals. // Locals.
uint32_t idx;
// I/O or Memory? // I/O or Memory?
if(ioFlag == 0) if(ioFlag == 0)
{ {
// Memory map switch. // #if(DEBUG_ENABLED & 1)
if(readFlag == 0) // if(Z80Ctrl->debug >= 2)
// {
// pr_info("MEM:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Certain machines have memory mapped I/O, these need to be handled in-situ as some reads may change the memory map.
// These updates are made whilst waiting for the CPLD to retrieve the requested byte.
//
// 0000 - 0FFF : MZ80K/A/700 = Monitor ROM or RAM (MZ80A rom swap)
// 1000 - CFFF : MZ80K/A/700 = RAM
// C000 - CFFF : MZ80A = Monitor ROM (MZ80A rom swap)
// D000 - D7FF : MZ80K/A/700 = VRAM
// D800 - DFFF : MZ700 = Colour VRAM (MZ700)
// E000 - E003 : MZ80K/A/700 = 8255
// E004 - E007 : MZ80K/A/700 = 8254
// E008 - E00B : MZ80K/A/700 = LS367
// E00C - E00F : MZ80A = Memory Swap (MZ80A)
// E010 - E013 : MZ80A = Reset Memory Swap (MZ80A)
// E014 : MZ80A/700 = Normat CRT display
// E015 : MZ80A/700 = Reverse CRT display
// E200 - E2FF : MZ80A/700 = VRAM roll up/roll down.
// E800 - EFFF : MZ80K/A/700 = User ROM socket or DD Eprom (MZ700)
// F000 - F7FF : MZ80K/A/700 = Floppy Disk interface.
// F800 - FFFF : MZ80K/A/700 = Floppy Disk interface.
switch(address)
{ {
default:
} else break;
{
} }
} else } else
// I/O Decoding. // I/O Decoding.
{ {
// Only lower 8 bits recognised in the tzpu. // #if(DEBUG_ENABLED & 1)
switch(address & 0xFF) // if(Z80Ctrl->debug >= 2)
// {
// pr_info("IO:%04x,%02x,%d,%d\n", address, data, ioFlag, readFlag);
// }
// #endif
// Determine if this is a memory management port and update the memory page if required.
switch(address & 0x00FF)
{ {
default: // MZ700 memory mode switch.
//
// MZ-700
// |0000:0FFF|1000:CFFF|D000:FFFF
// ------------------------------
// OUT 0xE0 = |DRAM | |
// OUT 0xE1 = | | |DRAM
// OUT 0xE2 = |MONITOR | |
// OUT 0xE3 = | | |Memory Mapped I/O
// OUT 0xE4 = |MONITOR |DRAM |Memory Mapped I/O
// OUT 0xE5 = | | |Inhibit
// OUT 0xE6 = | | |<return>
//
// <return> = Return to the state prior to the complimentary command being invoked.
// Enable lower 4K block as DRAM
case IO_ADDR_E0:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
break; break;
// Enable upper 12K block, including Video/Memory Mapped peripherals area, as DRAM.
case IO_ADDR_E1:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
// MZ-700 mode we only work in first 64K block.
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_RAM, idx);
}
}
break;
// Enable MOnitor ROM in lower 4K block
case IO_ADDR_E2:
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
break;
// Enable Video RAM and Memory mapped peripherals in upper 12K block.
case IO_ADDR_E3:
if(!Z80Ctrl->inhibitMode)
{
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Reset to power on condition memory map.
case IO_ADDR_E4:
// Lower 4K set to Monitor ROM.
for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, idx);
}
if(!Z80Ctrl->inhibitMode)
{
// Upper 12K to hardware.
for(idx=0xD000; idx < 0xE000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx);
}
for(idx=0xE000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_HW, idx);
}
}
break;
// Inhibit. Backup current page data in region 0xD000-0xFFFF and inhibit it.
case IO_ADDR_E5:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
backupMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_INHIBIT, idx);
}
Z80Ctrl->inhibitMode = 1;
break;
// Restore D000-FFFF to its original state.
case IO_ADDR_E6:
for(idx=0xD000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY)
{
restoreMemoryType(idx/MEMORY_BLOCK_GRANULARITY);
}
Z80Ctrl->inhibitMode = 0;
break;
// Port is not a memory management port.
default:
break;
} }
} }
} }
@@ -252,7 +513,9 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
switch(address & 0x00FF) switch(address & 0x00FF)
{ {
case IO_TZ_CTRLLATCH: case IO_TZ_CTRLLATCH:
//pr_info("CTRLLATCH:%02x\n", data); #if(DEBUG_ENABLED & 0x01)
if(Z80Ctrl->debug >=3) pr_info("CTRLLATCH:%02x\n", data);
#endif
// Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define. // Check to see if the memory mode page has been allocated for requested mode, if it hasnt, we need to allocate and then define.
Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1)); Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1));
@@ -270,7 +533,7 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
// A lot of the memory maps below are identical, minor changes such as RAM bank. This is a direct conversion of the VHDL code from the CPLD. // A lot of the memory maps below are identical, minor changes such as RAM bank. This is a direct conversion of the VHDL code from the CPLD.
// //
for(idx=0x0000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) for(idx=0x0000; idx < MEMORY_PAGE_SIZE; idx+=MEMORY_BLOCK_GRANULARITY)
{ {
switch(Z80Ctrl->memoryMode) switch(Z80Ctrl->memoryMode)
{ {
@@ -744,71 +1007,3 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag)
return; return;
} }
// Method to setup the memory page config to reflect installation of a tranZPUter SW Board. This sets up the default
// as the memory map changes according to selection and handled in-situ.
void tzpuSetupMemory(enum Z80_MEMORY_PROFILE mode)
{
// Locals.
uint32_t idx;
// The tranZPUter SW uses a CPLD to set a 4K Z80 memory range window into a 512K-1MB linear RAM block. The actual map required
// at any one time is governed by the Memory Config register at I/O port 0x60.
// This method sets the initial state, which is a normal Sharp operating mode, all memory and IO (except tranZPUter
// control IO block) are on the mainboard.
// Setup defaults.
TZPUCtrl.clkSrc = 0x00; // Clock defaults to host.
TZPUCtrl.regCmd = 0x00; // Default for the CPLD Command.
TZPUCtrl.regCmdStatus = 0x00; // Default for the CPLD Command Status.
TZPUCtrl.regCpuCfg = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
TZPUCtrl.regCpuInfo = 0x00; // Not used, as no FPGA available, but need to store/return value if addressed.
// Setup the CPLD status value, this is used by the host for configuration of tzfs.
#if(TARGET_HOST_MZ80A == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ80A;
#endif
#if(TARGET_HOST_MZ700 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ700;
#endif
#if(TARGET_HOST_MZ2000 == 1)
TZPUCtrl.regCpldInfo = (CPLD_VERSION << 4) | (CPLD_HAS_FPGA_VIDEO << 3) | HWMODE_MZ2000;
#endif
TZPUCtrl.regCpldCfg = 0x00; // Not used, as no CPLD available, but need to store/return value if addressed.
// Go through and clear all memory maps, valid for startup and reset.
for(idx=0; idx < MEMORY_MODES; idx++)
{
if(Z80Ctrl->page[idx] != NULL)
{
kfree(Z80Ctrl->page[idx]);
Z80Ctrl->page[idx] = NULL;
}
}
// Setup all initial TZFS memory modes
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_ORIG, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS2, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS3, 1);
tzpuWrite(IO_TZ_CTRLLATCH, TZMM_TZFS4, 1);
Z80Ctrl->memoryMode = 0x02; // Default memory mode, MZ-80A.
// I/O Ports on the tranZPUter SW board. All hosts have the same ports for the tzpu board.
for(idx=0x0000; idx < 0x10000; idx+=0x0100)
{
Z80Ctrl->iopage[idx+IO_TZ_CTRLLATCH] = IO_TZ_CTRLLATCH | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SETXMHZ] = IO_TZ_SETXMHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SET2MHZ] = IO_TZ_SET2MHZ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CLKSELRD] = IO_TZ_CLKSELRD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SVCREQ] = IO_TZ_SVCREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_SYSREQ] = IO_TZ_SYSREQ | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCMD] = IO_TZ_CPLDCMD | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDSTATUS] = IO_TZ_CPLDSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUCFG] = IO_TZ_CPUCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUSTATUS] = IO_TZ_CPUSTATUS | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPUINFO] = IO_TZ_CPUINFO | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDCFG] = IO_TZ_CPLDCFG | IO_TYPE_VIRTUAL_HW;
Z80Ctrl->iopage[idx+IO_TZ_CPLDINFO] = IO_TZ_CPLDINFO | IO_TYPE_VIRTUAL_HW;
}
pr_info("TZPU Memory Setup complete.\n");
}