diff --git a/.gitignore b/.gitignore index ad660cc2b..6bc873f0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1719,4 +1719,27 @@ software/FusionX/src/z80drv/z80drv.ko software/FusionX/src/z80drv/z80drv.mod.c software/FusionX/host/ 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/ diff --git a/CPLD/v1.0/MZ2000/build/tzpuFusionX_MZ2000.qsf b/CPLD/v1.0/MZ2000/build/tzpuFusionX_MZ2000.qsf index bc46a3d6b..868e14bc4 100644 --- a/CPLD/v1.0/MZ2000/build/tzpuFusionX_MZ2000.qsf +++ b/CPLD/v1.0/MZ2000/build/tzpuFusionX_MZ2000.qsf @@ -127,10 +127,10 @@ set_location_assignment PIN_21 -to VSOM_RSV[1] # SOM Control Signals # =================== 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_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_25 -to VSOM_WAIT set_location_assignment PIN_23 -to VSOM_RESET diff --git a/CPLD/v1.0/MZ2000/tzpuFusionX.vhd b/CPLD/v1.0/MZ2000/tzpuFusionX.vhd index 75b688022..5617a2625 100644 --- a/CPLD/v1.0/MZ2000/tzpuFusionX.vhd +++ b/CPLD/v1.0/MZ2000/tzpuFusionX.vhd @@ -2,16 +2,17 @@ -- -- Name: tzpuFusionX.vhd -- Version: MZ-2000 --- Created: June 2020 +-- Created: Jan 2023 -- Author(s): Philip Smart -- Description: tzpuFusionX CPLD logic definition file. -- This module contains the definition of the tzpuFusionX project plus enhancements -- for the MZ-2000. -- -- Credits: --- Copyright: (c) 2018-22 Philip Smart +-- Copyright: (c) 2018-23 Philip Smart -- --- 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 @@ -128,7 +129,7 @@ end entity; architecture rtl of cpld512 is - -- Finite State Machine states. + -- Z80 Finite State Machine states. type SOMFSMState is ( IdleCycle, @@ -174,6 +175,14 @@ architecture rtl of cpld512 is BusReqCycle ); + -- Controller FSM states. + type CTRLFSMState is + ( + CTRLCMD_Idle, + CTRLCMD_ReadIOWrite, + CTRLCMD_ReadIOWrite_1 + ); + -- CPU Interface internal signals. signal Z80_BUSRQni : std_logic; signal Z80_INTni : std_logic; @@ -185,7 +194,6 @@ architecture rtl of cpld512 is signal Z80_HALTni : std_logic; signal Z80_M1ni : std_logic; signal Z80_RFSHni : std_logic; - signal Z80_DATAi : std_logic_vector(7 downto 0); signal Z80_BUSRQ_ACKni : std_logic; -- Internal CPU state control. @@ -203,9 +211,10 @@ architecture rtl of cpld512 is -- Refresh control. 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 AUTOREFRESH_CNT : integer range 0 to 7; + signal AUTOREFRESH_CNT : integer range 0 to 63; signal FSM_STATUS : std_logic := '0'; signal FSM_CHECK_WAIT : 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_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_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). -- SPI Command interface. 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_PROCESSING : std_logic; 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, -- forcing the SOM to reboot. 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 timerPMReset : integer range 0 to 10 := 0; variable resetCount : integer range 0 to 3 := 0; @@ -311,7 +321,7 @@ begin end if; -- 100ms interval. - if(timer1 = 354000) then + if(timer1 = 400000) then timer100 := timer100 + 1; if(timer100 >= 10) then @@ -340,8 +350,11 @@ begin -- SPI Slave input. Receive command and data from the SOM. SPI_INPUT : process(VSOM_SPI_CLK) 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 - 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 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 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; 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_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA) begin + -- Chip Select inactive, disable process and reset control flags. if(VSOM_SPI_CSn = '1') then SPI_SHIFT_EN <= '0'; - SPI_BIT_CNT <= 15; + SPI_BIT_CNT <= 7; -- 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 + -- Each clock reset the shift enable and done flag in preparation for the next cycle. SPI_SHIFT_EN <= '1'; + -- Bit count decrements to detect when last bit of byte is sent. if(SPI_BIT_CNT > 0) then SPI_BIT_CNT <= SPI_BIT_CNT - 1; end if; + -- Shift out the next bit. VSOM_SPI_MISO <= SPI_TX_SREG(6); SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0'; @@ -392,6 +409,7 @@ begin SPI_FRAME_CNT<= 1; VSOM_SPI_MISO<= SPI_TX_DATA(7); 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 SPI_FRAME_CNT<= 2; VSOM_SPI_MISO<= SPI_TX_DATA(15); @@ -400,13 +418,14 @@ begin SPI_FRAME_CNT<= 3; VSOM_SPI_MISO<= SPI_TX_DATA(23); 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. SPI_FRAME_CNT<= 4; VSOM_SPI_MISO<= SPI_TX_DATA(31); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); + else + SPI_FRAME_CNT<= 0; end if; - SPI_BIT_CNT <= 7; end if; end if; @@ -424,6 +443,7 @@ begin AUTOREFRESH <= '0'; SPI_LOOPBACK_TEST <= '0'; SOM_CMD <= (others => '0'); + SOM_PARAM_CNT <= 0; SPI_CPU_ADDR <= (others => '0'); SPI_NEW_DATA <= '0'; @@ -432,6 +452,7 @@ begin -- for 8bit, 16bit and 32bit transmissions. -- 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> -- < 16bit Z80 Address > < Z80 Data > -- 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,66 +462,110 @@ begin -- elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then - -- Command is always located in the upper byte of frame 1. - SOM_CMD <= SPI_RX_DATA(7 downto 0); + -- 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; - -- Toggle flag to indicate new data arrived. - SPI_NEW_DATA <= not SPI_NEW_DATA; + -- Process if command, store parameters. + if(SOM_PARAM_CNT = 0) then + -- Command is always located in the upper byte of frame 1. + SOM_CMD <= SPI_RX_DATA(7 downto 0); - -- Process the command. Some commands require the FSM, others can be serviced immediately. - case SPI_RX_DATA(7 downto 0) is + -- Toggle flag to indicate new data arrived. + SPI_NEW_DATA <= not SPI_NEW_DATA; - -- Z80XACT(0..15): Setup data and address as provided then execute FSM. - 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"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" => + -- Process the command. Some commands require the FSM, others can be serviced immediately. + case SPI_RX_DATA(7 downto 0) is - -- Direct address set. - if(SPI_FRAME_CNT = 4) then - SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); - 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))); - -- end if; - end if; + -- Z80XACT(0..15): Setup data and address as provided then execute FSM. + 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"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read + X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => - if(SPI_FRAME_CNT > 1) then - SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); - end if; + -- Direct address set. + if(SPI_FRAME_CNT = 4) then + SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); + else + SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); + end if; - -- SETSIGSET1: Set control lines directly. - when X"F0" => - VIDEO_SRCi <= SPI_RX_DATA(8); - MONO_VIDEO_SRCi <= SPI_RX_DATA(9); - AUDIO_SRC_Li <= SPI_RX_DATA(10); - AUDIO_SRC_Ri <= SPI_RX_DATA(11); - VBUS_ENi <= SPI_RX_DATA(12); - VGA_BLANKn <= not SPI_RX_DATA(13); + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- Enable auto refresh DRAM cycle. - when X"F1" => - AUTOREFRESH <= '1'; + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO - -- Disable auto refresh DRAM cycle. - when X"F2" => - AUTOREFRESH <= '0'; + -- 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; - -- SETLOOPBACK: Enable loopback test mode. - when X"FE" => - SPI_LOOPBACK_TEST<= '1'; + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- No action, called to retrieve status. - when X"00" | X"FF" => + 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 - when others => - end case; + -- 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. + when X"F0" => + VIDEO_SRCi <= SPI_RX_DATA(8); + MONO_VIDEO_SRCi <= SPI_RX_DATA(9); + AUDIO_SRC_Li <= SPI_RX_DATA(10); + AUDIO_SRC_Ri <= SPI_RX_DATA(11); + VBUS_ENi <= SPI_RX_DATA(12); + VGA_BLANKn <= not SPI_RX_DATA(13); + + -- Enable auto refresh DRAM cycle. + when X"F1" => + AUTOREFRESH <= '1'; + + -- Disable auto refresh DRAM cycle. + when X"F2" => + AUTOREFRESH <= '0'; + + -- SETLOOPBACK: Enable loopback test mode. + when X"FE" => + SPI_LOOPBACK_TEST<= '1'; + + -- No action, called to retrieve status. + when X"00" | X"FF" => + + when others => + 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 process; @@ -561,9 +626,9 @@ begin CPU_DATA_EN <= '0'; CPU_DATA_IN <= (others => '0'); REFRESH_ADDR <= (others => '0'); - AUTOREFRESH_CNT <= 7; + AUTOREFRESH_CNT <= 63; IPAR <= (others => '0'); - NEW_SPI_CMD <= '0'; + NEW_SPI_DATA <= '0'; VCPU_CS_EDGE <= "11"; SPI_PROCESSING <= '0'; @@ -580,98 +645,12 @@ begin -- 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 - NEW_SPI_CMD <= '1'; + NEW_SPI_DATA <= '1'; end if; - -- 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 - CPU_DATA_EN <= '0'; - Z80_MREQni <= '1'; - Z80_IORQni <= '1'; - Z80_RDni <= '1'; - Z80_WRni <= '1'; - Z80_M1ni <= '1'; - FSM_STATUS <= '0'; - Z80_RFSHni <= '1'; - - -- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. - -- This will be overriden if the SPI receives a new command. - -- - if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then - AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1; - if(AUTOREFRESH_CNT = 0) then - FSM_STATE <= RefreshCycle_3; - end if; - end if; - end if; - - -- 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 - NEW_SPI_CMD <= '0'; - - -- Store new address and data for this command. - CPU_ADDR <= SPI_CPU_ADDR; - if(SPI_CPU_DATA /= CPU_DATA_OUT) then - CPU_DATA_OUT <= SPI_CPU_DATA; - end if; - - -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM - -- data prior to this execution block, which fires 1 cycle later on the same control clock. - -- If the command is not for the FSM then the READY mechanism is held for one - -- further cycle before going inactive. - case SOM_CMD is - when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => - -- Initiate a Fetch Cycle. - FSM_STATE <= FetchCycle; - - when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => - - -- Set the Z80 data bus value and initiate a Write Cycle. - FSM_STATE <= WriteCycle; - - when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => - -- Initiate a Read Cycle. - FSM_STATE <= ReadCycle; - - when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => - -- Set the Z80 data bus value and initiate an IO Write Cycle. - -- The SOM should set 15:8 to the B register value. - FSM_STATE <= WriteIOCycle; - - when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => - -- Initiate a Read IO Cycle. - FSM_STATE <= ReadIOCycle; - - when X"50" => - -- Register a Halt state. - FSM_STATE <= HaltCycle; - - when X"51" => - -- Initiate a refresh cycle. - FSM_STATE <= RefreshCycle_3; - - when X"E0" => - -- Initiate a Halt Cycle. - FSM_STATE <= HaltCycle; - - -- Set the Refresh Address register. - when X"E1" => - REFRESH_ADDR <= CPU_DATA_OUT; - - -- Set the Interrupt Page Address Register. - when X"E2" => - IPAR <= CPU_DATA_OUT; - - when others => - end case; - - -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. - if(SPI_NEW_DATA /= SPI_PROCESSING) then - SPI_PROCESSING <= not SPI_PROCESSING; - end if; - - -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. - FSM_STATUS <= '1'; + -- 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. @@ -686,7 +665,143 @@ begin FSM_WAIT_ACTIVE <= '1'; end if; - -- On each Z80 edge we advance the FSM to recreate the Z80 external signal transactions. + -- 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_RE = '1') then + CPU_DATA_EN <= '0'; + Z80_MREQni <= '1'; + Z80_IORQni <= '1'; + Z80_RDni <= '1'; + Z80_WRni <= '1'; + Z80_M1ni <= '1'; + FSM_STATUS <= '0'; + Z80_RFSHni <= '1'; + + -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences. + -- This period may be extended if the SPI receives a new command. + -- + if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then + if(AUTOREFRESH_CNT = 0) then + FSM_STATE <= RefreshCycle_3; + FSM_STATUS<= '1'; + -- 4164 DRAM = 256 cycles in 4ms. + AUTOREFRESH_CNT <= 63; + 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_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + + -- Store new address and data for this command. + if(NEW_SPI_DATA = '1') then + CPU_ADDR <= SPI_CPU_ADDR; + CPU_DATA_OUT <= SPI_CPU_DATA; + end if; + + -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM + -- data prior to this execution block, which fires 1 cycle later on the same control clock. + -- If the command is not for the FSM then the READY mechanism is held for one + -- further cycle before going inactive. + case SOM_CMD is + when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => + -- Initiate a Fetch Cycle. + FSM_STATE <= FetchCycle; + + when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => + + -- Set the Z80 data bus value and initiate a Write Cycle. + FSM_STATE <= WriteCycle; + + when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => + -- Initiate a Read Cycle. + FSM_STATE <= ReadCycle; + + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => + -- Set the Z80 data bus value and initiate an IO Write Cycle. + -- The SOM should set 15:8 to the B register value. + FSM_STATE <= WriteIOCycle; + + when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => + -- Initiate a Read IO Cycle. + 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" => + -- Register a Halt state. + FSM_STATE <= HaltCycle; + + when X"51" => + -- Initiate a refresh cycle. + FSM_STATE <= RefreshCycle_3; + + when X"E0" => + -- Initiate a Halt Cycle. + FSM_STATE <= HaltCycle; + + -- Set the Refresh Address register. + when X"E1" => + REFRESH_ADDR <= CPU_DATA_OUT; + + -- Set the Interrupt Page Address Register. + when X"E2" => + IPAR <= CPU_DATA_OUT; + + when others => + end case; + + -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. + if(SPI_NEW_DATA /= SPI_PROCESSING) then + SPI_PROCESSING<= not SPI_PROCESSING; + 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 <= '1'; + end if; + + when CTRLCMD_ReadIOWrite => + if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + NEW_SPI_DATA <= '0'; + CPU_DATA_EN <= '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; + + 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. @@ -704,7 +819,7 @@ begin when IdleCycle => CPU_LAST_T_STATE <= '1'; - -- FSM_STATE <= IdleCycle; + FSM_STATUS <= '0'; ----------------------------- -- Z80 Fetch Cycle. @@ -727,7 +842,7 @@ begin when FetchCycle_30 => -- 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; ----------------------------- @@ -736,18 +851,18 @@ begin when RefreshCycle => -- Latch data from mainboard. CPU_DATA_IN <= Z80_DATA; - FSM_STATUS <= '0'; Z80_RFSHni <= '0'; when RefreshCycle_11 => -- Falling edge of T3 activates the MREQ line. Z80_MREQni <= '0'; + FSM_STATUS <= '0'; when RefreshCycle_20 => when RefreshCycle_21 => Z80_MREQni <= '1'; - REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1; + REFRESH_ADDR <= REFRESH_ADDR + 1; FSM_STATE <= IdleCycle; when RefreshCycle_3 => @@ -769,7 +884,7 @@ begin when WriteCycle_21 => Z80_WRni <= '0'; if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= WriteCycle_20; + FSM_STATE <= WriteCycle_20; end if; when WriteCycle_30 => @@ -794,7 +909,7 @@ begin when ReadCycle_21 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= ReadCycle_20; + FSM_STATE <= ReadCycle_20; end if; when ReadCycle_30 => @@ -805,7 +920,6 @@ begin FSM_STATUS <= '0'; FSM_STATE <= IdleCycle; - ----------------------------- -- Z80 IO Write Cycle. ----------------------------- @@ -825,7 +939,7 @@ begin when WriteIOCycle_31 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= WriteIOCycle_20; + FSM_STATE <= WriteIOCycle_30; end if; when WriteIOCycle_40 => @@ -854,7 +968,7 @@ begin when ReadIOCycle_31 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= ReadIOCycle_20; + FSM_STATE <= ReadIOCycle_30; end if; when ReadIOCycle_40 => @@ -902,8 +1016,6 @@ begin Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' else (others => 'Z'); - -- Z80_DATAi <= Z80_DATA when Z80_RDn = '0' - -- else (others => '1'); Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' else 'Z'; Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' @@ -944,7 +1056,7 @@ begin -- Signal mirrors. VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING - else '1'; -- FSM Ready (1), Busy (0) + else '1'; -- FSM Ready (1), Busy (0) VSOM_LTSTATE <= '1' when CPU_LAST_T_STATE = '1' -- Last T-State in current cycle. else '0'; VSOM_BUSRQ <= not Z80_BUSRQn; -- Host device requesting Z80 Bus. @@ -981,5 +1093,9 @@ begin -- DAC clocks. --VGA_PXL_CLK <= CLK_50M; MONO_PXL_CLK <= VGA_PXL_CLK; + + -- Currently unassigned. + VGA_COLR <= '0'; + MONO_RSV <= '0'; end architecture; diff --git a/CPLD/v1.0/MZ700/build/tzpuFusionX_MZ700.qsf b/CPLD/v1.0/MZ700/build/tzpuFusionX_MZ700.qsf index 934556f84..c7266a7c0 100644 --- a/CPLD/v1.0/MZ700/build/tzpuFusionX_MZ700.qsf +++ b/CPLD/v1.0/MZ700/build/tzpuFusionX_MZ700.qsf @@ -127,10 +127,10 @@ set_location_assignment PIN_21 -to VSOM_RSV[1] # SOM Control Signals # =================== 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_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_25 -to VSOM_WAIT set_location_assignment PIN_23 -to VSOM_RESET diff --git a/CPLD/v1.0/MZ700/tzpuFusionX.vhd b/CPLD/v1.0/MZ700/tzpuFusionX.vhd index e031e0519..f20cd260f 100644 --- a/CPLD/v1.0/MZ700/tzpuFusionX.vhd +++ b/CPLD/v1.0/MZ700/tzpuFusionX.vhd @@ -2,16 +2,17 @@ -- -- Name: tzpuFusionX.vhd -- Version: MZ-700 --- Created: June 2020 +-- Created: Jan 2023 -- Author(s): Philip Smart -- Description: tzpuFusionX CPLD logic definition file. -- This module contains the definition of the tzpuFusionX project plus enhancements --- for the MZ700. +-- for the MZ-700. -- -- Credits: --- Copyright: (c) 2018-22 Philip Smart +-- Copyright: (c) 2018-23 Philip Smart -- --- 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 @@ -128,42 +129,62 @@ end entity; architecture rtl of cpld512 is - -- Finite State Machine states. + -- Z80 Finite State Machine states. type SOMFSMState is ( IdleCycle, FetchCycle, - FetchCycle_2, - FetchCycle_3, - FetchCycle_4, + FetchCycle_11, + FetchCycle_20, + FetchCycle_21, + FetchCycle_30, RefreshCycle, - RefreshCycle_1, - RefreshCycle_2, + RefreshCycle_11, + RefreshCycle_20, + RefreshCycle_21, RefreshCycle_3, WriteCycle, - WriteCycle_2, - WriteCycle_3, - WriteCycle_4, + WriteCycle_11, + WriteCycle_20, + WriteCycle_21, + WriteCycle_30, + WriteCycle_31, ReadCycle, - ReadCycle_2, - ReadCycle_3, - ReadCycle_4, + ReadCycle_11, + ReadCycle_20, + ReadCycle_21, + ReadCycle_30, + ReadCycle_31, WriteIOCycle, - WriteIOCycle_2, - WriteIOCycle_3, - WriteIOCycle_4, - WriteIOCycle_5, + WriteIOCycle_11, + WriteIOCycle_20, + WriteIOCycle_21, + WriteIOCycle_30, + WriteIOCycle_31, + WriteIOCycle_40, + WriteIOCycle_41, ReadIOCycle, - ReadIOCycle_2, - ReadIOCycle_3, - ReadIOCycle_4, + ReadIOCycle_11, + ReadIOCycle_20, + ReadIOCycle_21, + ReadIOCycle_30, + ReadIOCycle_31, + ReadIOCycle_40, + ReadIOCycle_41, HaltCycle, BusReqCycle ); + -- Controller FSM states. + type CTRLFSMState is + ( + CTRLCMD_Idle, + CTRLCMD_ReadIOWrite, + CTRLCMD_ReadIOWrite_1 + ); + -- CPU Interface internal signals. signal Z80_BUSRQni : std_logic; - signal Z80_BUSAKni : std_logic; signal Z80_INTni : std_logic; signal Z80_IORQni : std_logic; signal Z80_MREQni : std_logic; @@ -171,10 +192,8 @@ architecture rtl of cpld512 is signal Z80_RDni : std_logic; signal Z80_WRni : std_logic; signal Z80_HALTni : std_logic; - signal Z80_WAITni : std_logic; signal Z80_M1ni : std_logic; signal Z80_RFSHni : std_logic; - signal Z80_DATAi : std_logic_vector(7 downto 0); signal Z80_BUSRQ_ACKni : std_logic; -- Internal CPU state control. @@ -192,22 +211,24 @@ architecture rtl of cpld512 is -- Refresh control. 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 AUTOREFRESH_CNT : integer range 0 to 7; + signal AUTOREFRESH_CNT : integer range 0 to 63; 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 REFRESH_ADDR : std_logic_vector(7 downto 0); signal IPAR : std_logic_vector(7 downto 0); signal AUTOREFRESH : std_logic; -- Clock edge detection and flagging. - signal Z80_CLK_RE : std_logic := '0'; - signal Z80_CLK_FE : std_logic := '0'; - signal Z80_CLK_LEVEL : std_logic := '0'; - signal Z80_CLK_LAST : std_logic := '0'; - signal CPU_T_STATE : integer range 0 to 5; - signal CPU_T_STATES : integer range 0 to 5; + signal Z80_CLKi : std_logic; + signal Z80_CLK_LAST : std_logic_vector(1 downto 0); + signal Z80_CLK_RE : std_logic; + signal Z80_CLK_FE : std_logic; + signal Z80_CLK_TGL : std_logic; signal CPU_T_STATE_SET : integer range 0 to 5; 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_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_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). -- SPI Command interface. 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_PROCESSING : std_logic; 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 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. - SYSRESET: process( Z80_CLK, Z80_RESETn ) + SYSRESET: process( Z80_CLKi, Z80_RESETn ) variable timer1 : integer range 0 to 354000 := 0; variable timer100 : integer range 0 to 10 := 0; variable timerPMReset : integer range 0 to 10 := 0; @@ -264,7 +286,7 @@ begin variable cpuResetEdge : std_logic := '1'; begin -- 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(timerPMReset = 0 and PM_RESETi = '1') then @@ -322,8 +344,11 @@ begin -- SPI Slave input. Receive command and data from the SOM. SPI_INPUT : process(VSOM_SPI_CLK) 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 - 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 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 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; 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_OUTPUT : process(VSOM_SPI_CLK,VSOM_SPI_CSn,SPI_TX_DATA) begin + -- Chip Select inactive, disable process and reset control flags. if(VSOM_SPI_CSn = '1') then SPI_SHIFT_EN <= '0'; - SPI_BIT_CNT <= 15; + SPI_BIT_CNT <= 7; -- 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 + -- Each clock reset the shift enable and done flag in preparation for the next cycle. SPI_SHIFT_EN <= '1'; + -- Bit count decrements to detect when last bit of byte is sent. if(SPI_BIT_CNT > 0) then SPI_BIT_CNT <= SPI_BIT_CNT - 1; end if; + -- Shift out the next bit. VSOM_SPI_MISO <= SPI_TX_SREG(6); SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0'; @@ -374,6 +403,7 @@ begin SPI_FRAME_CNT<= 1; VSOM_SPI_MISO<= SPI_TX_DATA(7); 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 SPI_FRAME_CNT<= 2; VSOM_SPI_MISO<= SPI_TX_DATA(15); @@ -382,13 +412,14 @@ begin SPI_FRAME_CNT<= 3; VSOM_SPI_MISO<= SPI_TX_DATA(23); 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. SPI_FRAME_CNT<= 4; VSOM_SPI_MISO<= SPI_TX_DATA(31); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); + else + SPI_FRAME_CNT<= 0; end if; - SPI_BIT_CNT <= 7; end if; end if; @@ -406,6 +437,7 @@ begin AUTOREFRESH <= '1'; SPI_LOOPBACK_TEST <= '0'; SOM_CMD <= (others => '0'); + SOM_PARAM_CNT <= 0; SPI_CPU_ADDR <= (others => '0'); SPI_NEW_DATA <= '0'; @@ -414,125 +446,153 @@ begin -- for 8bit, 16bit and 32bit transmissions. -- 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> - -- < 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 + -- < 16bit Z80 Address > < Z80 Data > + -- 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 > - -- 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 + -- < > < Data > + -- 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 - -- Command is always located in the upper byte of frame 1. - SOM_CMD <= SPI_RX_DATA(7 downto 0); + -- 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; - -- Toggle flag to indicate new data arrived. - SPI_NEW_DATA <= not SPI_NEW_DATA; + -- Process if command, store parameters. + if(SOM_PARAM_CNT = 0) then + -- Command is always located in the upper byte of frame 1. + SOM_CMD <= SPI_RX_DATA(7 downto 0); - -- Process the command. Some commands require the FSM, others can be serviced immediately. - case SPI_RX_DATA(7 downto 0) is + -- Toggle flag to indicate new data arrived. + SPI_NEW_DATA <= not SPI_NEW_DATA; - -- Z80XACT(0..15): Setup data and address as provided then execute FSM. - 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"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" => + -- Process the command. Some commands require the FSM, others can be serviced immediately. + case SPI_RX_DATA(7 downto 0) is - -- Direct address set. - if(SPI_FRAME_CNT = 4) then - SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); - else - SPI_CPU_ADDR <= std_logic_vector(unsigned(CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); - end if; + -- Z80XACT(0..15): Setup data and address as provided then execute FSM. + 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"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read + X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => - if(SPI_FRAME_CNT > 1) then - SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); - end if; + -- Direct address set. + if(SPI_FRAME_CNT = 4) then + SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); + else + SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); + end if; - -- SETSIGSET1: Set control lines directly. - when X"F0" => - VIDEO_SRCi <= SPI_RX_DATA(8); - MONO_VIDEO_SRCi <= SPI_RX_DATA(9); - AUDIO_SRC_Li <= SPI_RX_DATA(10); - AUDIO_SRC_Ri <= SPI_RX_DATA(11); - VBUS_ENi <= SPI_RX_DATA(12); - VGA_BLANKn <= not SPI_RX_DATA(13); + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- Enable auto refresh DRAM cycle. - when X"F1" => - AUTOREFRESH <= '1'; + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO - -- Disable auto refresh DRAM cycle. - when X"F2" => - AUTOREFRESH <= '0'; + -- 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; - -- SETLOOPBACK: Enable loopback test mode. - when X"FE" => - SPI_LOOPBACK_TEST <= '1'; + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- No action, called to retrieve status. - when X"00" | X"FF" => + 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 - when others => - end case; + -- 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. + when X"F0" => + VIDEO_SRCi <= SPI_RX_DATA(8); + MONO_VIDEO_SRCi <= SPI_RX_DATA(9); + AUDIO_SRC_Li <= SPI_RX_DATA(10); + AUDIO_SRC_Ri <= SPI_RX_DATA(11); + VBUS_ENi <= SPI_RX_DATA(12); + VGA_BLANKn <= not SPI_RX_DATA(13); + + -- Enable auto refresh DRAM cycle. + when X"F1" => + AUTOREFRESH <= '1'; + + -- Disable auto refresh DRAM cycle. + when X"F2" => + AUTOREFRESH <= '0'; + + -- SETLOOPBACK: Enable loopback test mode. + when X"FE" => + SPI_LOOPBACK_TEST<= '1'; + + -- No action, called to retrieve status. + when X"00" | X"FF" => + + when others => + 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 process; - -- Z80 Clock edge detection. Each falling and rising edge sets a flag for 1 cycle along with the current clock level flag. - -- 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 ) + -- Process to detect the Z80 Clock edges. Each edge is used to recreate the Z80 external signals. + -- + Z80CLK: process( CLK_50M, Z80_CLKi, Z80_RESETn ) begin if(Z80_RESETn = '0') then - Z80_CLK_RE <= '0'; - Z80_CLK_FE <= '0'; - Z80_CLK_LEVEL <= '0'; - CPU_T_STATE <= 1; - CPU_LAST_T_STATE <= '0'; + Z80_CLK_RE <= '1'; + Z80_CLK_FE <= '1'; + Z80_CLK_TGL <= '1'; 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. - 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; + -- Default is to clear the signals, only active for 1 clock period. + Z80_CLK_RE <= '0'; + Z80_CLK_FE <= '0'; + Z80_CLK_TGL <= '0'; - end if; + -- Rising Edge. + if(Z80_CLKi = '1' and Z80_CLK_LAST = "00") then + Z80_CLK_RE <= '1'; - elsif(Z80_CLK = '0' and Z80_CLK_LAST = '1') then - Z80_CLK_FE <= '1'; - Z80_CLK_LEVEL <= '0'; + -- 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 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_FE <= '0'; + -- Falling Edge. + elsif(Z80_CLKi = '0' and Z80_CLK_LAST = "11") then + Z80_CLK_FE <= '1'; + Z80_CLK_TGL <= '1'; end if; - - -- Mechanism to set the T-State. - if(CPU_T_STATE_SET /= 0) then - CPU_T_STATE <= CPU_T_STATE_SET; - CPU_LAST_T_STATE <= '0'; - end if; - - Z80_CLK_LAST <= Z80_CLK; + Z80_CLK_LAST <= Z80_CLK_LAST(0) & Z80_CLKi; end if; 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. -- 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 if(Z80_RESETn = '0') then - Z80_BUSAKni <= '1'; Z80_IORQni <= '1'; Z80_MREQni <= '1'; Z80_RDni <= '1'; @@ -553,25 +612,22 @@ begin Z80_M1ni <= '1'; Z80_RFSHni <= '1'; Z80_BUSRQ_ACKni <= '1'; + FSM_CHECK_WAIT <= '0'; + FSM_WAIT_ACTIVE <= '0'; FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; RFSH_STATUS <= '0'; CPU_DATA_EN <= '0'; CPU_DATA_IN <= (others => '0'); REFRESH_ADDR <= (others => '0'); - AUTOREFRESH_CNT <= 7; + AUTOREFRESH_CNT <= 63; IPAR <= (others => '0'); - FSM_STATE <= IdleCycle; - NEW_SPI_CMD <= '0'; + NEW_SPI_DATA <= '0'; VCPU_CS_EDGE <= "11"; - CPU_T_STATES <= 3; - CPU_T_STATE_SET <= 0; SPI_PROCESSING <= '0'; 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 -- suspends processing and tri-states the bus. 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. 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; -- 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'; Z80_MREQni <= '1'; Z80_IORQni <= '1'; @@ -597,316 +670,337 @@ begin FSM_STATUS <= '0'; Z80_RFSHni <= '1'; - -- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. - -- This will be overriden if the SPI receives a new command. + -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences. + -- This period may be extended if the SPI receives a new command. -- if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then - AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1; if(AUTOREFRESH_CNT = 0) then FSM_STATE <= RefreshCycle_3; + FSM_STATUS<= '1'; + -- 4164 DRAM = 256 cycles in 4ms. + AUTOREFRESH_CNT <= 63; end if; end if; end if; - -- 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 - NEW_SPI_CMD <= '0'; + -------------------------------------------------------------------------------------------- + -- CPLD Macro Command Finite State Machine. + -------------------------------------------------------------------------------------------- - -- Store new address and data for this command. - CPU_ADDR <= SPI_CPU_ADDR; - if(SPI_CPU_DATA /= CPU_DATA_OUT) then - CPU_DATA_OUT <= SPI_CPU_DATA; + -- 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_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + + -- Store new address and data for this command. + if(NEW_SPI_DATA = '1') then + CPU_ADDR <= SPI_CPU_ADDR; + CPU_DATA_OUT <= SPI_CPU_DATA; + end if; + + -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM + -- data prior to this execution block, which fires 1 cycle later on the same control clock. + -- If the command is not for the FSM then the READY mechanism is held for one + -- further cycle before going inactive. + case SOM_CMD is + when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => + -- Initiate a Fetch Cycle. + FSM_STATE <= FetchCycle; + + when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => + + -- Set the Z80 data bus value and initiate a Write Cycle. + FSM_STATE <= WriteCycle; + + when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => + -- Initiate a Read Cycle. + FSM_STATE <= ReadCycle; + + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => + -- Set the Z80 data bus value and initiate an IO Write Cycle. + -- The SOM should set 15:8 to the B register value. + FSM_STATE <= WriteIOCycle; + + when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => + -- Initiate a Read IO Cycle. + 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" => + -- Register a Halt state. + FSM_STATE <= HaltCycle; + + when X"51" => + -- Initiate a refresh cycle. + FSM_STATE <= RefreshCycle_3; + + when X"E0" => + -- Initiate a Halt Cycle. + FSM_STATE <= HaltCycle; + + -- Set the Refresh Address register. + when X"E1" => + REFRESH_ADDR <= CPU_DATA_OUT; + + -- Set the Interrupt Page Address Register. + when X"E2" => + IPAR <= CPU_DATA_OUT; + + when others => + end case; + + -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. + if(SPI_NEW_DATA /= SPI_PROCESSING) then + SPI_PROCESSING<= not SPI_PROCESSING; + 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 <= '1'; + end if; + + when CTRLCMD_ReadIOWrite => + if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + NEW_SPI_DATA <= '0'; + CPU_DATA_EN <= '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; + + 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; - -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM - -- data prior to this execution block, which fires 1 cycle later on the same control clock. - -- If the command is not for the FSM then the READY mechanism is held for one - -- further cycle before going inactive. - case SOM_CMD is - when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => - -- Initiate a Fetch Cycle. - FSM_STATE <= FetchCycle; + -- Half cycle expired so we dont check the Z80 wait again. + FSM_CHECK_WAIT <= '0'; + FSM_WAIT_ACTIVE <= '0'; - when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => + -- FSM to implement all the required Z80 cycles. + -- + case FSM_STATE is - -- Set the Z80 data bus value and initiate a Write Cycle. - FSM_STATE <= WriteCycle; + when IdleCycle => + CPU_LAST_T_STATE <= '1'; + FSM_STATUS <= '0'; - when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => - -- Initiate a Read Cycle. - FSM_STATE <= ReadCycle; + ----------------------------- + -- Z80 Fetch Cycle. + ----------------------------- + when FetchCycle => + Z80_M1ni <= '0'; - when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => - -- Set the Z80 data bus value and initiate an IO Write Cycle. - -- The SOM should set 15:8 to the B register value. - FSM_STATE <= WriteIOCycle; + when FetchCycle_11 => + Z80_M1ni <= '0'; + Z80_MREQni <= '0'; + Z80_RDni <= '0'; - when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => - -- Initiate a Read IO Cycle. - FSM_STATE <= ReadIOCycle; + when FetchCycle_20 => + FSM_CHECK_WAIT <= '1'; - when X"50" => - -- Register a Halt state. - FSM_STATE <= HaltCycle; + when FetchCycle_21 => + if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then + FSM_STATE <= FetchCycle_20; + end if; - when X"51" => - -- Initiate a refresh cycle. - FSM_STATE <= RefreshCycle_3; + when FetchCycle_30 => + -- 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. + FSM_STATE <= RefreshCycle; - when X"E0" => - -- Initiate a Halt Cycle. - FSM_STATE <= HaltCycle; + ----------------------------- + -- Z80 Refresh Cycle. + ----------------------------- + when RefreshCycle => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + Z80_RFSHni <= '0'; - -- Set the Refresh Address register. - when X"E1" => - REFRESH_ADDR <= CPU_DATA_OUT; + when RefreshCycle_11 => + -- Falling edge of T3 activates the MREQ line. + Z80_MREQni <= '0'; + FSM_STATUS <= '0'; - -- Set the Interrupt Page Address Register. - when X"E2" => - IPAR <= CPU_DATA_OUT; + when RefreshCycle_20 => + + when RefreshCycle_21 => + Z80_MREQni <= '1'; + REFRESH_ADDR <= REFRESH_ADDR + 1; + FSM_STATE <= IdleCycle; + + when RefreshCycle_3 => + Z80_RFSHni <= '0'; + FSM_STATE <= RefreshCycle_11; + + ----------------------------- + -- Z80 Write Cycle. + ----------------------------- + when WriteCycle => + + when WriteCycle_11 => + Z80_MREQni <= '0'; + CPU_DATA_EN <= '1'; + + when WriteCycle_20 => + FSM_CHECK_WAIT <= '1'; + + when WriteCycle_21 => + Z80_WRni <= '0'; + if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then + FSM_STATE <= WriteCycle_20; + end if; + + when WriteCycle_30 => + + when WriteCycle_31 => + FSM_STATUS <= '0'; + Z80_MREQni <= '1'; + Z80_WRni <= '1'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 Read Cycle. + ----------------------------- + when ReadCycle => + + when ReadCycle_11 => + Z80_MREQni <= '0'; + Z80_RDni <= '0'; + + 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; + + when ReadCycle_30 => + + when ReadCycle_31 => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 IO Write Cycle. + ----------------------------- + when WriteIOCycle => + + when WriteIOCycle_11 => + CPU_DATA_EN <= '1'; + + when WriteIOCycle_20 => + Z80_IORQni <= '0'; + Z80_WRni <= '0'; + + 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; + + when WriteIOCycle_40 => + + when WriteIOCycle_41 => + FSM_STATUS <= '0'; + Z80_IORQni <= '1'; + Z80_WRni <= '1'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 IO Read Cycle. + ----------------------------- + when ReadIOCycle => + + when ReadIOCycle_11 => + + when ReadIOCycle_20 => + Z80_IORQni <= '0'; + Z80_RDni <= '0'; + + 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; + + when ReadIOCycle_40 => + + when ReadIOCycle_41 => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + FSM_STATUS <= '0'; + + -- IORQ/RD are deactivated at idle giving 1 clock to latch the data in. + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Halt Request. + ----------------------------- + when HaltCycle => + Z80_HALTni <= '0'; + FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 Bus Request. + ----------------------------- + when BusReqCycle => + Z80_BUSRQ_ACKni <= '0'; + FSM_STATE <= IdleCycle; when others => + FSM_STATE <= IdleCycle; end case; - - -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. - if(SPI_NEW_DATA /= SPI_PROCESSING) then - SPI_PROCESSING <= not SPI_PROCESSING; - end if; - - -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. - FSM_STATUS <= '1'; end if; - -- Refresh status bit. Indicates a Refresh cycle is under way. - if FSM_STATE = RefreshCycle or FSM_STATE = RefreshCycle_1 or FSM_STATE = RefreshCycle_2 or FSM_STATE = RefreshCycle_3 then - RFSH_STATUS <= '1'; - else - RFSH_STATUS <= '0'; - end if; - - -- FSM to implement all the required Z80 cycles. - -- - case FSM_STATE is - when IdleCycle => - - ----------------------------- - -- Z80 Fetch Cycle. - ----------------------------- - 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'; - FSM_STATE <= FetchCycle_2; - end if; - - when FetchCycle_2 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then - Z80_MREQni <= '0'; - Z80_RDni <= '0'; - FSM_STATE <= FetchCycle_3; - end if; - - when FetchCycle_3 => - 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 - -- 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). - if(CPU_T_STATE = 3 and Z80_CLK_RE = '1' and Z80_WAITni = '1') then - FSM_STATE <= RefreshCycle; - end if; - - ----------------------------- - -- Z80 Refresh Cycle. - ----------------------------- - when RefreshCycle => - -- Latch data from mainboard. - CPU_DATA_IN <= Z80_DATA; - FSM_STATUS <= '0'; - Z80_RFSHni <= '0'; - FSM_STATE <= RefreshCycle_1; - - when RefreshCycle_1 => - -- Falling edge of T3 activates the MREQ line. - if(Z80_CLK_FE = '1' and CPU_T_STATE = 3) then - Z80_MREQni <= '0'; - FSM_STATE <= RefreshCycle_2; - end if; - - when RefreshCycle_2 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 4) then - Z80_MREQni <= '1'; - end if; - if(Z80_MREQni = '1' and CPU_T_STATE = 4) then - REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1; - FSM_STATE <= IdleCycle; - end if; - - when RefreshCycle_3 => - CPU_T_STATE_SET <= 3; - CPU_T_STATES <= 4; - Z80_RFSHni <= '0'; - FSM_STATE <= RefreshCycle_1; - - ----------------------------- - -- Z80 Write Cycle. - ----------------------------- - 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 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then - Z80_MREQni <= '0'; - CPU_DATA_EN <= '1'; - FSM_STATE <= WriteCycle_3; - end if; - - when WriteCycle_3 => - if(CPU_T_STATE = 2 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - Z80_WRni <= '0'; - FSM_STATE <= WriteCycle_4; - end if; - - when WriteCycle_4 => - if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - Z80_MREQni <= '1'; - Z80_WRni <= '1'; - FSM_STATE <= IdleCycle; - end if; - - - ----------------------------- - -- Z80 Read Cycle. - ----------------------------- - 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 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 1) then - Z80_MREQni <= '0'; - Z80_RDni <= '0'; - FSM_STATE <= ReadCycle_3; - end if; - - when ReadCycle_3 => - if(CPU_T_STATE = 2 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - FSM_STATE <= ReadCycle_4; - end if; - - when ReadCycle_4 => - if(CPU_T_STATE = 3 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - -- Latch data from mainboard. - CPU_DATA_IN <= Z80_DATA; - FSM_STATUS <= '0'; - - -- MREQ/RD are deactivated at idle giving 1 clock to latch the data in. - FSM_STATE <= IdleCycle; - end if; - - - ----------------------------- - -- Z80 IO Write Cycle. - ----------------------------- - 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 => - if(Z80_CLK_FE = '1') then - CPU_DATA_EN <= '1'; - FSM_STATE <= WriteIOCycle_3; - end if; - - when WriteIOCycle_3 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 2) then - Z80_IORQni <= '0'; - Z80_WRni <= '0'; - FSM_STATE <= WriteIOCycle_4; - end if; - - when WriteIOCycle_4 => - -- 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 => - if(CPU_T_STATE = 4 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - Z80_IORQni <= '1'; - Z80_WRni <= '1'; - FSM_STATE <= IdleCycle; - end if; - - ----------------------------- - -- Z80 IO Read Cycle. - ----------------------------- - 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 => - if(Z80_CLK_FE = '1' and CPU_T_STATE = 2) then - Z80_IORQni <= '0'; - Z80_RDni <= '0'; - FSM_STATE <= ReadIOCycle_3; - end if; - - when ReadIOCycle_3 => - -- 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 => - if(CPU_T_STATE = 4 and Z80_CLK_FE = '1' and Z80_WAITni = '1') then - -- Latch data from mainboard. - CPU_DATA_IN <= Z80_DATA; - FSM_STATUS <= '0'; - - -- IORA/RD are deactivated at idle giving 1 clock to latch the data in. - FSM_STATE <= IdleCycle; - end if; - - ----------------------------- - -- Halt Request. - ----------------------------- - when HaltCycle => - Z80_HALTni <= '0'; - FSM_STATUS <= '0'; - FSM_STATE <= IdleCycle; - - ----------------------------- - -- Z80 Bus Request. - ----------------------------- - when BusReqCycle => - Z80_BUSRQ_ACKni <= '0'; - FSM_STATE <= IdleCycle; - end case; - VCPU_CS_EDGE <= VCPU_CS_EDGE(0) & VSOM_SPI_CSn; end if; end process; + Z80_CLKi <= not Z80_CLK; + -- CPU Interface tri-state control based on acknowledged bus request. Z80_ADDR <= IPAR & REFRESH_ADDR when Z80_RFSHni = '0' else @@ -916,8 +1010,6 @@ begin Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' else (others => 'Z'); - -- Z80_DATAi <= Z80_DATA when Z80_RDn = '0' - -- else (others => '1'); Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' else 'Z'; Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' @@ -938,7 +1030,6 @@ begin -- CPU Interface single state input. Z80_NMIni <= Z80_NMIn; Z80_INTni <= Z80_INTn; - Z80_WAITni <= Z80_WAITn; Z80_BUSRQni <= Z80_BUSRQn; -- SOM Reset. @@ -947,18 +1038,19 @@ begin -- SOM to CPLD Interface. VSOM_DATA_OUT <= CPU_DATA_IN when VSOM_HBYTE = '1' 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 (others => '0'); -- Loopback test, echo what was received. SPI_TX_DATA <= SPI_RX_DATA when SPI_LOOPBACK_TEST = '1' 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. VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING - else '1'; -- FSM Ready (1), Busy (0) + else '1'; -- FSM Ready (1), Busy (0) VSOM_LTSTATE <= '1' when CPU_LAST_T_STATE = '1' -- Last T-State in current cycle. else '0'; VSOM_BUSRQ <= not Z80_BUSRQn; -- Host device requesting Z80 Bus. @@ -990,10 +1082,14 @@ begin -- Generate composite sync. 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. --VGA_PXL_CLK <= CLK_50M; MONO_PXL_CLK <= VGA_PXL_CLK; + + -- Currently unassigned. + VGA_COLR <= '0'; + MONO_RSV <= '0'; end architecture; diff --git a/CPLD/v1.0/MZ80A/tzpuFusionX.vhd b/CPLD/v1.0/MZ80A/tzpuFusionX.vhd index 0b0875ef5..19021452d 100644 --- a/CPLD/v1.0/MZ80A/tzpuFusionX.vhd +++ b/CPLD/v1.0/MZ80A/tzpuFusionX.vhd @@ -15,6 +15,7 @@ -- 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. +-- Apr 2023 v1.2 - Updated from the PCW8256 development. -- --------------------------------------------------------------------------------------------------------- -- This source file is free software: you can redistribute it and-or modify @@ -131,7 +132,7 @@ end entity; architecture rtl of cpld512 is - -- Finite State Machine states. + -- Z80 Finite State Machine states. type SOMFSMState is ( IdleCycle, @@ -177,6 +178,14 @@ architecture rtl of cpld512 is BusReqCycle ); + -- Controller FSM states. + type CTRLFSMState is + ( + CTRLCMD_Idle, + CTRLCMD_ReadIOWrite, + CTRLCMD_ReadIOWrite_1 + ); + -- CPU Interface internal signals. signal Z80_BUSRQni : std_logic; signal Z80_INTni : std_logic; @@ -188,7 +197,6 @@ architecture rtl of cpld512 is signal Z80_HALTni : std_logic; signal Z80_M1ni : std_logic; signal Z80_RFSHni : std_logic; - signal Z80_DATAi : std_logic_vector(7 downto 0); signal Z80_BUSRQ_ACKni : std_logic; -- Internal CPU state control. @@ -206,14 +214,15 @@ architecture rtl of cpld512 is -- Refresh control. 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 AUTOREFRESH_CNT : integer range 0 to 7; + signal AUTOREFRESH_CNT : integer range 0 to 31; 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 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 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_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_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). -- SPI Command interface. 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_PROCESSING : std_logic; 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_INPUT : process(VSOM_SPI_CLK) 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 - 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 SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; @@ -396,6 +409,7 @@ begin SPI_FRAME_CNT<= 1; VSOM_SPI_MISO<= SPI_TX_DATA(7); 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 SPI_FRAME_CNT<= 2; VSOM_SPI_MISO<= SPI_TX_DATA(15); @@ -404,13 +418,14 @@ begin SPI_FRAME_CNT<= 3; VSOM_SPI_MISO<= SPI_TX_DATA(23); 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. SPI_FRAME_CNT<= 4; VSOM_SPI_MISO<= SPI_TX_DATA(31); SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); + else + SPI_FRAME_CNT<= 0; end if; - SPI_BIT_CNT <= 7; end if; end if; @@ -428,6 +443,7 @@ begin AUTOREFRESH <= '0'; SPI_LOOPBACK_TEST <= '0'; SOM_CMD <= (others => '0'); + SOM_PARAM_CNT <= 0; SPI_CPU_ADDR <= (others => '0'); SPI_NEW_DATA <= '0'; @@ -446,66 +462,110 @@ begin -- elsif(VSOM_SPI_CSn'event and VSOM_SPI_CSn = '1') then - -- Command is always located in the upper byte of frame 1. - SOM_CMD <= SPI_RX_DATA(7 downto 0); + -- 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; - -- Toggle flag to indicate new data arrived. - SPI_NEW_DATA <= not SPI_NEW_DATA; + -- Process if command, store parameters. + if(SOM_PARAM_CNT = 0) then + -- Command is always located in the upper byte of frame 1. + SOM_CMD <= SPI_RX_DATA(7 downto 0); - -- Process the command. Some commands require the FSM, others can be serviced immediately. - case SPI_RX_DATA(7 downto 0) is + -- Toggle flag to indicate new data arrived. + SPI_NEW_DATA <= not SPI_NEW_DATA; - -- Z80XACT(0..15): Setup data and address as provided then execute FSM. - 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"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" => + -- Process the command. Some commands require the FSM, others can be serviced immediately. + case SPI_RX_DATA(7 downto 0) is - -- Direct address set. - if(SPI_FRAME_CNT = 4) then - SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); - 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))); - -- end if; - end if; + -- Z80XACT(0..15): Setup data and address as provided then execute FSM. + 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"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read + X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => - if(SPI_FRAME_CNT > 1) then - SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); - end if; + -- Direct address set. + if(SPI_FRAME_CNT = 4) then + SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); + else + SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + unsigned(SPI_RX_DATA(2 downto 0))); + end if; - -- SETSIGSET1: Set control lines directly. - when X"F0" => - VIDEO_SRCi <= SPI_RX_DATA(8); - MONO_VIDEO_SRCi <= SPI_RX_DATA(9); - AUDIO_SRC_Li <= SPI_RX_DATA(10); - AUDIO_SRC_Ri <= SPI_RX_DATA(11); - VBUS_ENi <= SPI_RX_DATA(12); - VGA_BLANKn <= not SPI_RX_DATA(13); + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- Enable auto refresh DRAM cycle. - when X"F1" => - AUTOREFRESH <= '1'; + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => -- WriteIO - -- Disable auto refresh DRAM cycle. - when X"F2" => - AUTOREFRESH <= '0'; + -- 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; - -- SETLOOPBACK: Enable loopback test mode. - when X"FE" => - SPI_LOOPBACK_TEST<= '1'; + if(SPI_FRAME_CNT > 1) then + SPI_CPU_DATA <= SPI_RX_DATA(15 downto 8); + end if; - -- No action, called to retrieve status. - when X"00" | X"FF" => + 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 - when others => - end case; + -- 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. + when X"F0" => + VIDEO_SRCi <= SPI_RX_DATA(8); + MONO_VIDEO_SRCi <= SPI_RX_DATA(9); + AUDIO_SRC_Li <= SPI_RX_DATA(10); + AUDIO_SRC_Ri <= SPI_RX_DATA(11); + VBUS_ENi <= SPI_RX_DATA(12); + VGA_BLANKn <= not SPI_RX_DATA(13); + + -- Enable auto refresh DRAM cycle. + when X"F1" => + AUTOREFRESH <= '1'; + + -- Disable auto refresh DRAM cycle. + when X"F2" => + AUTOREFRESH <= '0'; + + -- SETLOOPBACK: Enable loopback test mode. + when X"FE" => + SPI_LOOPBACK_TEST<= '1'; + + -- No action, called to retrieve status. + when X"00" | X"FF" => + + when others => + 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 process; @@ -566,9 +626,9 @@ begin CPU_DATA_EN <= '0'; CPU_DATA_IN <= (others => '0'); REFRESH_ADDR <= (others => '0'); - AUTOREFRESH_CNT <= 7; + AUTOREFRESH_CNT <= 31; IPAR <= (others => '0'); - NEW_SPI_CMD <= '0'; + NEW_SPI_DATA <= '0'; VCPU_CS_EDGE <= "11"; SPI_PROCESSING <= '0'; @@ -585,99 +645,12 @@ begin -- 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 - NEW_SPI_CMD <= '1'; + NEW_SPI_DATA <= '1'; end if; - -- 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 - CPU_DATA_EN <= '0'; - Z80_MREQni <= '1'; - Z80_IORQni <= '1'; - Z80_RDni <= '1'; - Z80_WRni <= '1'; - Z80_M1ni <= '1'; - FSM_STATUS <= '0'; - Z80_RFSHni <= '1'; - - -- Auto DRAM refresh cycles. When enabled, every 7 host clock cycles, a 2 cycle refresh period commences. - -- This will be overriden if the SPI receives a new command. - -- - if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then - AUTOREFRESH_CNT <= AUTOREFRESH_CNT - 1; - if(AUTOREFRESH_CNT = 0) then - FSM_STATE <= RefreshCycle_3; - FSM_STATUS<= '1'; - end if; - end if; - end if; - - -- 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 - NEW_SPI_CMD <= '0'; - - -- Store new address and data for this command. - CPU_ADDR <= SPI_CPU_ADDR; - if(SPI_CPU_DATA /= CPU_DATA_OUT) then - CPU_DATA_OUT <= SPI_CPU_DATA; - end if; - - -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM - -- data prior to this execution block, which fires 1 cycle later on the same control clock. - -- If the command is not for the FSM then the READY mechanism is held for one - -- further cycle before going inactive. - case SOM_CMD is - when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => - -- Initiate a Fetch Cycle. - FSM_STATE <= FetchCycle; - - when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => - - -- Set the Z80 data bus value and initiate a Write Cycle. - FSM_STATE <= WriteCycle; - - when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => - -- Initiate a Read Cycle. - FSM_STATE <= ReadCycle; - - when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => - -- Set the Z80 data bus value and initiate an IO Write Cycle. - -- The SOM should set 15:8 to the B register value. - FSM_STATE <= WriteIOCycle; - - when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => - -- Initiate a Read IO Cycle. - FSM_STATE <= ReadIOCycle; - - when X"50" => - -- Register a Halt state. - FSM_STATE <= HaltCycle; - - when X"51" => - -- Initiate a refresh cycle. - FSM_STATE <= RefreshCycle_3; - - when X"E0" => - -- Initiate a Halt Cycle. - FSM_STATE <= HaltCycle; - - -- Set the Refresh Address register. - when X"E1" => - REFRESH_ADDR <= CPU_DATA_OUT; - - -- Set the Interrupt Page Address Register. - when X"E2" => - IPAR <= CPU_DATA_OUT; - - when others => - end case; - - -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. - if(SPI_NEW_DATA /= SPI_PROCESSING) then - SPI_PROCESSING <= not SPI_PROCESSING; - end if; - - -- FSM Status bit. When processing a command it is set, cleared when idle. Used by SOM to determine command completion. - FSM_STATUS <= '1'; + -- 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. @@ -692,7 +665,143 @@ begin FSM_WAIT_ACTIVE <= '1'; end if; - -- On each Z80 edge we advance the FSM to recreate the Z80 external signal transactions. + -- 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_RE = '1') then + CPU_DATA_EN <= '0'; + Z80_MREQni <= '1'; + Z80_IORQni <= '1'; + Z80_RDni <= '1'; + Z80_WRni <= '1'; + Z80_M1ni <= '1'; + FSM_STATUS <= '0'; + Z80_RFSHni <= '1'; + + -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences. + -- This period may be extended if the SPI receives a new command. + -- + if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then + if(AUTOREFRESH_CNT = 0) then + FSM_STATE <= RefreshCycle_3; + FSM_STATUS<= '1'; + -- 4116 DRAM = 128 cycles in 2ms. + AUTOREFRESH_CNT <= 31; + 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_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + + -- Store new address and data for this command. + if(NEW_SPI_DATA = '1') then + CPU_ADDR <= SPI_CPU_ADDR; + CPU_DATA_OUT <= SPI_CPU_DATA; + end if; + + -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM + -- data prior to this execution block, which fires 1 cycle later on the same control clock. + -- If the command is not for the FSM then the READY mechanism is held for one + -- further cycle before going inactive. + case SOM_CMD is + when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => + -- Initiate a Fetch Cycle. + FSM_STATE <= FetchCycle; + + when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => + + -- Set the Z80 data bus value and initiate a Write Cycle. + FSM_STATE <= WriteCycle; + + when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => + -- Initiate a Read Cycle. + FSM_STATE <= ReadCycle; + + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => + -- Set the Z80 data bus value and initiate an IO Write Cycle. + -- The SOM should set 15:8 to the B register value. + FSM_STATE <= WriteIOCycle; + + when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => + -- Initiate a Read IO Cycle. + 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" => + -- Register a Halt state. + FSM_STATE <= HaltCycle; + + when X"51" => + -- Initiate a refresh cycle. + FSM_STATE <= RefreshCycle_3; + + when X"E0" => + -- Initiate a Halt Cycle. + FSM_STATE <= HaltCycle; + + -- Set the Refresh Address register. + when X"E1" => + REFRESH_ADDR <= CPU_DATA_OUT(6 downto 0); + + -- Set the Interrupt Page Address Register. + when X"E2" => + IPAR <= CPU_DATA_OUT; + + when others => + end case; + + -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. + if(SPI_NEW_DATA /= SPI_PROCESSING) then + SPI_PROCESSING<= not SPI_PROCESSING; + 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 <= '1'; + end if; + + when CTRLCMD_ReadIOWrite => + if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + NEW_SPI_DATA <= '0'; + CPU_DATA_EN <= '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; + + 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. @@ -711,7 +820,6 @@ begin when IdleCycle => CPU_LAST_T_STATE <= '1'; FSM_STATUS <= '0'; - -- FSM_STATE <= IdleCycle; ----------------------------- -- Z80 Fetch Cycle. @@ -734,7 +842,7 @@ begin when FetchCycle_30 => -- 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; ----------------------------- @@ -754,7 +862,7 @@ begin when RefreshCycle_21 => Z80_MREQni <= '1'; - REFRESH_ADDR(6 downto 0) <= REFRESH_ADDR(6 downto 0) + 1; + REFRESH_ADDR <= REFRESH_ADDR + 1; FSM_STATE <= IdleCycle; when RefreshCycle_3 => @@ -776,7 +884,7 @@ begin when WriteCycle_21 => Z80_WRni <= '0'; if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= WriteCycle_20; + FSM_STATE <= WriteCycle_20; end if; when WriteCycle_30 => @@ -801,7 +909,7 @@ begin when ReadCycle_21 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= ReadCycle_20; + FSM_STATE <= ReadCycle_20; end if; when ReadCycle_30 => @@ -812,7 +920,6 @@ begin FSM_STATUS <= '0'; FSM_STATE <= IdleCycle; - ----------------------------- -- Z80 IO Write Cycle. ----------------------------- @@ -832,7 +939,7 @@ begin when WriteIOCycle_31 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= WriteIOCycle_20; + FSM_STATE <= WriteIOCycle_30; end if; when WriteIOCycle_40 => @@ -861,7 +968,7 @@ begin when ReadIOCycle_31 => if(Z80_WAITn = '0' or FSM_WAIT_ACTIVE = '1') then - FSM_STATE <= ReadIOCycle_20; + FSM_STATE <= ReadIOCycle_30; end if; when ReadIOCycle_40 => @@ -901,7 +1008,7 @@ begin Z80_CLKi <= not Z80_CLK; -- 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 CPU_ADDR when Z80_BUSRQ_ACKni = '1' else @@ -909,8 +1016,6 @@ begin Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' else (others => 'Z'); - -- Z80_DATAi <= Z80_DATA when Z80_RDn = '0' - -- else (others => '1'); Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' else 'Z'; Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' @@ -951,7 +1056,7 @@ begin -- Signal mirrors. VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING - else '1'; -- FSM Ready (1), Busy (0) + else '1'; -- FSM Ready (1), Busy (0) VSOM_LTSTATE <= '1' when CPU_LAST_T_STATE = '1' -- Last T-State in current cycle. else '0'; VSOM_BUSRQ <= not Z80_BUSRQn; -- Host device requesting Z80 Bus. diff --git a/CPLD/v1.0/PCW8256/tzpuFusionX.vhd b/CPLD/v1.0/PCW8256/tzpuFusionX.vhd new file mode 100644 index 000000000..892ef093b --- /dev/null +++ b/CPLD/v1.0/PCW8256/tzpuFusionX.vhd @@ -0,0 +1,1083 @@ +------------------------------------------------------------------------------------------------------- +-- +-- Name: tzpuFusionX.vhd +-- Version: Amstrad PCW-8256 +-- Created: Mar 2023 +-- Author(s): Philip Smart +-- Description: tzpuFusionX CPLD logic definition file. +-- This module contains the definition of the tzpuFusionX project plus enhancements +-- for the Amstrad PCW-8256. +-- +-- Credits: +-- Copyright: (c) 2018-23 Philip Smart +-- +-- 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. +-- Apr 2023 v1.1 - Significant changes to attempt to increase throughput as the Amstrad +-- has been designed to operate items such as the FDC within a flyback +-- window which is very difficult to meet given the boot code. +-- +--------------------------------------------------------------------------------------------------------- +-- This source file is free software: you can redistribute it and-or modify +-- it under the terms of the GNU General Public License as published +-- by the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This source file is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +--------------------------------------------------------------------------------------------------------- +library ieee; +library pkgs; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.tzpuFusionX_pkg.all; + +entity cpld512 is + generic ( + SPI_CLK_POLARITY : std_logic := '0' + ); + 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 Control Signals + VSOM_SPI_CLK : in std_logic; -- SOM SPI Channel 0 Clock. + VSOM_SPI_MOSI : in std_logic; -- MOSI Input. + VSOM_SPI_MISO : out std_logic; -- MISO Output. + VSOM_SPI_CSn : in std_logic; -- Enable. + + -- 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 cpld512 is + + -- Z80 Finite State Machine states. + type SOMFSMState is + ( + IdleCycle, + FetchCycle, + FetchCycle_11, + FetchCycle_20, + FetchCycle_21, + FetchCycle_30, + RefreshCycle, + RefreshCycle_11, + RefreshCycle_20, + RefreshCycle_21, + RefreshCycle_3, + WriteCycle, + WriteCycle_11, + WriteCycle_20, + WriteCycle_21, + WriteCycle_30, + WriteCycle_31, + ReadCycle, + ReadCycle_11, + ReadCycle_20, + ReadCycle_21, + ReadCycle_30, + ReadCycle_31, + WriteIOCycle, + WriteIOCycle_11, + WriteIOCycle_20, + WriteIOCycle_21, + WriteIOCycle_30, + WriteIOCycle_31, + WriteIOCycle_40, + WriteIOCycle_41, + ReadIOCycle, + ReadIOCycle_11, + ReadIOCycle_20, + ReadIOCycle_21, + ReadIOCycle_30, + ReadIOCycle_31, + ReadIOCycle_40, + ReadIOCycle_41, + HaltCycle, + BusReqCycle + ); + + -- Controller FSM states. + type CTRLFSMState is + ( + CTRLCMD_Idle, + CTRLCMD_ReadIOWrite, + CTRLCMD_ReadIOWrite_1 + ); + + -- CPU Interface internal signals. + signal Z80_BUSRQni : std_logic; + signal Z80_INTni : std_logic; + signal Z80_IORQni : std_logic; + signal Z80_MREQni : std_logic; + signal Z80_NMIni : std_logic; + signal Z80_RDni : std_logic; + signal Z80_WRni : std_logic; + signal Z80_HALTni : std_logic; + signal Z80_M1ni : std_logic; + signal Z80_RFSHni : std_logic; + signal Z80_BUSRQ_ACKni : std_logic; + + -- Internal CPU state control. + signal CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0'); + signal CPU_DATA_IN : std_logic_vector(7 downto 0) := (others => '0'); + signal CPU_DATA_OUT : std_logic_vector(7 downto 0) := (others => '0'); + signal CPU_DATA_EN : std_logic; + + -- Clocks. + signal CLK_25Mi : std_logic := '0'; + + -- Reset control + signal PM_RESETi : std_logic := '1'; + signal VSOM_RESETni : std_logic := '1'; + + -- Refresh control. + signal FSM_STATE : SOMFSMState := IdleCycle; + 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 AUTOREFRESH_CNT : integer range 0 to 63; + signal FSM_STATUS : std_logic := '0'; + signal RFSH_STATUS : std_logic := '0'; + signal REFRESH_ADDR : std_logic_vector(7 downto 0); + signal IPAR : std_logic_vector(7 downto 0); + signal AUTOREFRESH : std_logic; + + -- Clock edge detection and flagging. + signal Z80_CLKi : std_logic; + signal Z80_CLK_LAST : std_logic_vector(1 downto 0); + signal Z80_CLK_RE : std_logic; + signal Z80_CLK_FE : std_logic; + signal Z80_CLK_TGL : std_logic; + signal CPU_T_STATE_SET : integer range 0 to 5; + signal CPU_LAST_T_STATE : std_logic := '0'; + + -- SPI Slave interface. + signal SPI_SHIFT_EN : std_logic; + signal SPI_TX_SREG : std_logic_vector(6 downto 0); -- TX 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_RX_DATA : std_logic_vector(31 downto 0); -- Data received. + 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). + + -- SPI Command interface. + 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_PROCESSING : std_logic; + signal SPI_CPU_ADDR : std_logic_vector(15 downto 0) := (others => '0'); + signal SPI_CPU_DATA : std_logic_vector(7 downto 0) := (others => '0'); + + -- Test modes. + signal SPI_LOOPBACK_TEST : std_logic := '0'; + + -- Video/Audio control + signal VIDEO_SRCi : std_logic := '0'; + signal MONO_VIDEO_SRCi : std_logic := '0'; + signal AUDIO_SRC_Li : std_logic := '0'; + signal AUDIO_SRC_Ri : std_logic := '0'; + signal VBUS_ENi : std_logic := '1'; + + + function to_std_logic(L: boolean) return std_logic is + begin + if L then + return('1'); + else + return('0'); + end if; + end function to_std_logic; +begin + + + -- System RESET. + -- + -- The FusionX has multiple reset sources, Z80_RESETn, MB_IPLn, MB_RESETn. On the PCW-8256 there is no external reset, so we implement the standard + -- reset logic but it will never be triggered externally. + -- + SYSRESET: process( Z80_CLKi, Z80_RESETn ) + variable timer1 : integer range 0 to 400000 := 0; + variable timer100 : integer range 0 to 10 := 0; + variable timerPMReset : integer range 0 to 10 := 0; + variable resetCount : integer range 0 to 3 := 0; + variable cpuResetEdge : std_logic := '1'; + begin + -- Synchronous on the HOST Clock. + 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(timerPMReset = 0 and PM_RESETi = '1') then + PM_RESETi <= '0'; + end if; + + -- If the VSOM_RESETni is active after reset timer expiry, cancel the RESET state. + if(timerPMReset = 0 and VSOM_RESETni = '0') then + VSOM_RESETni <= '1'; + end if; + + -- Each time the reset button is pressed, count the edges. + if(Z80_RESETn = '0' and cpuResetEdge = '1' and (resetCount = 0 or timer100 > 5)) then + resetCount := resetCount + 1; + VSOM_RESETni <= '0'; + timerPMReset := 5; + timer100 := 0; + + -- If there are 2 or more reset signals in a given period it means a SOM reset is required. + if(resetCount >= 2) then + PM_RESETi <= '1'; + timerPMReset := 10; + resetCount := 0; + end if; + + end if; + + -- 100ms interval. + if(timer1 = 400000) then + timer100 := timer100 + 1; + + if(timer100 >= 10) then + timer100 := 0; + resetCount := 0; + end if; + + if(timerPMReset > 0) then + timerPMReset := timerPMReset - 1; + end if; + end if; + + timer1 := timer1 - 1; + cpuResetEdge := Z80_RESETn; + end if; + end process; + + -- Create Mono DAC Clock based on primary clock. + MONOCLK: process( CLK_50M ) + begin + if(rising_edge(CLK_50M)) then + CLK_25Mi <= not CLK_25Mi; + end if; + end process; + + -- SPI Slave input. Receive command and data from the SOM. + SPI_INPUT : process(VSOM_SPI_CLK) + 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 + elsif(VSOM_SPI_CLK'event and VSOM_SPI_CLK = SPI_CLK_POLARITY) then + if(VSOM_SPI_CSn = '0') then + SPI_RX_SREG <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; + + -- End of frame then store the data prior to next bit arrival. + -- Convert to Little Endian, same as SOM. + if(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 1 and SPI_BIT_CNT = 0) then + SPI_RX_DATA(7 downto 0) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; + + elsif(SPI_SHIFT_EN = '1' and SPI_FRAME_CNT = 2 and SPI_BIT_CNT = 0) then + SPI_RX_DATA(15 downto 8) <= SPI_RX_SREG(6 downto 0) & VSOM_SPI_MOSI; + + 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; + + 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; + end if; + end if; + end if; + end process; + + -- 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) + begin + -- Chip Select inactive, disable process and reset control flags. + if(VSOM_SPI_CSn = '1') then + SPI_SHIFT_EN <= '0'; + SPI_BIT_CNT <= 7; + + -- 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 + -- Each clock reset the shift enable and done flag in preparation for the next cycle. + SPI_SHIFT_EN <= '1'; + + -- Bit count decrements to detect when last bit of byte is sent. + if(SPI_BIT_CNT > 0) then + SPI_BIT_CNT <= SPI_BIT_CNT - 1; + end if; + + -- Shift out the next bit. + VSOM_SPI_MISO <= SPI_TX_SREG(6); + SPI_TX_SREG <= SPI_TX_SREG(5 downto 0) & '0'; + + -- First clock after CS goes active, load up the data to be sent to the SOM. + if(SPI_SHIFT_EN = '0' or SPI_BIT_CNT = 0) then + + if(SPI_LOOPBACK_TEST = '1') then + VSOM_SPI_MISO<= SPI_RX_SREG(7); + SPI_TX_SREG <= SPI_RX_SREG(6 downto 0); + + elsif(SPI_SHIFT_EN = '0') then + SPI_FRAME_CNT<= 1; + VSOM_SPI_MISO<= SPI_TX_DATA(7); + 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 + SPI_FRAME_CNT<= 2; + VSOM_SPI_MISO<= SPI_TX_DATA(15); + SPI_TX_SREG <= SPI_TX_DATA(14 downto 8); + elsif(SPI_FRAME_CNT = 2) then + SPI_FRAME_CNT<= 3; + VSOM_SPI_MISO<= SPI_TX_DATA(23); + SPI_TX_SREG <= SPI_TX_DATA(22 downto 16); + 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. + SPI_FRAME_CNT<= 4; + VSOM_SPI_MISO<= SPI_TX_DATA(31); + SPI_TX_SREG <= SPI_TX_DATA(30 downto 24); + else + SPI_FRAME_CNT<= 0; + end if; + SPI_BIT_CNT <= 7; + end if; + end if; + end process; + + SPI_REGISTER : process(Z80_RESETn, VSOM_SPI_CSn, SPI_FRAME_CNT) + begin + if(Z80_RESETn = '0') then + VIDEO_SRCi <= '0'; + VGA_BLANKn <= '1'; + VBUS_ENi <= '1'; + MONO_VIDEO_SRCi <= '1'; + AUDIO_SRC_Li <= '0'; + AUDIO_SRC_Ri <= '0'; + AUTOREFRESH <= '1'; + SPI_LOOPBACK_TEST <= '0'; + SOM_CMD <= (others => '0'); + SOM_PARAM_CNT <= 0; + SPI_CPU_ADDR <= (others => '0'); + SPI_NEW_DATA <= '0'; + + -- On rising edge of SPI CSn a new data packet from the SOM has arrived and in the shift register SPI_RX_SREG. + -- The variable SPI_FRAME_CNT indicates which byte (frame) in a 32bit word has been transmitted. This allows + -- for 8bit, 16bit and 32bit transmissions. + -- 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> + -- < 16bit Z80 Address > < Z80 Data > + -- 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 > + -- 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 + + -- 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. + SOM_CMD <= SPI_RX_DATA(7 downto 0); + + -- Toggle flag to indicate new data arrived. + SPI_NEW_DATA <= not SPI_NEW_DATA; + + -- Process the command. Some commands require the FSM, others can be serviced immediately. + case SPI_RX_DATA(7 downto 0) is + + -- Z80XACT(0..15): Setup data and address as provided then execute FSM. + 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"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" | -- Read + X"48" | X"49" | X"4A" | X"4B" | X"4C" | X"4D" | X"4E" | X"4F" => + + -- Direct address set. + if(SPI_FRAME_CNT = 4) then + SPI_CPU_ADDR <= SPI_RX_DATA(31 downto 16); + else + SPI_CPU_ADDR <= std_logic_vector(unsigned(SPI_CPU_ADDR) + 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"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. + when X"F0" => + VIDEO_SRCi <= SPI_RX_DATA(8); + MONO_VIDEO_SRCi <= SPI_RX_DATA(9); + AUDIO_SRC_Li <= SPI_RX_DATA(10); + AUDIO_SRC_Ri <= SPI_RX_DATA(11); + VBUS_ENi <= SPI_RX_DATA(12); + VGA_BLANKn <= not SPI_RX_DATA(13); + + -- Enable auto refresh DRAM cycle. + when X"F1" => + AUTOREFRESH <= '1'; + + -- Disable auto refresh DRAM cycle. + when X"F2" => + AUTOREFRESH <= '0'; + + -- SETLOOPBACK: Enable loopback test mode. + when X"FE" => + SPI_LOOPBACK_TEST<= '1'; + + -- No action, called to retrieve status. + when X"00" | X"FF" => + + when others => + 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 process; + + -- Process to detect the Z80 Clock edges. Each edge is used to recreate the Z80 external signals. + -- + Z80CLK: process( CLK_50M, Z80_CLKi, Z80_RESETn ) + begin + if(Z80_RESETn = '0') then + Z80_CLK_RE <= '1'; + Z80_CLK_FE <= '1'; + Z80_CLK_TGL <= '1'; + + elsif(rising_edge(CLK_50M)) then + + -- Default is to clear the signals, only active for 1 clock period. + Z80_CLK_RE <= '0'; + Z80_CLK_FE <= '0'; + Z80_CLK_TGL <= '0'; + + -- Rising Edge. + if(Z80_CLKi = '1' and Z80_CLK_LAST = "00") then + Z80_CLK_RE <= '1'; + + -- 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 process; + + -- SOM Finite State Machine. + -- + -- 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. + -- + SOMFSM: process( CLK_50M, Z80_CLKi, Z80_RESETn ) + begin + if(Z80_RESETn = '0') then + Z80_IORQni <= '1'; + Z80_MREQni <= '1'; + Z80_RDni <= '1'; + Z80_WRni <= '1'; + Z80_HALTni <= '1'; + Z80_M1ni <= '1'; + Z80_RFSHni <= '1'; + Z80_BUSRQ_ACKni <= '1'; + FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; + RFSH_STATUS <= '0'; + CPU_DATA_EN <= '0'; + CPU_DATA_IN <= (others => '0'); + REFRESH_ADDR <= (others => '0'); + AUTOREFRESH_CNT <= 63; + IPAR <= (others => '0'); + NEW_SPI_DATA <= '0'; + VCPU_CS_EDGE <= "11"; + SPI_PROCESSING <= '0'; + + elsif(rising_edge(CLK_50M)) then + + -- 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. + if(Z80_BUSRQn = '0' and Z80_BUSRQ_ACKni = '1' and FSM_STATE = IdleCycle) then + FSM_STATE <= BusReqCycle; + end if; + if(Z80_BUSRQn = '1' and Z80_BUSRQ_ACKni = '0' and FSM_STATE = IdleCycle) then + Z80_BUSRQ_ACKni <= '1'; + end if; + + -- 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 + 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; + + -- 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_RE = '1') then + CPU_DATA_EN <= '0'; + Z80_MREQni <= '1'; + Z80_IORQni <= '1'; + Z80_RDni <= '1'; + Z80_WRni <= '1'; + Z80_M1ni <= '1'; + FSM_STATUS <= '0'; + Z80_RFSHni <= '1'; + FSM_STATUS <= '0'; + + -- Auto DRAM refresh cycles. When enabled, every 15.6us a refresh period commences. + -- This period may be extended if the SPI receives a new command. + -- + if AUTOREFRESH = '1' and FSM_STATE = IdleCycle then + if(AUTOREFRESH_CNT = 0) then + FSM_STATE <= RefreshCycle_3; + FSM_STATUS<= '1'; + -- 41257 DRAM = 256 cycles in 4ms. + AUTOREFRESH_CNT <= 63; + 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_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + + -- Store new address and data for this command. + if(NEW_SPI_DATA = '1') then + CPU_ADDR <= SPI_CPU_ADDR; + CPU_DATA_OUT <= SPI_CPU_DATA; + end if; + + -- Process the SOM command. The SPI_REGISTER executes non FSM commands and stores FSM + -- data prior to this execution block, which fires 1 cycle later on the same control clock. + -- If the command is not for the FSM then the READY mechanism is held for one + -- further cycle before going inactive. + case SOM_CMD is + when X"10" | X"11" | X"12" | X"13" | X"14" | X"15" | X"16" | X"17" => + -- Initiate a Fetch Cycle. + FSM_STATE <= FetchCycle; + + when X"18" | X"19" | X"1A" | X"1B" | X"1C" | X"1D" | X"1E" | X"1F" => + + -- Set the Z80 data bus value and initiate a Write Cycle. + FSM_STATE <= WriteCycle; + + when X"20" | X"21" | X"22" | X"23" | X"24" | X"25" | X"26" | X"27" => + -- Initiate a Read Cycle. + FSM_STATE <= ReadCycle; + + when X"28" | X"29" | X"2A" | X"2B" | X"2C" | X"2D" | X"2E" | X"2F" => + -- Set the Z80 data bus value and initiate an IO Write Cycle. + -- The SOM should set 15:8 to the B register value. + FSM_STATE <= WriteIOCycle; + + when X"30" | X"31" | X"32" | X"33" | X"34" | X"35" | X"36" | X"37" => + -- Initiate a Read IO Cycle. + 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" => + -- Register a Halt state. + FSM_STATE <= HaltCycle; + + when X"51" => + -- Initiate a refresh cycle. + FSM_STATE <= RefreshCycle_3; + + when X"E0" => + -- Initiate a Halt Cycle. + FSM_STATE <= HaltCycle; + + -- Set the Refresh Address register. + when X"E1" => + REFRESH_ADDR <= CPU_DATA_OUT; + + -- Set the Interrupt Page Address Register. + when X"E2" => + IPAR <= CPU_DATA_OUT; + + when others => + end case; + + -- Toggle the processing flag to negate the new data flag. Used to indicate device is busy. + if(SPI_NEW_DATA /= SPI_PROCESSING) then + SPI_PROCESSING<= not SPI_PROCESSING; + 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 <= '1'; + end if; + + when CTRLCMD_ReadIOWrite => + if(NEW_SPI_DATA = '1' and FSM_STATE = IdleCycle and Z80_CLK_RE = '1') then + NEW_SPI_DATA <= '0'; + CPU_DATA_EN <= '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; + + 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; + + -- FSM to implement all the required Z80 cycles. + -- + case FSM_STATE is + + when IdleCycle => + CPU_LAST_T_STATE <= '1'; + FSM_STATUS <= '0'; + + ----------------------------- + -- Z80 Fetch Cycle. + ----------------------------- + when FetchCycle => + Z80_M1ni <= '0'; + + when FetchCycle_11 => + Z80_M1ni <= '0'; + Z80_MREQni <= '0'; + Z80_RDni <= '0'; + + when FetchCycle_20 => + + when FetchCycle_21 => + if(Z80_WAITn = '0') then + FSM_STATE <= FetchCycle_20; + end if; + + when FetchCycle_30 => + -- 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. + FSM_STATE <= RefreshCycle; + + ----------------------------- + -- Z80 Refresh Cycle. + ----------------------------- + when RefreshCycle => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + Z80_RFSHni <= '0'; + + when RefreshCycle_11 => + -- Falling edge of T3 activates the MREQ line. + Z80_MREQni <= '0'; + FSM_STATUS <= '0'; + + when RefreshCycle_20 => + + when RefreshCycle_21 => + Z80_MREQni <= '1'; + REFRESH_ADDR <= REFRESH_ADDR + 1; + FSM_STATE <= IdleCycle; + + when RefreshCycle_3 => + Z80_RFSHni <= '0'; + FSM_STATE <= RefreshCycle_11; + + ----------------------------- + -- Z80 Write Cycle. + ----------------------------- + when WriteCycle => + + when WriteCycle_11 => + Z80_MREQni <= '0'; + CPU_DATA_EN <= '1'; + + when WriteCycle_20 => + + when WriteCycle_21 => + Z80_WRni <= '0'; + if(Z80_WAITn = '0') then + FSM_STATE <= WriteCycle_20; + end if; + + when WriteCycle_30 => + + when WriteCycle_31 => + FSM_STATUS <= '0'; + Z80_MREQni <= '1'; + Z80_WRni <= '1'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 Read Cycle. + ----------------------------- + when ReadCycle => + + when ReadCycle_11 => + Z80_MREQni <= '0'; + Z80_RDni <= '0'; + + when ReadCycle_20 => + + when ReadCycle_21 => + if(Z80_WAITn = '0') then + FSM_STATE <= ReadCycle_20; + end if; + + when ReadCycle_30 => + + when ReadCycle_31 => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 IO Write Cycle. + ----------------------------- + when WriteIOCycle => + + when WriteIOCycle_11 => + CPU_DATA_EN <= '1'; + + when WriteIOCycle_20 => + Z80_IORQni <= '0'; + Z80_WRni <= '0'; + + when WriteIOCycle_21 => + + when WriteIOCycle_30 => + + when WriteIOCycle_31 => + if(Z80_WAITn = '0') then + FSM_STATE <= WriteIOCycle_30; + end if; + + when WriteIOCycle_40 => + + when WriteIOCycle_41 => + FSM_STATUS <= '0'; + Z80_IORQni <= '1'; + Z80_WRni <= '1'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 IO Read Cycle. + ----------------------------- + when ReadIOCycle => + + when ReadIOCycle_11 => + + when ReadIOCycle_20 => + Z80_IORQni <= '0'; + Z80_RDni <= '0'; + + when ReadIOCycle_21 => + + when ReadIOCycle_30 => + + when ReadIOCycle_31 => + if(Z80_WAITn = '0') then + FSM_STATE <= ReadIOCycle_30; + end if; + + when ReadIOCycle_40 => + + when ReadIOCycle_41 => + -- Latch data from mainboard. + CPU_DATA_IN <= Z80_DATA; + FSM_STATUS <= '0'; + + -- IORQ/RD are deactivated at idle giving 1 clock to latch the data in. + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Halt Request. + ----------------------------- + when HaltCycle => + Z80_HALTni <= '0'; + FSM_STATUS <= '0'; + FSM_STATE <= IdleCycle; + + ----------------------------- + -- Z80 Bus Request. + ----------------------------- + when BusReqCycle => + Z80_BUSRQ_ACKni <= '0'; + FSM_STATE <= IdleCycle; + + when others => + FSM_STATE <= IdleCycle; + end case; + end if; + + VCPU_CS_EDGE <= VCPU_CS_EDGE(0) & VSOM_SPI_CSn; + end if; + end process; + + Z80_CLKi <= not Z80_CLK; + + -- CPU Interface tri-state control based on acknowledged bus request. + Z80_ADDR <= IPAR & REFRESH_ADDR when Z80_RFSHni = '0' + else + CPU_ADDR when Z80_BUSRQ_ACKni = '1' + else + (others => 'Z'); + Z80_DATA <= CPU_DATA_OUT when Z80_BUSRQ_ACKni = '1' and CPU_DATA_EN = '1' + else + (others => 'Z'); + Z80_RDn <= Z80_RDni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_WRn <= Z80_WRni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_M1n <= Z80_M1ni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_RFSHn <= Z80_RFSHni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_MREQn <= Z80_MREQni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_IORQn <= Z80_IORQni when Z80_BUSRQ_ACKni = '1' + else 'Z'; + Z80_BUSAKn <= Z80_BUSRQ_ACKni; + + -- CPU Interface single state output. + Z80_HALTn <= Z80_HALTni; + + -- CPU Interface single state input. + Z80_NMIni <= Z80_NMIn; + Z80_INTni <= Z80_INTn; + Z80_BUSRQni <= Z80_BUSRQn; + + -- SOM Reset. + PM_RESET <= PM_RESETi; + + -- SOM to CPLD Interface. + VSOM_DATA_OUT <= CPU_DATA_IN when VSOM_HBYTE = '1' + else + FSM_STATUS & RFSH_STATUS & Z80_BUSRQ_ACKni & Z80_BUSRQni & Z80_INTni & Z80_NMIni & Z80_WAITn & Z80_RESETn when VSOM_HBYTE = '0' + else + (others => '0'); + + -- Loopback test, echo what was received. + SPI_TX_DATA <= SPI_RX_DATA when SPI_LOOPBACK_TEST = '1' + else + --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. + VSOM_READY <= '0' when FSM_STATUS='1' or SPI_NEW_DATA /= SPI_PROCESSING + else '1'; -- FSM Ready (1), Busy (0) + VSOM_LTSTATE <= '1' when CPU_LAST_T_STATE = '1' -- Last T-State in current cycle. + else '0'; + VSOM_BUSRQ <= not Z80_BUSRQn; -- Host device requesting Z80 Bus. + VSOM_BUSACK <= not Z80_BUSRQ_ACKni; -- Host device granted Z80 Bus + VSOM_INT <= not Z80_INTn; -- Z80 INT signal + VSOM_NMI <= not Z80_NMIn; -- Z80 NMI signal + VSOM_WAIT <= not Z80_WAITn; -- Z80 WAIT signal + VSOM_RESET <= not VSOM_RESETni; -- Z80 RESET signal + VSOM_RSV <= (others => '0'); -- Reserved pins. + + -- Video/Audio control signals. + VIDEO_SRC <= VIDEO_SRCi; + MONO_VIDEO_SRC <= MONO_VIDEO_SRCi; + AUDIO_SRC_L <= AUDIO_SRC_Li; + AUDIO_SRC_R <= AUDIO_SRC_Ri; + + -- USB Power Supply enable. + VBUS_EN <= VBUS_ENi; + + -- Monochrome output is based on the incoming VGA to give the best chrominance levels. + MONO_R <= VGA_R; + MONO_G <= VGA_G; + MONO_B <= VGA_B; + + -- Blanking is active when all colour signals are at 0. The DAC converts values in range 4v .. 5v to adjust chrominance + -- but true off can obly be achieved by bringing the signal value to 0v which is achieved by a Mux activated with this blanking signal. + MONO_BLANKn <= '0' when VGA_R = "000" and VGA_G = "000" and VGA_B = "000" + else '1'; + + -- Generate composite sync. + VGA_CSYNCn <= VGA_HSYNCn xor not VGA_VSYNCn; + MONO_CSYNCn <= not VGA_HSYNCn xor not VGA_VSYNCn; + + -- DAC clocks. + --VGA_PXL_CLK <= CLK_50M; + MONO_PXL_CLK <= VGA_PXL_CLK; + + -- Currently unassigned. + VGA_COLR <= '0'; + MONO_RSV <= '0'; + +end architecture; diff --git a/CPLD/v1.0/PCW8256/tzpuFusionX_Toplevel.vhd b/CPLD/v1.0/PCW8256/tzpuFusionX_Toplevel.vhd new file mode 100644 index 000000000..763873a78 --- /dev/null +++ b/CPLD/v1.0/PCW8256/tzpuFusionX_Toplevel.vhd @@ -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 +-- +-- 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 . +--------------------------------------------------------------------------------------------------------- +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; diff --git a/CPLD/v1.0/PCW8256/tzpuFusionX_pkg.vhd b/CPLD/v1.0/PCW8256/tzpuFusionX_pkg.vhd new file mode 100644 index 000000000..3e3e6b305 --- /dev/null +++ b/CPLD/v1.0/PCW8256/tzpuFusionX_pkg.vhd @@ -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 +-- +-- 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 . +--------------------------------------------------------------------------------------------------------- + +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; diff --git a/software/FusionX/bin/k64fcpu b/software/FusionX/bin/k64fcpu index 25895fffb..ab6e2c48a 100755 Binary files a/software/FusionX/bin/k64fcpu and b/software/FusionX/bin/k64fcpu differ diff --git a/software/FusionX/bin/sharpbiter b/software/FusionX/bin/sharpbiter index 618f28913..5cfccdbf7 100755 Binary files a/software/FusionX/bin/sharpbiter and b/software/FusionX/bin/sharpbiter differ diff --git a/software/FusionX/bin/z80ctrl b/software/FusionX/bin/z80ctrl index 3aef72c7a..fbf5ee300 100755 Binary files a/software/FusionX/bin/z80ctrl and b/software/FusionX/bin/z80ctrl differ diff --git a/software/FusionX/modules/ttymzdrv.ko b/software/FusionX/modules/ttymzdrv.ko index 689ee126b..6bbd66c18 100644 Binary files a/software/FusionX/modules/ttymzdrv.ko and b/software/FusionX/modules/ttymzdrv.ko differ diff --git a/software/FusionX/modules/z80drv.ko b/software/FusionX/modules/z80drv.ko index d55bc8c94..623569693 100644 Binary files a/software/FusionX/modules/z80drv.ko and b/software/FusionX/modules/z80drv.ko differ diff --git a/software/FusionX/src/ttymz/Makefile b/software/FusionX/src/ttymz/Makefile index 2a45abf17..a94f9a45e 100644 --- a/software/FusionX/src/ttymz/Makefile +++ b/software/FusionX/src/ttymz/Makefile @@ -1,15 +1,17 @@ # Select the target host. -#MODEL := MZ2000 -#MODEL := MZ700 -#DEBUG := y -MODEL := MZ80A +#MODEL := MZ2000 +#MODEL := MZ700 +#MODEL := MZ80A +#MODEL := PCW8XXX +#MODEL := PCW9XXX KERNEL := $(PWD)/../../../linux/kernel FUSIONX := $(PWD)/../.. 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) 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 obj-m += ttymzdrv.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/padmux_tables.o +all: + @echo "Specify target host, ie. make " + @echo "Supported hosts: MZ80A, MZ700, MZ2000, PCW8XXX, PCW9XXX" -all: +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 "Build TTYMZ driver for host: $(MODEL)" make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules - @echo "" install: @echo "Copy kernel driver..." - @cp ttymz.ko $(FUSIONX)/modules/ + @cp ttymzdrv.ko $(FUSIONX)/modules/ clean: make -C $(KERNEL) M=$(PWD) clean + @rm -f ttymz diff --git a/software/FusionX/src/ttymz/sharpmz.c b/software/FusionX/src/ttymz/sharpmz.c index ebdb232a6..dbd82e2f7 100644 --- a/software/FusionX/src/ttymz/sharpmz.c +++ b/software/FusionX/src/ttymz/sharpmz.c @@ -542,21 +542,21 @@ static t_scanCodeMap scanCodeMap[] = { CTRL_G , // ^G F7 CTRL_H , // ^H F8 // S5 28 - 2F - NOKEY , - NOKEY , - NOKEY , - NOKEY , + HOTKEY_ORIGINAL, // 1 - Hotkey to invoke original mode. + HOTKEY_RFS40, // 2 - Hotkey to invoke RFS 40 mode. + HOTKEY_TZFS, // 3 - Hotkey to invoke TZFS mode. + HOTKEY_LINUX, // 4 - Hotkey to invoke Linux mode. NOKEY , NOKEY , NOKEY , NOKEY , // S6 30 - 37 (ERROR? 7 VALUES ONLY!!) - NOKEY , // ^YEN E6 - CTRL_CAPPA , // ^ EF + NOKEY , // ^YEN E6 + CTRL_CAPPA , // ^ EF NOKEY , NOKEY , NOKEY , - CTRL_UNDSCR , // ^, + CTRL_UNDSCR , // ^, NOKEY , NOKEY , // S7 - 38 - 3F @@ -567,22 +567,22 @@ static t_scanCodeMap scanCodeMap[] = { NOKEY , NOKEY , NOKEY , - CTRL_SLASH , // ^/ EE + CTRL_SLASH , // ^/ EE // S8 40 - 47 - modifier keys. - NOKEY , // BREAK - CTRL+BREAK - not yet assigned - NOKEY , // CTRL + NOKEY , // BREAK - CTRL+BREAK - not yet assigned + NOKEY , // CTRL NOKEY , NOKEY , NOKEY , NOKEY , NOKEY , - NOKEY , // SHIFT + NOKEY , // SHIFT // S9 48 - 4F - Function keys. - FUNC1 , // Function key F1 - FUNC2 , // Function key F2 - FUNC3 , // Function key F3 - FUNC4 , // Function key F4 - FUNC5 , // Function key F5 + FUNC1 , // Function key F1 + FUNC2 , // Function key F2 + FUNC3 , // Function key F3 + FUNC4 , // Function key F4 + FUNC5 , // Function key F5 NOKEY , NOKEY , NOKEY @@ -681,8 +681,8 @@ static t_scanCodeMap scanCodeMap[] = { NOKEY }} }; -#endif -#if (TARGET_HOST_MZ80A == 1) + +#elif (TARGET_HOST_MZ80A == 1) static t_scanCodeMap scanCodeMap[] = { // 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 // 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. 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. // 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), @@ -1249,6 +1748,11 @@ static t_ansiKeyMap ansiKeySeq[] = { // 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. // WRITE_HARDWARE(1, MBADDR_KEYPF, 0x8A); // 10001010 CTRL WORD MODE0 @@ -1312,10 +1816,11 @@ void mzBeep(uint32_t freq, uint32_t timeout) // Locals. #if (TARGET_HOST_MZ80A == 1) uint16_t freqDiv = TIMER_8253_MZ80A_FREQ/(freq*2); - #else + #elif (TARGET_HOST_MZ700 == 1) uint16_t freqDiv = TIMER_8253_MZ700_FREQ/freq; #endif + #if (TARGET_HOST_MZ2000 == 0) // 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_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. audio.audioStopTimer = timeout == 0 ? 11 : (timeout/10)+1; // Each unit is 10ms, valid range 1..n + #endif return; } @@ -2964,8 +3470,33 @@ uint8_t mzSweepKeys(void) { 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. // if((keyboard.scanbuf[0][0] & 0x01) == 0) diff --git a/software/FusionX/src/ttymz/sharpmz.h b/software/FusionX/src/ttymz/sharpmz.h index a8fe6ccd1..426791f7b 100755 --- a/software/FusionX/src/ttymz/sharpmz.h +++ b/software/FusionX/src/ttymz/sharpmz.h @@ -36,182 +36,210 @@ extern "C" { #endif -#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700 -#define TARGET_HOST_MZ2000 0 // MZ2000 -#define TARGET_HOST_MZ80A 1 // MZ80A +// 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 // Video display constants. -#define VC_MAX_ROWS 25 // Maximum number of rows on display. -#define VC_MAX_COLUMNS 80 // Maximum number of columns on display. -#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_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. +#endif +#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. // Keyboard constants. -#define KEYB_AUTOREPEAT_INITIAL_TIME 800 // Time in milliseconds before starting autorepeat. -#define KEYB_AUTOREPEAT_TIME 100 // Time in milliseconds between auto repeating characters. -#define KEYB_FLASH_TIME 350 // Time in milliseconds for the cursor flash change. -#define CURSOR_THICK_BLOCK 0x43 // Thick block cursor for lower case CAPS OFF -#define CURSOR_BLOCK 0xEF // Block cursor for SHIFT Lock. -#define CURSOR_UNDERLINE 0x3E // Thick underscore for CAPS Lock. -#define MAX_KEYB_BUFFER_SIZE 32 // Maximum size of the keyboard buffer. +#define KEYB_AUTOREPEAT_INITIAL_TIME 800 // Time in milliseconds before starting autorepeat. +#define KEYB_AUTOREPEAT_TIME 100 // Time in milliseconds between auto repeating characters. +#define KEYB_FLASH_TIME 350 // Time in milliseconds for the cursor flash change. +#define CURSOR_THICK_BLOCK 0x43 // Thick block cursor for lower case CAPS OFF +#define CURSOR_BLOCK 0xEF // Block cursor for SHIFT Lock. +#define CURSOR_UNDERLINE 0x3E // Thick underscore for CAPS Lock. +#define MAX_KEYB_BUFFER_SIZE 32 // Maximum size of the keyboard buffer. // Audio constants. -#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_MZ80A_FREQ 2000000 // 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. -#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller. -#define VIDEO_VRAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D000 // Base address of the character video RAM using direct addressing. -#define VIDEO_VRAM_SIZE 0x800 // Size of the video RAM. -#define VIDEO_ARAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D800 // Base address of the character attribute RAM using direct addressing. -#define VIDEO_ARAM_SIZE 0x800 // Size of the attribute RAM. +#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller. +#define VIDEO_VRAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D000 // Base address of the character video RAM using direct addressing. +#define VIDEO_VRAM_SIZE 0x800 // Size of the video RAM. +#define VIDEO_ARAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D800 // Base address of the character attribute RAM using direct addressing. +#define VIDEO_ARAM_SIZE 0x800 // Size of the attribute RAM. // Video Module control bits. -#define VMMODE_MASK 0xF8 // Mask to mask out video mode. -#define VMMODE_MZ80K 0x00 // Video mode = MZ80K -#define VMMODE_MZ80C 0x01 // Video mode = MZ80C -#define VMMODE_MZ1200 0x02 // Video mode = MZ1200 -#define VMMODE_MZ80A 0x03 // Video mode = MZ80A -#define VMMODE_MZ700 0x04 // Video mode = MZ700 -#define VMMODE_MZ800 0x05 // Video mode = MZ800 -#define VMMODE_MZ1500 0x06 // Video mode = MZ1500 -#define VMMODE_MZ80B 0x07 // Video mode = MZ80B -#define VMMODE_MZ2000 0x08 // Video mode = MZ2000 -#define VMMODE_MZ2200 0x09 // Video mode = MZ2200 -#define VMMODE_MZ2500 0x0A // Video mode = MZ2500 -#define VMMODE_80CHAR 0x80 // Enable 80 character display. -#define VMMODE_80CHAR_MASK 0x7F // Mask to filter out display width control bit. -#define VMMODE_COLOUR 0x20 // Enable colour display. -#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit. +#define VMMODE_MASK 0xF8 // Mask to mask out video mode. +#define VMMODE_MZ80K 0x00 // Video mode = MZ80K +#define VMMODE_MZ80C 0x01 // Video mode = MZ80C +#define VMMODE_MZ1200 0x02 // Video mode = MZ1200 +#define VMMODE_MZ80A 0x03 // Video mode = MZ80A +#define VMMODE_MZ700 0x04 // Video mode = MZ700 +#define VMMODE_MZ800 0x05 // Video mode = MZ800 +#define VMMODE_MZ1500 0x06 // Video mode = MZ1500 +#define VMMODE_MZ80B 0x07 // Video mode = MZ80B +#define VMMODE_MZ2000 0x08 // Video mode = MZ2000 +#define VMMODE_MZ2200 0x09 // Video mode = MZ2200 +#define VMMODE_MZ2500 0x0A // Video mode = MZ2500 +#define VMMODE_80CHAR 0x80 // Enable 80 character display. +#define VMMODE_80CHAR_MASK 0x7F // Mask to filter out display width control bit. +#define VMMODE_COLOUR 0x20 // Enable colour display. +#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit. // Sharp MZ colour attributes. -#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute. -#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute. -#define VMATTR_FG_RED 0x20 // Foreground red character attribute. -#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute. -#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute. -#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute. -#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute. -#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute. -#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute. -#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute. -#define VMATTR_BG_BLACK 0x00 // Background black character attribute. -#define VMATTR_BG_BLUE 0x01 // Background blue character attribute. -#define VMATTR_BG_RED 0x02 // Background red character attribute. -#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute. -#define VMATTR_BG_GREEN 0x04 // Background green character attribute. -#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute. -#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute. -#define VMATTR_BG_WHITE 0x07 // Background white character attribute. -#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute. -#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute. +#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute. +#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute. +#define VMATTR_FG_RED 0x20 // Foreground red character attribute. +#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute. +#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute. +#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute. +#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute. +#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute. +#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute. +#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute. +#define VMATTR_BG_BLACK 0x00 // Background black character attribute. +#define VMATTR_BG_BLUE 0x01 // Background blue character attribute. +#define VMATTR_BG_RED 0x02 // Background red character attribute. +#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute. +#define VMATTR_BG_GREEN 0x04 // Background green character attribute. +#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute. +#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute. +#define VMATTR_BG_WHITE 0x07 // Background white character attribute. +#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute. +#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute. // Sharp MZ constants. // -#define MBADDR_KEYPA 0xE000 // Mainboard 8255 Port A -#define MBADDR_KEYPB 0xE001 // Mainboard 8255 Port B -#define MBADDR_KEYPC 0xE002 // Mainboard 8255 Port C -#define MBADDR_KEYPF 0xE003 // Mainboard 8255 Mode Control -#define MBADDR_CSTR 0xE002 // Mainboard 8255 Port C -#define MBADDR_CSTPT 0xE003 // Mainboard 8255 Mode Control -#define MBADDR_CONT0 0xE004 // Mainboard 8253 Counter 0 -#define MBADDR_CONT1 0xE005 // Mainboard 8253 Counter 1 -#define MBADDR_CONT2 0xE006 // Mainboard 8253 Counter 1 -#define MBADDR_CONTF 0xE007 // Mainboard 8253 Mode Control -#define MBADDR_SUNDG 0xE008 // Register for reading the tempo timer status (cursor flash). horizontal blank and switching sound on/off. -#define MBADDR_TEMP 0xE008 // As above, different name used in original source when writing. -#define MBADDR_MEMSW 0xE00C // Memory swap, 0000->C000, C000->0000 -#define MBADDR_MEMSWR 0xE010 // Reset memory swap. -#define MBADDR_NRMDSP 0xE014 // Return display to normal. -#define MBADDR_INVDSP 0xE015 // Invert display. -#define MBADDR_SCLDSP 0xE200 // Hardware scroll, a read to each location adds 8 to the start of the video access address therefore creating hardware scroll. 00 - reset to power up -#define MBADDR_SCLBASE 0xE2 // High byte scroll base. -#define MBADDR_DSPCTL 0xDFFF // Display 40/80 select register (bit 7) +#define MBADDR_KEYPA 0xE000 // Mainboard 8255 Port A +#define MBADDR_KEYPB 0xE001 // Mainboard 8255 Port B +#define MBADDR_KEYPC 0xE002 // Mainboard 8255 Port C +#define MBADDR_KEYPF 0xE003 // Mainboard 8255 Mode Control +#define MBADDR_CSTR 0xE002 // Mainboard 8255 Port C +#define MBADDR_CSTPT 0xE003 // Mainboard 8255 Mode Control +#define MBADDR_CONT0 0xE004 // Mainboard 8253 Counter 0 +#define MBADDR_CONT1 0xE005 // Mainboard 8253 Counter 1 +#define MBADDR_CONT2 0xE006 // Mainboard 8253 Counter 1 +#define MBADDR_CONTF 0xE007 // Mainboard 8253 Mode Control +#define MBADDR_SUNDG 0xE008 // Register for reading the tempo timer status (cursor flash). horizontal blank and switching sound on/off. +#define MBADDR_TEMP 0xE008 // As above, different name used in original source when writing. +#define MBADDR_MEMSW 0xE00C // Memory swap, 0000->C000, C000->0000 +#define MBADDR_MEMSWR 0xE010 // Reset memory swap. +#define MBADDR_NRMDSP 0xE014 // Return display to normal. +#define MBADDR_INVDSP 0xE015 // Invert display. +#define MBADDR_SCLDSP 0xE200 // Hardware scroll, a read to each location adds 8 to the start of the video access address therefore creating hardware scroll. 00 - reset to power up +#define MBADDR_SCLBASE 0xE2 // High byte scroll base. +#define MBADDR_DSPCTL 0xDFFF // Display 40/80 select register (bit 7) //Common character definitions. -#define SCROLL 0x01 // Set scroll direction UP. -#define BELL 0x07 -#define ENQ 0x05 -#define SPACE 0x20 -#define TAB 0x09 // TAB ACROSS (8 SPACES FOR SD-BOARD) -#define CR 0x0D -#define LF 0x0A -#define FF 0x0C -#define DELETE 0x7F -#define BACKS 0x08 -#define SOH 0x01 // For XModem etc. -#define EOT 0x04 -#define ACK 0x06 -#define NAK 0x15 -#define NUL 0x00 -//#define NULL 0x00 -#define CTRL_A 0x01 -#define CTRL_B 0x02 -#define CTRL_C 0x03 -#define CTRL_D 0x04 -#define CTRL_E 0x05 -#define CTRL_F 0x06 -#define CTRL_G 0x07 -#define CTRL_H 0x08 -#define CTRL_I 0x09 -#define CTRL_J 0x0A -#define CTRL_K 0x0B -#define CTRL_L 0x0C -#define CTRL_M 0x0D -#define CTRL_N 0x0E -#define CTRL_O 0x0F -#define CTRL_P 0x10 -#define CTRL_Q 0x11 -#define CTRL_R 0x12 -#define CTRL_S 0x13 -#define CTRL_T 0x14 -#define CTRL_U 0x15 -#define CTRL_V 0x16 -#define CTRL_W 0x17 -#define CTRL_X 0x18 -#define CTRL_Y 0x19 -#define CTRL_Z 0x1A -#define ESC 0x1B -#define CTRL_SLASH 0x1C -#define CTRL_LB 0x1B -#define CTRL_RB 0x1D -#define CTRL_CAPPA 0x1E -#define CTRL_UNDSCR 0x1F -#define CTRL_AT 0x00 -#define FUNC1 0x80 -#define FUNC2 0x81 -#define FUNC3 0x82 -#define FUNC4 0x83 -#define FUNC5 0x84 -#define FUNC6 0x85 -#define FUNC7 0x86 -#define FUNC8 0x87 -#define FUNC9 0x88 -#define FUNC10 0x89 -#define PAGEUP 0xE0 -#define PAGEDOWN 0xE1 -#define CURHOMEKEY 0xE2 -#define ALPHAGRAPHKEY 0xE3 -#define HOTKEY_ORIGINAL 0xE8 -#define HOTKEY_RFS80 0xE9 -#define HOTKEY_RFS40 0xEA -#define HOTKEY_TZFS 0xEB -#define HOTKEY_LINUX 0xEC -#define NOKEY 0xF0 -#define CURSRIGHT 0xF1 -#define CURSLEFT 0xF2 -#define CURSUP 0xF3 -#define CURSDOWN 0xF4 -#define DBLZERO 0xF5 -#define INSERT 0xF6 -#define CLRKEY 0xF7 -#define HOMEKEY 0xF8 -#define ENDKEY 0xF9 -#define ANSITGLKEY 0xFA -#define BREAKKEY 0xFB -#define GRAPHKEY 0xFC -#define ALPHAKEY 0xFD -#define DEBUGKEY 0xFE // Special key to enable debug features such as the ANSI emulation. +#define SCROLL 0x01 // Set scroll direction UP. +#define BELL 0x07 +#define ENQ 0x05 +#define SPACE 0x20 +#define TAB 0x09 // TAB ACROSS (8 SPACES FOR SD-BOARD) +#define CR 0x0D +#define LF 0x0A +#define FF 0x0C +#define DELETE 0x7F +#define BACKS 0x08 +#define SOH 0x01 // For XModem etc. +#define EOT 0x04 +#define ACK 0x06 +#define NAK 0x15 +#define NUL 0x00 +//#define NULL 0x00 +#define CTRL_A 0x01 +#define CTRL_B 0x02 +#define CTRL_C 0x03 +#define CTRL_D 0x04 +#define CTRL_E 0x05 +#define CTRL_F 0x06 +#define CTRL_G 0x07 +#define CTRL_H 0x08 +#define CTRL_I 0x09 +#define CTRL_J 0x0A +#define CTRL_K 0x0B +#define CTRL_L 0x0C +#define CTRL_M 0x0D +#define CTRL_N 0x0E +#define CTRL_O 0x0F +#define CTRL_P 0x10 +#define CTRL_Q 0x11 +#define CTRL_R 0x12 +#define CTRL_S 0x13 +#define CTRL_T 0x14 +#define CTRL_U 0x15 +#define CTRL_V 0x16 +#define CTRL_W 0x17 +#define CTRL_X 0x18 +#define CTRL_Y 0x19 +#define CTRL_Z 0x1A +#define ESC 0x1B +#define CTRL_SLASH 0x1C +#define CTRL_LB 0x1B +#define CTRL_RB 0x1D +#define CTRL_CAPPA 0x1E +#define CTRL_UNDSCR 0x1F +#define CTRL_AT 0x00 +#define FUNC1 0x80 +#define FUNC2 0x81 +#define FUNC3 0x82 +#define FUNC4 0x83 +#define FUNC5 0x84 +#define FUNC6 0x85 +#define FUNC7 0x86 +#define FUNC8 0x87 +#define FUNC9 0x88 +#define FUNC10 0x89 +#define PAGEUP 0xE0 +#define PAGEDOWN 0xE1 +#define CURHOMEKEY 0xE2 +#define ALPHAGRAPHKEY 0xE3 +#define HOTKEY_ORIGINAL 0xE8 +#define HOTKEY_RFS80 0xE9 +#define HOTKEY_RFS40 0xEA +#define HOTKEY_TZFS 0xEB +#define HOTKEY_LINUX 0xEC +#define NOKEY 0xF0 +#define CURSRIGHT 0xF1 +#define CURSLEFT 0xF2 +#define CURSUP 0xF3 +#define CURSDOWN 0xF4 +#define DBLZERO 0xF5 +#define INSERT 0xF6 +#define CLRKEY 0xF7 +#define HOMEKEY 0xF8 +#define ENDKEY 0xF9 +#define ANSITGLKEY 0xFA +#define BREAKKEY 0xFB +#define GRAPHKEY 0xFC +#define ALPHAKEY 0xFD +#define DEBUGKEY 0xFE // Special key to enable debug features such as the ANSI emulation. // Macros. // @@ -285,7 +313,7 @@ typedef struct { uint8_t dispCode; } 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 { uint8_t scanCode[80]; diff --git a/software/FusionX/src/ttymz/z80io.c b/software/FusionX/src/ttymz/z80io.c index 084eeaf91..ca816832a 100644 --- a/software/FusionX/src/ttymz/z80io.c +++ b/software/FusionX/src/ttymz/z80io.c @@ -431,7 +431,7 @@ uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData) //-------------------------------------------------------- // Test Methods. //-------------------------------------------------------- -#ifdef INCLUDE_TEST_METHODS +#if defined(INCLUDE_TEST_METHODS) && INCLUDE_TEST_METHODS == 1 #include "z80io_test.c" #else uint8_t z80io_Z80_TestMemory(void) diff --git a/software/FusionX/src/ttymz/z80io.h b/software/FusionX/src/ttymz/z80io.h index 459e941f0..74b393b33 100755 --- a/software/FusionX/src/ttymz/z80io.h +++ b/software/FusionX/src/ttymz/z80io.h @@ -39,7 +39,7 @@ #endif // Definitions to control compilation. -//#define INCLUDE_TEST_METHODS 0 +#define INCLUDE_TEST_METHODS 0 // CPLD Commands. #define CPLD_CMD_FETCH_ADDR 0x10 @@ -82,6 +82,14 @@ #define CPLD_CMD_READIO_ADDR_P5 0x35 #define CPLD_CMD_READIO_ADDR_P6 0x36 #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_REFRESH 0x51 #define CPLD_CMD_SET_SIGROUP1 0xF0 @@ -386,8 +394,8 @@ #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_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4) -#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ - MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ +#define SPI_SEND_8(_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); \ @@ -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_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; \ - MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ 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);\ 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); \ @@ -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_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); \ +#define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \ + MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ + MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \ 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); \ @@ -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_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ } -#define SPI_SEND32i(_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); \ - pr_info("Stage 0");\ - while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ - pr_info("Stage 1");\ +#define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \ + MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ + MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \ + 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); \ - pr_info("Stage 2");\ - timeout = MAX_CHECK_CNT; \ - while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \ - pr_info("Stage 3");\ + 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_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_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ } - // while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; }; // read 2 byte #define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2)) // write 2 byte diff --git a/software/FusionX/src/z80drv/MZ2000/z80ctrl.c b/software/FusionX/src/z80drv/MZ2000/z80ctrl.c deleted file mode 100644 index 6e7565c6a..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80ctrl.c +++ /dev/null @@ -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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 = 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 # 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(" -- # 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); -} diff --git a/software/FusionX/src/z80drv/MZ2000/z80driver.c b/software/FusionX/src/z80drv/MZ2000/z80driver.c deleted file mode 100644 index 019e605bc..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80driver.c +++ /dev/null @@ -1,1616 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Name: z80driver.c -// Created: Oct 2022 -// Author(s): Philip Smart -// Description: Z80 Driver -// This file contains the methods used to create a linux device driver which provides -// the services of a Z80 CPU emulation and the control of an underlying Z80'less host -// system. In essence this driver is the host Z80 CPU. -// 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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" -#include "z80menu.h" -#include "z80driver.h" - -#include -#include -#include - -/* Meta Information */ -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Philip D Smart"); -MODULE_DESCRIPTION("Z80 CPU Emulator and Hardware Interface Driver"); - -/* Global variables for the threads */ -static struct task_struct *kthread_z80; -static int threadId_z80 = 1; - -// Device class and major numbers. -static struct class *class; -static struct device *device; -static int major; - -// CPU Instance. -static Z80 Z80CPU; - -// Z80 Control data. -static t_Z80Ctrl *Z80Ctrl = NULL; - -// Runtime control of the CPU. As the CPU runs in a detached thread on core 1, the cpu needs to be suspended before any external -// operations can take place. This is achieved with the runtime mutex. -enum Z80_RUN_STATES Z80RunMode; -static struct mutex Z80RunModeMutex; -static DEFINE_MUTEX(Z80DRV_MUTEX); - - -//------------------------------------------------------------------------------------------------------------------------------- -// -// Z80 CPU Kernel Logic. -// -// THe Z80 CPU is initialised and set running, processing instructions either from the underlying host hardware or internal -// memory. The configuration and flow is controlled via the Z80Ctrl structure which is User Space accessible. -// -//------------------------------------------------------------------------------------------------------------------------------- - -// Method to read a byte from physical hardware or internal virtual memory/devices. -// The page table indicates the source and the read is processed accordingly. -static zuint8 z80_read(void *context, zuint16 address) -{ - // Locals. - // - zuint8 data; -// uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // Only read if the address is in physical RAM. - if(isPhysical(address)) - { - // Commence cycle to retrieve the data from Real RAM. - // Optimise SPI according to last address sent to CPLD. -// if(addrDiff >=0 && addrDiff < 8) -// { -// SPI_SEND8((CPLD_CMD_READ_ADDR + addrDiff)); -// Z80Ctrl->z80PrevAddr += addrDiff; -// } else -// { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = address; -// } - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. - // while(CPLD_LAST_TSTATE() == 0); - } - else if(isVirtualHW(address)) - { - // Virtual Hardware - call the handler. - switch(realAddress(address)) - { - default: - break; - } - } - else if(isVirtualMemory(address)) - { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); -// if(address >= 0xCF01 && address < 0xCF0A) -// pr_info("Read:0x%x, 0x%x, 0x%x", address, data, isVirtualROM(address) ? getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE : getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK)); - } - - // Keyport data? Store. - if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 8 && (data & 0x41) == 0) - { - Z80Ctrl->keyportShiftCtrl = 0x01; - } else - if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 0 && (data & 0x80) == 0) - { - Z80Ctrl->keyportHotKey = 0x01; - } - return(data); -} - -// Method to write a byte to physical hardware or internal virtual memory or devices. -// The page table indicates the target and the write is processed accordingly. -static void z80_write(void *context, zuint16 address, zuint8 data) -{ - // Locals. - // uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // To detect Hotkey presses, we need to store the keboard strobe data and on keydata read. - if(isHW(address) && address == 0xE000) - { - Z80Ctrl->keyportStrobe = data; - } - - // Write to physical host? - if(isPhysical(address)) - { - // Commence cycle to write the data to real RAM. - // Optimise SPI according to last address sent to CPLD. -// if(addrDiff >=0 && addrDiff < 8) -// { -//if(address >= 0xD010 && address < 0xD017) { data = 0x41; } -// SPI_SEND16((data << 8) | (CPLD_CMD_WRITE_ADDR + addrDiff)); -// Z80Ctrl->z80PrevAddr += addrDiff; -// } else -// { - SPI_SEND32((uint32_t)address << 16 | data << 8 | CPLD_CMD_WRITE_ADDR); - Z80Ctrl->z80PrevAddr = address; -// } - - // Write-thru to virtual memory if we update real memory. - // if(isPhysicalRAM(address)) - // writeVirtualRAM(address, data); - -// if(address >= 0xD000 && address < 0xD018) -// pr_info("Write:0x%x, 0x%x, 0x%x", address, data, isVirtualROM(address) ? getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE : getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK)); - - // Pause until the Last T-State is detected. - // while(CPLD_LAST_TSTATE() == 0); - } - else if(isVirtualHW(address)) - { - // Virtual Hardware - call the handler. - switch(realAddress(address)) - { - default: - break; - } - } - else if(isVirtualRAM(address)) - { - // Update virtual memory. - writeVirtualRAM(address, data); -//pr_info("Write:0x%x, 0x%x, 0x%x", address, data, isVirtualROM(address) ? getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE : getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK)); - } - // Cannot write to virtual ROM so no logic. -} - -// Primary Opcode fetch method. This method is called each time a single or multi-byte opcode is -// encountered. Opcode data is retrieved via the z80_fetch method. -// -// Depending on the address and the configured page map, the opcode is fetched from hardware -// or internal virtual memory. As this method is the primary timing method for Z80 instructions -// (read/write methods dont affect the timing so much as long as they operate in less than the read/write -// cycle of an original Z80). -// Initially the timing on the virtual memory is set by a governor delay but this will be updated to a more -// precise M/T-State cycle per instruction type delay. -static zuint8 z80_fetch_opcode(void *context, zuint16 address) -{ - // Locals. - zuint8 opcode = 0x00; -// uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - volatile uint32_t idx; // Leave as volatile otherwise optimiser will optimise out the delay code. - Z_UNUSED(context) - - // Normally only opcode fetches occur in RAM but allow any physical address as it could be a Z80 programming trick. - if(isPhysical(address)) - { - // Commence cycle to fetch the opcode from potentially Real RAM albeit it could be any physical hardware. - // Optimise SPI according to last address sent to CPLD. -// if(addrDiff >=0 && addrDiff < 8) -// { -// SPI_SEND8((CPLD_CMD_FETCH_ADDR + addrDiff)); -// Z80Ctrl->z80PrevAddr += addrDiff; -// } else -// { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_FETCH_ADDR); - Z80Ctrl->z80PrevAddr = address; -// } - while(CPLD_READY() == 0); - opcode = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. -// while(CPLD_LAST_TSTATE() == 0); - } else - // Virtual fetches only occur in memory as we are not emulating original hardware. - if(isVirtualMemory(address)) - { - // for(idx=0; idx < Z80Ctrl->cpuGovernorDelay; idx++); - // for(idx=0; idx < 238; idx++); - // 243 loaded - - // Retrieve data from virtual memory. - // opcode = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - - if(isVirtualROM(address)) - { - opcode = readVirtualROM(address); - for(idx=0; idx < Z80Ctrl->cpuGovernorDelayROM; idx++); - } else - { - opcode = readVirtualRAM(address); - for(idx=0; idx < Z80Ctrl->cpuGovernorDelayRAM; idx++); - } - } -//if(opcode == 0xED && address > 0x270) -// pr_info("Fetch:0x%x, 0x%x, 0x%x", address, opcode, isVirtualROM(address) ? getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK) + Z80_VIRTUAL_RAM_SIZE : getPageAddr(address, MEMORY_TYPE_VIRTUAL_MASK)); - return(opcode); -} - -// Method similar to z80_read, kept seperate to avoid additional what-if logic and doesnt require virtual hardware logic. -// -static zuint8 z80_fetch(void *context, zuint16 address) -{ - // Locals. - // - zuint8 data = 0x00; -// uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // Normally only opcode fetches occur in RAM but allow any physical address as it could be a Z80 programming trick. - if(isPhysical(address)) - { - // Commence cycle to retrieve the data from Real RAM. - // Optimise SPI according to last address sent to CPLD. -// if(addrDiff >=0 && addrDiff < 8) -// { -// SPI_SEND8((CPLD_CMD_READ_ADDR + addrDiff)); -// Z80Ctrl->z80PrevAddr += addrDiff; -// } else -// { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = address; -// } - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. -// while(CPLD_LAST_TSTATE() == 0); - } else - if(isVirtualMemory(address)) - { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - } -//pr_info("(0x%x)", data); - - return(data); -} - -// Method to perform a Z80 input operation. This normally goes to hardware and the CPLD executes the required cycle. -// Some ports are dedicated virtual ports providing virtual services to the host computer/application. These are intercepted -// and processed in this driver. -static zuint8 z80_in(void *context, zuint16 port) -{ - // Locals. - zuint8 value; -// uint16_t portDiff = (uint16_t)port - Z80Ctrl->z80PrevPort; - Z_UNUSED(context) - - // Physical port go direct to hardware to retrieve value. - if(isPhysicalIO(port)) - { - // Commence cycle to retrieve the value from the I/O port. Port contains the 16bit BC value. - // Optimise SPI according to last port sent to CPLD. -// if(portDiff >=0 && portDiff < 8) -// { -// SPI_SEND8((CPLD_CMD_READIO_ADDR + portDiff)); -// Z80Ctrl->z80PrevPort += portDiff; -// } else -// { - SPI_SEND32((uint32_t)port << 16 | CPLD_CMD_READIO_ADDR); - Z80Ctrl->z80PrevPort = port; -// } - - // Whilst waiting for the CPLD, we now determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { - // Port is not a memory management port. - default: - break; - } - - // Finally ensure the data from the port is ready and retrieve it. - while(CPLD_READY() == 0); - value = READ_CPLD_DATA_IN(); - } else - // Virtual I/O Port. - { - // Virtual I/O - call the handler. - switch(realPort(port)) - { - default: - value = 0x00; - break; - } - } -// if((port&0x00ff) == 0xE8) -// pr_info("z80_in:0x%x, 0x%x\n", port, value); - return(value); -} - -// Method to perform a Z80 output operation. This normally goes to hardware and the CPLD executes the required cycle. -// Some ports are dedicated virtual ports providing virtual services to the host computer/application. These are intercepted -// and processed in this driver. -// There are also ports which are both hardware and need mirroring in software. These ports, typically memory mapping ports. -// when activated in the hardware need to be mirrored in the page table so correct virtual memory is used when addressed. -static void z80_out(void *context, zuint16 port, zuint8 value) -{ - // Locals. - uint32_t idx; -// uint16_t portDiff = (uint16_t)port - Z80Ctrl->z80PrevPort; - Z_UNUSED(context) - -//if((port&0xff) == 0xe8) -//{ -// value |= 0xC0; -//} -//udelay(10); - // Physical port go direct to hardware to retrieve value. - if(isPhysicalIO(port)) - { - // Commence cycle to write the value to the I/O port. Port contains the 16bit BC value. - // Optimise SPI according to last port sent to CPLD. -// if(portDiff >=0 && portDiff < 8) -// { -// SPI_SEND16((value << 8) | (CPLD_CMD_WRITEIO_ADDR + portDiff)); -// Z80Ctrl->z80PrevPort += portDiff; -// if((port&0x00ff) == 0xE8) -//pr_info("OUT:0x%x(%x)=0x%x\n", port, portDiff, value); -// } else -// { - SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); - Z80Ctrl->z80PrevPort = port; -// if((port&0x00ff) == 0xE8) -// { -// SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); -// udelay(10); -// SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); -// udelay(10); -// SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); -// udelay(10); -// SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); -// udelay(10); -//pr_info("OUT:0x%x=0x%x\n", port, value); -// } -// } - -//udelay(10); - // Determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { -#if(TARGET_HOST_MZ2000 == 1) - case IO_ADDR_E0: - break; - - case IO_ADDR_E1: - break; - - case IO_ADDR_E2: - break; - - case IO_ADDR_E3: - // Program control register. - if(value & 0x80) - { - } else - { - switch((value >> 1) & 0x07) - { - // NST toggle. - case 1: - // NST pages in all RAM and resets cpu. - if(value & 0x01) - { - Z80Ctrl->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); - } - } - z80_instant_reset(&Z80CPU); - } - break; - - default: - break; - } - } - break; - - case IO_ADDR_E8: - // NEED FLAG TO SET THIS WHEN CALLED WITH NON MEMORY SWITCH BYTE - if(isPhysical(0xD000) && (value & 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, (Z80Ctrl->lowMemorySwap ? idx - 0x8000 : idx)); - } - } - } else - if(value & 0x80) - { - if(value & 0x40) - { - setMemoryType(0xD000/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, 0xD000); - } else - { - for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) - { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx); - } - } - } - break; -#endif - -#if(TARGET_HOST_MZ700 == 1) - // 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_PHYSICAL_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_PHYSICAL_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_PHYSICAL_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_PHYSICAL_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; -#endif - - // Port is not a memory management port. - default: - break; - } - } else - if(isVirtualIO(port)) - { - // 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 to the state prior to the complimentary command being invoked. - - // Determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { -#if(TARGET_HOST_MZ700 == 1) - // 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; -#endif - - // Port is not a memory management port. - default: - break; - } - } else - { - // Virtual I/O - call the handler. - switch(realPort(port)) - { - default: - break; - } - } -//pr_info("z80_out:0x%x, 0x%x\n", port, value); -} - -// NOP - No Operation method. This instruction is used for timing, padding out an application or during -// HALT cycles to ensure Refresh occurs. -// If the address is configured as hardware (via the page table) then a refresh cycle is requested otherwise -// nothing to be done. -static zuint8 z80_nop(void *context, zuint16 address) -{ - // Locals. - Z_UNUSED(context) - - if(isPhysical(address)) - { - // If autorefresh is not enabled, send a single refresh request. - if(Z80Ctrl->refreshDRAM == 0) - SPI_SEND8(CPLD_CMD_REFRESH); -pr_info("NOP"); - } - return 0x00; -} - -// HALT - CPU executes a HALT instruction which results in the HALT line going active low and then it enters -// a state executing NOP instructions to ensure DRAM refresh until a reset or INT event. -static void z80_halt(void *context, zboolean state) -{ - // Locals. - Z_UNUSED(context) Z_UNUSED(state) - - // Inform CPLD of halt state. - printk("z80_halt\n"); - SPI_SEND8(CPLD_CMD_HALT); - Z80CPU.cycles = Z80_MAXIMUM_CYCLES; -} - -// Methods below are not yet implemented, Work In Progress! -static zuint8 z80_context(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_context\n"); - return 0x00; -} -static zuint8 z80_nmia(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_nmia\n"); - return 0x00; -} -static zuint8 z80_inta(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_inta\n"); - return 0x00; -} -static zuint8 z80_intFetch(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_int_fetch\n"); - return 0x00; -} -static void z80_ldia(void *context) -{ - Z_UNUSED(context) - printk("z80_ldia\n"); -} -static void z80_ldra(void *context) -{ - Z_UNUSED(context) - printk("z80_ldra\n"); -} -static void z80_reti(void *context) -{ - Z_UNUSED(context) - printk("z80_reti\n"); -} -static void z80_retn(void *context) -{ - Z_UNUSED(context) - printk("z80_retn\n"); -} -static zuint8 z80_illegal(void *context, zuint8 opcode) -{ - Z_UNUSED(context) - printk("z80_illegal\n"); - return 0x00; -} - -// Z80 CPU Emulation Thread -// ------------------------ -// This is a kernel thread, bound to CPU 1 with IRQ's disabled. -// The Z80 is controlled by a mutex protected variable to define run, stop, pause and terminate modes. -int thread_z80(void * thread_nr) -{ - // Locals. - uint8_t canRun = 0; - int t_nr = *(int *) thread_nr; - //struct sched_param param = {.sched_priority = 99}; - spinlock_t spinLock; - unsigned long flags; - - // Initialise spinlock and disable IRQ's. We should be the only process running on core 1. - spin_lock_init(&spinLock); - spin_lock_irqsave(&spinLock, flags); - - // Assign this emulation to high priority realtime scheduling. Also the task will be assigned to an isolated CPU. - //sched_setscheduler(current, SCHED_RR, ¶m); - - // Run the CPU forever or until a stop occurs. - while(!kthread_should_stop()) - { - // Run the Z80 emulation if enabled. - if(canRun) z80_run(&Z80CPU, 100); - - // Reset pressed? - if(CPLD_RESET()) - { - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); - - // Wait for release before restarting CPU. - while(CPLD_RESET()); - } else - { - // Update state to indicate request has been actioned. - mutex_lock(&Z80RunModeMutex); - if(Z80RunMode == Z80_STOP) Z80RunMode = Z80_STOPPED; - if(Z80RunMode == Z80_PAUSE) Z80RunMode = Z80_PAUSED; - if(Z80RunMode == Z80_CONTINUE) Z80RunMode = Z80_RUNNING; - if(Z80RunMode == Z80_RUNNING) canRun=1; else canRun=0; - mutex_unlock(&Z80RunModeMutex); - - // Hotkey pressed? Bring up user menu. - if(Z80Ctrl->keyportShiftCtrl && Z80Ctrl->keyportHotKey) - { - z80menu(); - Z80Ctrl->keyportShiftCtrl = 0; - Z80Ctrl->keyportHotKey = 0; - } - } - } - - // Release spinlock as we are unloading driver. - spin_unlock_irqrestore(&spinLock, flags); - printk("kthread - Z80 Thread %d finished execution!\n", t_nr); - return 0; -} - - -//------------------------------------------------------------------------------------------------------------------------------- -// -// User space driver access. -// -//------------------------------------------------------------------------------------------------------------------------------- - - -// Device close. -// When a user space application terminates or closes the z80drv device driver, this function is called -// to close any open connections, memory and variables required to handle the user space application -// requests. -static int z80drv_release(struct inode *inodep, struct file *filep) -{ - // Locals. - - mutex_unlock(&Z80DRV_MUTEX); - //pr_info("z80drv: Device successfully closed\n"); - - return(0); -} - -// Device open. -// When a user space application open's the z80drv device driver, this function is called -// to initialise and allocate any required memory or devices prior to servicing requests from the -// user space application. -static int z80drv_open(struct inode *inodep, struct file *filep) -{ - // Locals. - int ret = 0; - - if(!mutex_trylock(&Z80DRV_MUTEX)) - { - pr_alert("z80drv: device busy!\n"); - ret = -EBUSY; - goto out; - } - - //pr_info("z80drv: Device opened\n"); - -out: - return(ret); -} - -// Map shared memory. -// The z80drv allocates on the stack a chunk of memory and control variables which is used to control the Z80 Emulation state -// and provide it with internal 'virtual memory'. This virtual memory is either used as the core Z80 memory or as banked extensions -// to the host DRAM. -// The user space application is able to bind with the shared memory to perform tasks such as load/save of applications. -static int z80drv_mmap(struct file *filp, struct vm_area_struct *vma) -{ - // Locals. - int ret = 0; - struct page *page = NULL; - unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); - - // Make sure requested size is within range of the Z80 control structure. The kernel may page align it so the size may be bigger than - // the structure. - if(size < sizeof(t_Z80Ctrl) || size > sizeof(t_Z80Ctrl)*2) - { - ret = -EINVAL; - goto out; - } - - // Map the memory and exit. - page = virt_to_page((unsigned long)Z80Ctrl + (vma->vm_pgoff << PAGE_SHIFT)); - ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, vma->vm_page_prot); - if (ret != 0) - { - goto out; - } - -out: - return ret; -} - -// Device read. -// This method allows an application which opens the z80drv driver to read data in a stream. It is here for -// possible future use. -static ssize_t z80drv_read(struct file *filep, char *buffer, size_t len, loff_t *offset) -{ - // Locals. - int ret; - - if (len > Z80_VIRTUAL_RAM_SIZE) - { - pr_info("read overflow!\n"); - ret = -EFAULT; - goto out; - } - - if (copy_to_user(buffer, Z80Ctrl, len) == 0) - { - pr_info("z80drv: copy %u char to the user\n", len); - ret = len; - } else - { - ret = -EFAULT; - } - -out: - return ret; -} - -// Device write. -// This method allows an application which opens the z80drv driver to write stream data. It is here for -// possible future use. -static ssize_t z80drv_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) -{ - // Locals. - int ret; - - if (copy_from_user(Z80Ctrl, buffer, len)) - { - pr_err("z80drv: write fault!\n"); - ret = -EFAULT; - goto out; - } - pr_info("z80drv: copy %d char from the user\n", len); - ret = len; - -out: - return ret; -} - -// Function to dump out a given section of the physical host memory. -// -int memoryDump(uint32_t memaddr, uint32_t memsize, uint32_t dispaddr, uint8_t dispwidth) -{ - uint8_t displayWidth = dispwidth; - uint32_t pnt = memaddr; - uint32_t endAddr = memaddr + memsize; - uint32_t addr = dispaddr; - uint8_t data; - uint32_t i = 0; - int result = -1; - char c = 0; - - // If not set, calculate output line width according to connected display width. - // - if(displayWidth == 0) - { - switch(MAX_SCREEN_WIDTH) - { - case 40: - displayWidth = 8; - break; - case 80: - displayWidth = 16; - break; - default: - displayWidth = 32; - break; - } - } - - while (1) - { - printk(KERN_INFO "%08X", addr); // print address - printk(KERN_CONT ": "); - - // print hexadecimal data - for (i=0; i < displayWidth; ) - { - if(pnt+i < endAddr) - { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = pnt+i; - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - printk(KERN_CONT "%02X", data); - } - else - printk(KERN_CONT " "); - i++; - - printk(KERN_CONT " "); - } - - // print ascii data - printk(KERN_CONT " |"); - - // print single ascii char - for (i=0; i < displayWidth; i++) - { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = pnt+i; - while(CPLD_READY() == 0); - c = (char)READ_CPLD_DATA_IN(); - if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~')) - printk(KERN_CONT "%c", (char)c); - else - printk(KERN_CONT " "); - } - - printk(KERN_CONT "|\n"); - - // Move on one row. - pnt += displayWidth; - addr += displayWidth; - - // End of buffer, exit the loop. - if(pnt >= (memaddr + memsize)) - { - break; - } - } - - return(result); -} - -// Method to setup a default memory/IO profile. This profile will be changed by the host processing and also can be tweaked -// by the z80ctrl application. -// -void setupMemory(enum Z80_MEMORY_PROFILE mode) -{ - // Locals. - uint32_t idx; - - if(mode == USE_PHYSICAL_RAM) - { - // Initialise the page pointers and memory to use physical RAM. - for(idx=0x0000; idx < 0x10000; 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 < 0x10000; 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) - { -#if(TARGET_HOST_MZ2000 == 1) - // 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 < 0x10000; 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, (Z80Ctrl->lowMemorySwap ? idx - 0x8000 : idx)); - } - } - for(idx=0x0000; idx < 0x10000; idx++) - { - Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW; - } - // Enable refresh as using virtual RAM stops refresh of host DRAM. - Z80Ctrl->refreshDRAM = 1; - pr_info("Im here\n"); - } -#endif - - // Enable autorefresh if refreshDRAM is set. - SPI_SEND8(Z80Ctrl->refreshDRAM == 1 ? CPLD_CMD_SET_AUTO_REFRESH : CPLD_CMD_CLEAR_AUTO_REFRESH); - - // Inhibit mode disabled. - Z80Ctrl->inhibitMode = 0; - return; -} - -// IOCTL Method -// This method allows User Space application to control the Z80 CPU and internal functionality of the -// device driver. This is the preferred control method along with the shared memory segment for the driver. -static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) -{ - // Locals. - struct ioctlCmd ioctlCmd; - uint16_t idx; - uint32_t tmp[2]; - enum Z80_RUN_STATES currentRunMode; - enum Z80_RUN_STATES nextRunMode; - - // Get current running mode so any operations on the Z80 return it to original mode unless action overrides it. - mutex_lock(&Z80RunModeMutex); currentRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - - switch(cmd) - { - // Basic commands. - case IOCTL_CMD_SEND: - if(copy_from_user(&ioctlCmd, (int32_t *)arg, sizeof(ioctlCmd))) - printk("IOCTL - Couldnt retrieve command!\n"); - else - { - //printk("IOCTL - Command (%08x)\n", ioctlCmd.cmd); - switch(ioctlCmd.cmd) - { - // Command to stop the Z80 CPU and power off. - case IOCTL_CMD_Z80_STOP: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - z80_power(&Z80CPU, FALSE); - Z80_PC(Z80CPU) = 0; - printk("Z80 stopped.\n"); - break; - - // Command to power on and start the Z80 CPU. - case IOCTL_CMD_Z80_START: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_RUNNING; mutex_unlock(&Z80RunModeMutex); - - z80_power(&Z80CPU, TRUE); - printk("Z80 started.\n"); - break; - - // Command to pause the Z80. - case IOCTL_CMD_Z80_PAUSE: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_PAUSE; mutex_unlock(&Z80RunModeMutex); - printk("Z80 paused.\n"); - break; - - // Command to release a paused Z80. - case IOCTL_CMD_Z80_CONTINUE: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_CONTINUE; mutex_unlock(&Z80RunModeMutex); - printk("Z80 running.\n"); - break; - - // Command to perform a CPU reset. - case IOCTL_CMD_Z80_RESET: - // Stop the CPU prior to reset. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Reset.\n"); - break; - - // Command to setup the page table to use host memory and physical hardware. - case IOCTL_CMD_USE_HOST_RAM: - // Stop the CPU prior to memory reconfiguration. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80Ctrl->defaultPageMode = USE_PHYSICAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); - - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Set to use Host Memory.\n"); - break; - - // Command to setup the page table to use virtual memory, only physical hardware is accessed on the host. - case IOCTL_CMD_USE_VIRTUAL_RAM: - // Stop the CPU prior to memory reconfiguration. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Set to use Virtual Memory.\n"); - break; - - // Command to synchronise virtual memory to host DRAM. - case IOCTL_CMD_SYNC_TO_HOST_RAM: - // Stop the CPU prior to memory sync. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Copy virtual memory to host DRAM. - for(idx=0x1000; idx < 0xD000; idx++) - { - SPI_SEND32((uint32_t)idx << 16 | Z80Ctrl->memory[idx] << 8 | CPLD_CMD_WRITE_ADDR); - } - - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Host DRAM syncd with Virtual Memory.\n"); - break; - - // Command to dump out host memory. - case IOCTL_CMD_DUMP_MEMORY: - // Need to suspend the Z80 otherwise we will get memory clashes. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_PAUSE; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_PAUSE); - - // Dump out the physical memory address. - memoryDump(ioctlCmd.addr.start, ioctlCmd.addr.end - ioctlCmd.addr.start, ioctlCmd.addr.start, 0); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to set the governor delay to approximate real Z80 cpu frequencies when running in virtual memory. - case IOCTL_CMD_Z80_CPU_FREQ: - switch(ioctlCmd.speed.speedMultiplier) - { - case 2: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_7MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_7MHZ; - break; - - case 4: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_14MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_14MHZ; - break; - - case 8: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_28MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_28MHZ; - break; - - case 16: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_56MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_56MHZ; - break; - - case 32: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_112MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_112MHZ; - break; - - case 64: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_224MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_224MHZ; - break; - - case 128: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_448MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_448MHZ; - break; - - case 1: - default: - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_3_54MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_3_54MHZ; - break; - } - break; - - // Command to set the Z80 CPU Program Counter value. - case IOCTL_CMD_SETPC: - // Stop the CPU prior to PC change. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80_PC(Z80CPU) = ioctlCmd.z80.pc; - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Set PC to %04x\n", ioctlCmd.z80.pc); - break; - - // Method to send adhoc commands to the CPLD, ie for switching active display etc. - case IOCTL_CMD_CPLD_CMD: - // Stop the CPU prior to sending a direct command to the CPLD. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Send the command, small delay then send NOP to retrieve the response. - z80io_SPI_Send32(ioctlCmd.cpld.cmd, &tmp[0]); - udelay(50); - z80io_SPI_Send32(0x00000000, &tmp[0]); - z80io_SPI_Send32(0x00000000, &tmp[1]); - pr_info("CPLD TX:%08x, RX:%08x,%08x\n", ioctlCmd.cpld.cmd, tmp[0], tmp[1]); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of SOM to CPLD SPI tests. - case IOCTL_CMD_SPI_TEST: - // Stop the CPU prior to SPI testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform SPI Tests. - z80io_SPI_Test(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of SOM to CPLD Parallel Bus tests. - case IOCTL_CMD_PRL_TEST: - // Stop the CPU prior to SPI testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform Parallel Bus tests. - z80io_PRL_Test(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of Z80 host memory tests to assess the performance of the SOM->CPLD interface. - case IOCTL_CMD_Z80_MEMTEST: - // Stop the CPU prior to Host memory testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform host memory tests. - z80io_Z80_TestMemory(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - default: - break; - } - } - break; - - default: - printk("IOCTL - Unhandled Command (%08x)\n", ioctlCmd.cmd); - break; - - } - return 0; -} - -// Structure to declare public API methods. -// Standard Linux device driver structure to declare accessible methods within the driver. -static const struct file_operations z80drv_fops = { - .open = z80drv_open, - .read = z80drv_read, - .write = z80drv_write, - .release = z80drv_release, - .mmap = z80drv_mmap, - .unlocked_ioctl = z80drv_ioctl, - .owner = THIS_MODULE, -}; - -// Initialisation. -// This is the entry point into the device driver when loaded into the kernel. -// The method intialises any required hardware (ie. GPIO's, SPI etc), memory and the Z80 -// Emulation. It also allocates the Major and Minor device numbers and sets up the -// device in /dev. -static int __init ModuleInit(void) -{ - // Locals. - int idx; - int ret = 0; - - // Setup the Z80 handlers. - Z80CPU.context = z80_context; - Z80CPU.fetch = z80_fetch; - Z80CPU.fetch_opcode = z80_fetch_opcode; - Z80CPU.read = z80_read; - Z80CPU.write = z80_write; - Z80CPU.nop = z80_nop; - Z80CPU.in = z80_in; - Z80CPU.out = z80_out; - Z80CPU.halt = z80_halt; - Z80CPU.nmia = z80_nmia; - Z80CPU.inta = z80_inta; - Z80CPU.int_fetch = z80_intFetch; - Z80CPU.ld_i_a = z80_ldia; - Z80CPU.ld_r_a = z80_ldra; - Z80CPU.reti = z80_reti; - Z80CPU.retn = z80_retn; - Z80CPU.illegal = z80_illegal; - - mutex_init(&Z80DRV_MUTEX); - - // Get device Major number. - major = register_chrdev(0, DEVICE_NAME, &z80drv_fops); - if (major < 0) { - pr_info("z80drv: fail to register major number!"); - ret = major; - goto initExit; - } - - class = class_create(THIS_MODULE, CLASS_NAME); - if (IS_ERR(class)){ - unregister_chrdev(major, DEVICE_NAME); - pr_info("z80drv: failed to register device class"); - ret = PTR_ERR(class); - goto initExit; - } - - device = device_create(class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); - if (IS_ERR(device)) { - class_destroy(class); - unregister_chrdev(major, DEVICE_NAME); - ret = PTR_ERR(device); - goto initExit; - } - - // Allocate the Z80 memory to be shared between this kernel space driver and the controlling user space application. - Z80Ctrl = (t_Z80Ctrl *)kmalloc(sizeof(t_Z80Ctrl), GFP_KERNEL); - if (Z80Ctrl == NULL) - { - pr_info("z80drv: failed to allocate memory!"); - ret = -ENOMEM; - goto initExit; - } - - // Initialise the hardware to host interface. - z80io_init(); - - // 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. - for(idx=0; idx < Z80_VIRTUAL_RAM_SIZE; idx++) - { -#if(TARGET_HOST_MZ700 == 1) - if(idx >= 0x1000 && idx < 0xD000) - { - SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR); - while(CPLD_READY() == 0); - Z80Ctrl->memory[idx] = z80io_PRL_Read8(1); - } else - { - Z80Ctrl->memory[idx] = 0x00; - } -#endif -#if(TARGET_HOST_MZ2000 == 1) - if(idx >= 0x8000 && idx < 0x10000) - { - SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR); - while(CPLD_READY() == 0); - Z80Ctrl->memory[idx-0x8000] = z80io_PRL_Read8(1); - } else - { - if(idx >= 0x0000 && idx < 0x8000) - Z80Ctrl->memory[idx+0x8000] = 0x00; - else - Z80Ctrl->memory[idx] = 0x00; - } -#endif - } - -#if(TARGET_HOST_MZ700 == 1) - Z80Ctrl->memory[0x1200] = 0x01; - Z80Ctrl->memory[0x1201] = 0x86; - Z80Ctrl->memory[0x1202] = 0xf2; - Z80Ctrl->memory[0x1203] = 0x3e; - Z80Ctrl->memory[0x1204] = 0x15; - Z80Ctrl->memory[0x1205] = 0x3d; - Z80Ctrl->memory[0x1206] = 0x20; - Z80Ctrl->memory[0x1207] = 0xfd; - Z80Ctrl->memory[0x1208] = 0x0b; - Z80Ctrl->memory[0x1209] = 0x78; - Z80Ctrl->memory[0x120a] = 0xb1; - Z80Ctrl->memory[0x120b] = 0x20; - Z80Ctrl->memory[0x120c] = 0xf6; - Z80Ctrl->memory[0x120d] = 0xc3; - Z80Ctrl->memory[0x120e] = 0x00; - Z80Ctrl->memory[0x120f] = 0x00; -#endif - - // Copy the host BIOS into the Virtual ROM. - for(idx=0; idx < Z80_VIRTUAL_ROM_SIZE; idx++) - { - SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR); - while(CPLD_READY() == 0); - Z80Ctrl->memory[Z80_VIRTUAL_RAM_SIZE+idx] = z80io_PRL_Read8(1); - } - -#if(TARGET_HOST_MZ2000 == 1) - Z80Ctrl->lowMemorySwap = 1; -#endif - - // Setup Refresh so that an automatic refresh mode is performed by the CPLD whilst running in Virtual memory. This is necessary as opcode fetches from - // host memory, by the CPLD, normally performs the refresh and when running in virtual memory, these refresh cycles arent performed. - Z80Ctrl->refreshDRAM = 0; - - // Setup the governor delay, it is the delay per opcode fetch to restrict the Z80 CPU to a given speed. - Z80Ctrl->cpuGovernorDelayROM = INSTRUCTION_DELAY_ROM_3_54MHZ; - Z80Ctrl->cpuGovernorDelayRAM = INSTRUCTION_DELAY_RAM_3_54MHZ; - - // Setup the default Page Mode. This is needed if an event such as a reset occurs which needs to return the page and iotable back to default. - Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; - - // Setup memory profile to use internal virtual RAM (SOM kernel RAM rather than HOST DRAM). - setupMemory(Z80Ctrl->defaultPageMode); - - // Initialse run control. - mutex_init(&Z80RunModeMutex); - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - - // Setup the address and port history for communicating addresses to the CPLD. Used to shorten the instruction length to increase latency. - Z80Ctrl->z80PrevAddr = 0xFFFF; - Z80Ctrl->z80PrevPort = 0xFFFF; - - // Initialse hotkey detection variables. - Z80Ctrl->keyportStrobe = 0x00; - Z80Ctrl->keyportShiftCtrl = 0x00; - Z80Ctrl->keyportHotKey = 0x00; - - // PC to start and power on the CPU - Z80_PC(Z80CPU) = 0; - z80_power(&Z80CPU, TRUE); - - // Create thread to run the Z80 cpu. - kthread_z80 = kthread_create(thread_z80, &threadId_z80, "kthread_z80"); - if(kthread_z80 != NULL) - { - printk("kthread - Thread Z80 was created, waking...!\n"); - kthread_bind(kthread_z80, 1); - wake_up_process(kthread_z80); - } - else { - printk("kthread - Thread Z80 could not be created!\n"); - ret = -1; - goto initExit; - } - -initExit: - return ret; -} - -// Exit -// This method is called when the device driver is removed from the kernel with the rmmod command. -// It is responsible for closing and freeing all allocated memory, terminating all threads and removing -// the device from the /dev directory. -static void __exit ModuleExit(void) -{ - // Stop the internal threads. - //printk("kthread - Stop Z80 thread\n"); - kthread_stop(kthread_z80); - - // Return the memory used for the Z80 'virtual memory' and control variables. - kfree(Z80Ctrl); - - // Nothing to be done for the hardware. - - // Cleanup and remove the device. - mutex_destroy(&Z80DRV_MUTEX); - device_destroy(class, MKDEV(major, 0)); - class_unregister(class); - class_destroy(class); - unregister_chrdev(major, DEVICE_NAME); - - //pr_info("z80drv: unregistered!\n"); -} - -module_init(ModuleInit); -module_exit(ModuleExit); diff --git a/software/FusionX/src/z80drv/MZ2000/z80driver.h b/software/FusionX/src/z80drv/MZ2000/z80driver.h deleted file mode 100644 index bf622a6da..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80driver.h +++ /dev/null @@ -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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#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 = | | | | | | | | | | | | -// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | | -// IN 0xE1 = | |DRAM | |DRAM | | | | |DRAM | | | -// -// = 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 - physical host RAM - // 0x40 - physical host ROM - // 0x20 - physical host VRAM - // 0x10 - physical host hardware - // 0x08 - virtual host RAM - // 0x04 - virtual host ROM - // 0x02 - virtual host hardware - // 16bit Input Address -> map -> Pointer to 24bit memory address + type flag. - // -> Pointer+ 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 - physical host hardware - // <0x40>FF - 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 diff --git a/software/FusionX/src/z80drv/MZ2000/z80io.c b/software/FusionX/src/z80drv/MZ2000/z80io.c deleted file mode 100644 index 08286199d..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80io.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -//#include -//#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" - -#include -#include -#include -#include - -//------------------------------------------------------------------------------------------------------------------------------- -// -// 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 diff --git a/software/FusionX/src/z80drv/MZ2000/z80io.h b/software/FusionX/src/z80drv/MZ2000/z80io.h deleted file mode 100755 index 9e5c1b0f2..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80io.h +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#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 diff --git a/software/FusionX/src/z80drv/MZ2000/z80io_test.c b/software/FusionX/src/z80drv/MZ2000/z80io_test.c deleted file mode 100644 index ac7346b57..000000000 --- a/software/FusionX/src/z80drv/MZ2000/z80io_test.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include - -//-------------------------------------------------------- -// 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); -} diff --git a/software/FusionX/src/z80drv/MZ700/optparse.h b/software/FusionX/src/z80drv/MZ700/optparse.h deleted file mode 100644 index f96184add..000000000 --- a/software/FusionX/src/z80drv/MZ700/optparse.h +++ /dev/null @@ -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 */ diff --git a/software/FusionX/src/z80drv/MZ700/z80ctrl.c b/software/FusionX/src/z80drv/MZ700/z80ctrl.c deleted file mode 100644 index 70c69f2b5..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80ctrl.c +++ /dev/null @@ -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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 = 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 # 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); -} diff --git a/software/FusionX/src/z80drv/MZ700/z80driver.c b/software/FusionX/src/z80drv/MZ700/z80driver.c deleted file mode 100644 index f1159cf18..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80driver.c +++ /dev/null @@ -1,1447 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Name: z80driver.c -// Created: Oct 2022 -// Author(s): Philip Smart -// Description: Z80 Driver -// This file contains the methods used to create a linux device driver which provides -// the services of a Z80 CPU emulation and the control of an underlying Z80'less host -// system. In essence this driver is the host Z80 CPU. -// 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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" -#include "z80menu.h" -#include "z80driver.h" - -#include -#include -#include - -/* Meta Information */ -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Philip D Smart"); -MODULE_DESCRIPTION("Z80 CPU Emulator and Hardware Interface Driver"); - -/* Global variables for the threads */ -static struct task_struct *kthread_z80; -static int threadId_z80 = 1; - -// Device class and major numbers. -static struct class *class; -static struct device *device; -static int major; - -// CPU Instance. -static Z80 Z80CPU; - -// Z80 Control data. -static t_Z80Ctrl *Z80Ctrl = NULL; - -// Runtime control of the CPU. As the CPU runs in a detached thread on core 1, the cpu needs to be suspended before any external -// operations can take place. This is achieved with the runtime mutex. -enum Z80_RUN_STATES Z80RunMode; -static struct mutex Z80RunModeMutex; -static DEFINE_MUTEX(Z80DRV_MUTEX); - - -//------------------------------------------------------------------------------------------------------------------------------- -// -// Z80 CPU Kernel Logic. -// -// THe Z80 CPU is initialised and set running, processing instructions either from the underlying host hardware or internal -// memory. The configuration and flow is controlled via the Z80Ctrl structure which is User Space accessible. -// -//------------------------------------------------------------------------------------------------------------------------------- - -// Method to read a byte from physical hardware or internal virtual memory/devices. -// The page table indicates the source and the read is processed accordingly. -static zuint8 z80_read(void *context, zuint16 address) -{ - // Locals. - // - zuint8 data; - uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // Only read if the address is in physical RAM. - if(isPhysical(address)) - { - // Commence cycle to retrieve the data from Real RAM. - // Optimise SPI according to last address sent to CPLD. - if(addrDiff >=0 && addrDiff < 8) - { - SPI_SEND8((CPLD_CMD_READ_ADDR + addrDiff)); - Z80Ctrl->z80PrevAddr += addrDiff; - } else - { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = address; - } - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. - while(CPLD_LAST_TSTATE() == 0); - } - else if(isVirtualHW(address)) - { - // Virtual Hardware - call the handler. - switch(realAddress(address)) - { - default: - break; - } - } - else if(isVirtualMemory(address)) - { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - } - - // Keyport data? Store. - if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 8 && (data & 0x41) == 0) - { - Z80Ctrl->keyportShiftCtrl = 0x01; - } else - if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 0 && (data & 0x80) == 0) - { - Z80Ctrl->keyportHotKey = 0x01; - } - return(data); -} - -// Method to write a byte to physical hardware or internal virtual memory or devices. -// The page table indicates the target and the write is processed accordingly. -static void z80_write(void *context, zuint16 address, zuint8 data) -{ - // Locals. - uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // To detect Hotkey presses, we need to store the keboard strobe data and on keydata read. - if(isHW(address) && address == 0xE000) - { - Z80Ctrl->keyportStrobe = data; - } - - // Write to physical host? - if(isPhysical(address)) - { - // Commence cycle to write the data to real RAM. - // Optimise SPI according to last address sent to CPLD. - if(addrDiff >=0 && addrDiff < 8) - { - SPI_SEND16((data << 8) | (CPLD_CMD_WRITE_ADDR + addrDiff)); - Z80Ctrl->z80PrevAddr += addrDiff; - } else - { - SPI_SEND32((uint32_t)address << 16 | data << 8 | CPLD_CMD_WRITE_ADDR); - Z80Ctrl->z80PrevAddr = address; - } - - // Write-thru to virtual memory if we update real memory. - // if(isPhysicalRAM(address)) - // writeVirtualRAM(address, data); - - // Pause until the Last T-State is detected. - while(CPLD_LAST_TSTATE() == 0); - } - else if(isVirtualHW(address)) - { - // Virtual Hardware - call the handler. - switch(realAddress(address)) - { - default: - break; - } - } - else if(isVirtualRAM(address)) - { - // Update virtual memory. - writeVirtualRAM(address, data); - } - // Cannot write to virtual ROM so no logic. -} - -// Primary Opcode fetch method. This method is called each time a single or multi-byte opcode is -// encountered. Opcode data is retrieved via the z80_fetch method. -// -// Depending on the address and the configured page map, the opcode is fetched from hardware -// or internal virtual memory. As this method is the primary timing method for Z80 instructions -// (read/write methods dont affect the timing so much as long as they operate in less than the read/write -// cycle of an original Z80). -// Initially the timing on the virtual memory is set by a governor delay but this will be updated to a more -// precise M/T-State cycle per instruction type delay. -static zuint8 z80_fetch_opcode(void *context, zuint16 address) -{ - // Locals. - zuint8 opcode = 0x00; - uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - volatile uint32_t idx; // Leave as volatile otherwise optimiser will optimise out the delay code. - Z_UNUSED(context) - - // Normally only opcode fetches occur in RAM but allow any physical address as it could be a Z80 programming trick. - if(isPhysical(address)) - { - // Commence cycle to fetch the opcode from potentially Real RAM albeit it could be any physical hardware. - // Optimise SPI according to last address sent to CPLD. - if(addrDiff >=0 && addrDiff < 8) - { - SPI_SEND8((CPLD_CMD_FETCH_ADDR + addrDiff)); - Z80Ctrl->z80PrevAddr += addrDiff; - } else - { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_FETCH_ADDR); - Z80Ctrl->z80PrevAddr = address; - } - while(CPLD_READY() == 0); - opcode = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. - while(CPLD_LAST_TSTATE() == 0); - } else - // Virtual fetches only occur in memory as we are not emulating original hardware. - if(isVirtualMemory(address)) - { - for(idx=0; idx < Z80Ctrl->cpuGovernorDelay; idx++); - - // Retrieve data from virtual memory. - opcode = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - } - return(opcode); -} - -// Method similar to z80_read, kept seperate to avoid additional what-if logic and doesnt require virtual hardware logic. -// -static zuint8 z80_fetch(void *context, zuint16 address) -{ - // Locals. - // - zuint8 data = 0x00; - uint16_t addrDiff = (uint16_t)address - Z80Ctrl->z80PrevAddr; - Z_UNUSED(context) - - // Normally only opcode fetches occur in RAM but allow any physical address as it could be a Z80 programming trick. - if(isPhysical(address)) - { - // Commence cycle to retrieve the data from Real RAM. - // Optimise SPI according to last address sent to CPLD. - if(addrDiff >=0 && addrDiff < 8) - { - SPI_SEND8((CPLD_CMD_READ_ADDR + addrDiff)); - Z80Ctrl->z80PrevAddr += addrDiff; - } else - { - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = address; - } - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. - while(CPLD_LAST_TSTATE() == 0); - } else - if(isVirtualMemory(address)) - { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - } - -// // Keyport data? Store. -// if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 8 && (data & 0x41) == 0) -// { -// Z80Ctrl->keyportShiftCtrl = 0x01; -// } else -// if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 0 && (data & 0x80) == 0) -// { -// Z80Ctrl->keyportHotKey = 0x01; -// } - return(data); -} - -// Method to perform a Z80 input operation. This normally goes to hardware and the CPLD executes the required cycle. -// Some ports are dedicated virtual ports providing virtual services to the host computer/application. These are intercepted -// and processed in this driver. -static zuint8 z80_in(void *context, zuint16 port) -{ - // Locals. - zuint8 value; - uint16_t portDiff = (uint16_t)port - Z80Ctrl->z80PrevPort; - Z_UNUSED(context) - - printk("z80_in\n"); - // Physical port go direct to hardware to retrieve value. - if(isPhysicalIO(port)) - { - // Commence cycle to retrieve the value from the I/O port. Port contains the 16bit BC value. - // Optimise SPI according to last port sent to CPLD. - if(portDiff >=0 && portDiff < 8) - { - SPI_SEND8((CPLD_CMD_READIO_ADDR + portDiff)); - Z80Ctrl->z80PrevPort += portDiff; - } else - { - SPI_SEND32((uint32_t)port << 16 | CPLD_CMD_READIO_ADDR); - Z80Ctrl->z80PrevPort = port; - } - - // Whilst waiting for the CPLD, we now determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { - // Port is not a memory management port. - default: - break; - } - - // Finally ensure the data from the port is ready and retrieve it. - while(CPLD_READY() == 0); - value = READ_CPLD_DATA_IN(); - } else - // Virtual I/O Port. - { - // Virtual I/O - call the handler. - switch(realPort(port)) - { - default: - value = 0x00; - break; - } - } - return(value); -} - -// Method to perform a Z80 output operation. This normally goes to hardware and the CPLD executes the required cycle. -// Some ports are dedicated virtual ports providing virtual services to the host computer/application. These are intercepted -// and processed in this driver. -// There are also ports which are both hardware and need mirroring in software. These ports, typically memory mapping ports. -// when activated in the hardware need to be mirrored in the page table so correct virtual memory is used when addressed. -static void z80_out(void *context, zuint16 port, zuint8 value) -{ - // Locals. - uint32_t idx; - uint16_t portDiff = (uint16_t)port - Z80Ctrl->z80PrevPort; - Z_UNUSED(context) - - // Physical port go direct to hardware to retrieve value. - if(isPhysicalIO(port)) - { - // Commence cycle to write the value to the I/O port. Port contains the 16bit BC value. - // Optimise SPI according to last port sent to CPLD. - if(portDiff >=0 && portDiff < 8) - { - SPI_SEND16((value << 8) | (CPLD_CMD_WRITEIO_ADDR + portDiff)); - Z80Ctrl->z80PrevPort += portDiff; -//pr_info("OUT:0x%x(%x)=0x%x\n", port, portDiff, value); - } else - { - SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); - Z80Ctrl->z80PrevPort = port; -//pr_info("OUT:0x%x=0x%x\n", port, value); - } - - // Determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { - // 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_PHYSICAL_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_PHYSICAL_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_PHYSICAL_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_PHYSICAL_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; - } - } else - if(isVirtualIO(port)) - { - // 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 to the state prior to the complimentary command being invoked. - - // Determine if this is a memory management port and update the memory page if required. - switch(port & 0x00FF) - { - // 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; - } - } else - { - // Virtual I/O - call the handler. - switch(realPort(port)) - { - default: - break; - } - } -} - -// NOP - No Operation method. This instruction is used for timing, padding out an application or during -// HALT cycles to ensure Refresh occurs. -// If the address is configured as hardware (via the page table) then a refresh cycle is requested otherwise -// nothing to be done. -static zuint8 z80_nop(void *context, zuint16 address) -{ - // Locals. - Z_UNUSED(context) - - if(isPhysical(address)) - { - // If autorefresh is not enabled, send a single refresh request. - if(Z80Ctrl->refreshDRAM == 0) - SPI_SEND8(CPLD_CMD_REFRESH); - } - return 0x00; -} - -// HALT - CPU executes a HALT instruction which results in the HALT line going active low and then it enters -// a state executing NOP instructions to ensure DRAM refresh until a reset or INT event. -static void z80_halt(void *context, zboolean state) -{ - // Locals. - Z_UNUSED(context) Z_UNUSED(state) - - // Inform CPLD of halt state. - printk("z80_halt\n"); - SPI_SEND8(CPLD_CMD_HALT); - Z80CPU.cycles = Z80_MAXIMUM_CYCLES; -} - -// Methods below are not yet implemented, Work In Progress! -static zuint8 z80_nmia(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_nmia\n"); - return 0x00; -} -static zuint8 z80_inta(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_inta\n"); - return 0x00; -} -static zuint8 z80_intFetch(void *context, zuint16 address) -{ - Z_UNUSED(context) - printk("z80_int_fetch\n"); - return 0x00; -} - -// Z80 CPU Emulation Thread -// ------------------------ -// This is a kernel thread, bound to CPU 1 with IRQ's disabled. -// The Z80 is controlled by a mutex protected variable to define run, stop, pause and terminate modes. -int thread_z80(void * thread_nr) -{ - // Locals. - uint8_t canRun = 0; - int t_nr = *(int *) thread_nr; - //struct sched_param param = {.sched_priority = 99}; - spinlock_t spinLock; - unsigned long flags; - - // Initialise spinlock and disable IRQ's. We should be the only process running on core 1. - spin_lock_init(&spinLock); - spin_lock_irqsave(&spinLock, flags); - - // Assign this emulation to high priority realtime scheduling. Also the task will be assigned to an isolated CPU. - //sched_setscheduler(current, SCHED_RR, ¶m); - - // Run the CPU forever or until a stop occurs. - while(!kthread_should_stop()) - { - // Run the Z80 emulation if enabled. - if(canRun) z80_run(&Z80CPU, 100); - - // Reset pressed? - if(CPLD_RESET()) - { - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); - - // Wait for release before restarting CPU. - while(CPLD_RESET()); - } else - { - // Update state to indicate request has been actioned. - mutex_lock(&Z80RunModeMutex); - if(Z80RunMode == Z80_STOP) Z80RunMode = Z80_STOPPED; - if(Z80RunMode == Z80_PAUSE) Z80RunMode = Z80_PAUSED; - if(Z80RunMode == Z80_CONTINUE) Z80RunMode = Z80_RUNNING; - if(Z80RunMode == Z80_RUNNING) canRun=1; else canRun=0; - mutex_unlock(&Z80RunModeMutex); - - // Hotkey pressed? Bring up user menu. - if(Z80Ctrl->keyportShiftCtrl && Z80Ctrl->keyportHotKey) - { - z80menu(); - Z80Ctrl->keyportShiftCtrl = 0; - Z80Ctrl->keyportHotKey = 0; - } - } - } - - // Release spinlock as we are unloading driver. - spin_unlock_irqrestore(&spinLock, flags); - printk("kthread - Z80 Thread %d finished execution!\n", t_nr); - return 0; -} - - -//------------------------------------------------------------------------------------------------------------------------------- -// -// User space driver access. -// -//------------------------------------------------------------------------------------------------------------------------------- - - -// Device close. -// When a user space application terminates or closes the z80drv device driver, this function is called -// to close any open connections, memory and variables required to handle the user space application -// requests. -static int z80drv_release(struct inode *inodep, struct file *filep) -{ - // Locals. - - mutex_unlock(&Z80DRV_MUTEX); - //pr_info("z80drv: Device successfully closed\n"); - - return(0); -} - -// Device open. -// When a user space application open's the z80drv device driver, this function is called -// to initialise and allocate any required memory or devices prior to servicing requests from the -// user space application. -static int z80drv_open(struct inode *inodep, struct file *filep) -{ - // Locals. - int ret = 0; - - if(!mutex_trylock(&Z80DRV_MUTEX)) - { - pr_alert("z80drv: device busy!\n"); - ret = -EBUSY; - goto out; - } - - //pr_info("z80drv: Device opened\n"); - -out: - return(ret); -} - -// Map shared memory. -// The z80drv allocates on the stack a chunk of memory and control variables which is used to control the Z80 Emulation state -// and provide it with internal 'virtual memory'. This virtual memory is either used as the core Z80 memory or as banked extensions -// to the host DRAM. -// The user space application is able to bind with the shared memory to perform tasks such as load/save of applications. -static int z80drv_mmap(struct file *filp, struct vm_area_struct *vma) -{ - // Locals. - int ret = 0; - struct page *page = NULL; - unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); - - // Make sure requested size is within range of the Z80 control structure. The kernel may page align it so the size may be bigger than - // the structure. - if(size < sizeof(t_Z80Ctrl) || size > sizeof(t_Z80Ctrl)*2) - { - ret = -EINVAL; - goto out; - } - - // Map the memory and exit. - page = virt_to_page((unsigned long)Z80Ctrl + (vma->vm_pgoff << PAGE_SHIFT)); - ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, vma->vm_page_prot); - if (ret != 0) - { - goto out; - } - -out: - return ret; -} - -// Device read. -// This method allows an application which opens the z80drv driver to read data in a stream. It is here for -// possible future use. -static ssize_t z80drv_read(struct file *filep, char *buffer, size_t len, loff_t *offset) -{ - // Locals. - int ret; - - if (len > Z80_VIRTUAL_RAM_SIZE) - { - pr_info("read overflow!\n"); - ret = -EFAULT; - goto out; - } - - if (copy_to_user(buffer, Z80Ctrl, len) == 0) - { - pr_info("z80drv: copy %u char to the user\n", len); - ret = len; - } else - { - ret = -EFAULT; - } - -out: - return ret; -} - -// Device write. -// This method allows an application which opens the z80drv driver to write stream data. It is here for -// possible future use. -static ssize_t z80drv_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) -{ - // Locals. - int ret; - - if (copy_from_user(Z80Ctrl, buffer, len)) - { - pr_err("z80drv: write fault!\n"); - ret = -EFAULT; - goto out; - } - pr_info("z80drv: copy %d char from the user\n", len); - ret = len; - -out: - return ret; -} - -// Function to dump out a given section of the physical host memory. -// -int memoryDump(uint32_t memaddr, uint32_t memsize, uint32_t dispaddr, uint8_t dispwidth) -{ - uint8_t displayWidth = dispwidth; - uint32_t pnt = memaddr; - uint32_t endAddr = memaddr + memsize; - uint32_t addr = dispaddr; - uint8_t data; - uint32_t i = 0; - int result = -1; - char c = 0; - - // If not set, calculate output line width according to connected display width. - // - if(displayWidth == 0) - { - switch(MAX_SCREEN_WIDTH) - { - case 40: - displayWidth = 8; - break; - case 80: - displayWidth = 16; - break; - default: - displayWidth = 32; - break; - } - } - - while (1) - { - printk(KERN_INFO "%08X", addr); // print address - printk(KERN_CONT ": "); - - // print hexadecimal data - for (i=0; i < displayWidth; ) - { - if(pnt+i < endAddr) - { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = pnt+i; - while(CPLD_READY() == 0); - data = READ_CPLD_DATA_IN(); - printk(KERN_CONT "%02X", data); - } - else - printk(KERN_CONT " "); - i++; - - printk(KERN_CONT " "); - } - - // print ascii data - printk(KERN_CONT " |"); - - // print single ascii char - for (i=0; i < displayWidth; i++) - { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); - Z80Ctrl->z80PrevAddr = pnt+i; - while(CPLD_READY() == 0); - c = (char)READ_CPLD_DATA_IN(); - if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~')) - printk(KERN_CONT "%c", (char)c); - else - printk(KERN_CONT " "); - } - - printk(KERN_CONT "|\n"); - - // Move on one row. - pnt += displayWidth; - addr += displayWidth; - - // End of buffer, exit the loop. - if(pnt >= (memaddr + memsize)) - { - break; - } - } - - return(result); -} - -// Method to setup a default memory/IO profile. This profile will be changed by the host processing and also can be tweaked -// by the z80ctrl application. -// -void setupMemory(enum Z80_MEMORY_PROFILE mode) -{ - // Locals. - uint32_t idx; - - if(mode == USE_PHYSICAL_RAM) - { - // Initialise the page pointers and memory to use physical RAM. - for(idx=0x0000; idx < 0x10000; 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); - } - - // 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_HW, idx); - } - } - for(idx=0x0000; idx < 0x10000; 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 < 0x10000; 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); - } - - // 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_HW, idx); - } - } - for(idx=0x0000; idx < 0x10000; idx++) - { - if((idx & 0x00FF) >= IO_ADDR_E0 && (idx & 0x00FF) < IO_ADDR_E7) - Z80Ctrl->iopage[idx] = idx | IO_TYPE_VIRTUAL_HW; - else - Z80Ctrl->iopage[idx] = idx | IO_TYPE_PHYSICAL_HW; - } - // Enable refresh as using virtual RAM stops refresh of host DRAM. - Z80Ctrl->refreshDRAM = 1; - } - - // Enable autorefresh if refreshDRAM is set. - SPI_SEND8(Z80Ctrl->refreshDRAM == 1 ? CPLD_CMD_SET_AUTO_REFRESH : CPLD_CMD_CLEAR_AUTO_REFRESH); - - // Inhibit mode disabled. - Z80Ctrl->inhibitMode = 0; - return; -} - -// IOCTL Method -// This method allows User Space application to control the Z80 CPU and internal functionality of the -// device driver. This is the preferred control method along with the shared memory segment for the driver. -static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) -{ - // Locals. - struct ioctlCmd ioctlCmd; - uint16_t idx; - uint32_t tmp; - enum Z80_RUN_STATES currentRunMode; - enum Z80_RUN_STATES nextRunMode; - - // Get current running mode so any operations on the Z80 return it to original mode unless action overrides it. - mutex_lock(&Z80RunModeMutex); currentRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - - switch(cmd) - { - // Basic commands. - case IOCTL_CMD_SEND: - if(copy_from_user(&ioctlCmd, (int32_t *)arg, sizeof(ioctlCmd))) - printk("IOCTL - Couldnt retrieve command!\n"); - else - { - //printk("IOCTL - Command (%08x)\n", ioctlCmd.cmd); - switch(ioctlCmd.cmd) - { - // Command to stop the Z80 CPU and power off. - case IOCTL_CMD_Z80_STOP: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - z80_power(&Z80CPU, FALSE); - Z80_PC(Z80CPU) = 0; - printk("Z80 stopped.\n"); - break; - - // Command to power on and start the Z80 CPU. - case IOCTL_CMD_Z80_START: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_RUNNING; mutex_unlock(&Z80RunModeMutex); - - z80_power(&Z80CPU, TRUE); - printk("Z80 started.\n"); - break; - - // Command to pause the Z80. - case IOCTL_CMD_Z80_PAUSE: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_PAUSE; mutex_unlock(&Z80RunModeMutex); - printk("Z80 paused.\n"); - break; - - // Command to release a paused Z80. - case IOCTL_CMD_Z80_CONTINUE: - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_CONTINUE; mutex_unlock(&Z80RunModeMutex); - printk("Z80 running.\n"); - break; - - // Command to perform a CPU reset. - case IOCTL_CMD_Z80_RESET: - // Stop the CPU prior to reset. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - z80_instant_reset(&Z80CPU); - setupMemory(Z80Ctrl->defaultPageMode); - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Reset.\n"); - break; - - // Command to setup the page table to use host memory and physical hardware. - case IOCTL_CMD_USE_HOST_RAM: - // Stop the CPU prior to memory reconfiguration. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80Ctrl->defaultPageMode = USE_PHYSICAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); - - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Set to use Host Memory.\n"); - break; - - // Command to setup the page table to use virtual memory, only physical hardware is accessed on the host. - case IOCTL_CMD_USE_VIRTUAL_RAM: - // Stop the CPU prior to memory reconfiguration. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; - setupMemory(Z80Ctrl->defaultPageMode); - z80_instant_reset(&Z80CPU); - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Set to use Virtual Memory.\n"); - break; - - // Command to synchronise virtual memory to host DRAM. - case IOCTL_CMD_SYNC_TO_HOST_RAM: - // Stop the CPU prior to memory sync. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Copy virtual memory to host DRAM. - for(idx=0x1000; idx < 0xD000; idx++) - { - SPI_SEND32((uint32_t)idx << 16 | Z80Ctrl->memory[idx] << 8 | CPLD_CMD_WRITE_ADDR); - } - - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Z80 Host DRAM syncd with Virtual Memory.\n"); - break; - - // Command to dump out host memory. - case IOCTL_CMD_DUMP_MEMORY: - // Need to suspend the Z80 otherwise we will get memory clashes. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_PAUSE; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_PAUSE); - - // Dump out the physical memory address. - memoryDump(ioctlCmd.addr.start, ioctlCmd.addr.end - ioctlCmd.addr.start, ioctlCmd.addr.start, 0); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to set the governor delay to approximate real Z80 cpu frequencies when running in virtual memory. - case IOCTL_CMD_Z80_CPU_FREQ: - switch(ioctlCmd.speed.speedMultiplier) - { - case 2: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_7MHZ; - break; - - case 4: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_14MHZ; - break; - - case 8: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_28MHZ; - break; - - case 16: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_56MHZ; - break; - - case 32: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_112MHZ; - break; - - case 64: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_224MHZ; - break; - - case 128: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_448MHZ; - break; - - case 1: - default: - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_3_54MHZ; - break; - } - break; - - // Command to set the Z80 CPU Program Counter value. - case IOCTL_CMD_SETPC: - // Stop the CPU prior to PC change. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - Z80_PC(Z80CPU) = ioctlCmd.z80.pc; - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - printk("Set PC to %04x\n", ioctlCmd.z80.pc); - break; - - // Method to send adhoc commands to the CPLD, ie for switching active display etc. - case IOCTL_CMD_CPLD_CMD: - // Stop the CPU prior to sending a direct command to the CPLD. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Send the command, small delay then send NOP to retrieve the response. - SPI_SEND32(ioctlCmd.cpld.cmd); - udelay(10); - z80io_SPI_Send32(0x00000000, &tmp); - pr_info("CPLD RX:%08x\n", tmp); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of SOM to CPLD SPI tests. - case IOCTL_CMD_SPI_TEST: - // Stop the CPU prior to SPI testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform SPI Tests. - z80io_SPI_Test(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of SOM to CPLD Parallel Bus tests. - case IOCTL_CMD_PRL_TEST: - // Stop the CPU prior to SPI testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform Parallel Bus tests. - z80io_PRL_Test(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - // Command to run a series of Z80 host memory tests to assess the performance of the SOM->CPLD interface. - case IOCTL_CMD_Z80_MEMTEST: - // Stop the CPU prior to Host memory testing. - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - do { mutex_lock(&Z80RunModeMutex); nextRunMode = Z80RunMode ; mutex_unlock(&Z80RunModeMutex); - } while(nextRunMode == Z80_STOP); - - // Perform host memory tests. - z80io_Z80_TestMemory(); - - // Z80 can continue. - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - break; - - default: - break; - } - } - break; - - default: - printk("IOCTL - Unhandled Command (%08x)\n", ioctlCmd.cmd); - break; - - } - return 0; -} - -// Structure to declare public API methods. -// Standard Linux device driver structure to declare accessible methods within the driver. -static const struct file_operations z80drv_fops = { - .open = z80drv_open, - .read = z80drv_read, - .write = z80drv_write, - .release = z80drv_release, - .mmap = z80drv_mmap, - .unlocked_ioctl = z80drv_ioctl, - .owner = THIS_MODULE, -}; - -// Initialisation. -// This is the entry point into the device driver when loaded into the kernel. -// The method intialises any required hardware (ie. GPIO's, SPI etc), memory and the Z80 -// Emulation. It also allocates the Major and Minor device numbers and sets up the -// device in /dev. -static int __init ModuleInit(void) -{ - // Locals. - int idx; - int ret = 0; - - // Setup the Z80 handlers. - Z80CPU.context = NULL; - Z80CPU.fetch_opcode = z80_fetch_opcode; - Z80CPU.fetch = z80_fetch; - Z80CPU.read = z80_read; - Z80CPU.write = z80_write; - Z80CPU.nop = z80_nop; - Z80CPU.in = z80_in; - Z80CPU.out = z80_out; - Z80CPU.halt = z80_halt; - Z80CPU.nmia = z80_nmia; - Z80CPU.inta = z80_inta; - Z80CPU.int_fetch = z80_intFetch; - Z80CPU.ld_i_a = NULL; - Z80CPU.ld_r_a = NULL; - Z80CPU.reti = NULL; - Z80CPU.retn = NULL; - Z80CPU.hook = NULL; - Z80CPU.illegal = NULL; - Z80CPU.options = Z80_MODEL_ZILOG_NMOS; - - mutex_init(&Z80DRV_MUTEX); - - // Get device Major number. - major = register_chrdev(0, DEVICE_NAME, &z80drv_fops); - if (major < 0) { - pr_info("z80drv: fail to register major number!"); - ret = major; - goto initExit; - } - - class = class_create(THIS_MODULE, CLASS_NAME); - if (IS_ERR(class)){ - unregister_chrdev(major, DEVICE_NAME); - pr_info("z80drv: failed to register device class"); - ret = PTR_ERR(class); - goto initExit; - } - - device = device_create(class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); - if (IS_ERR(device)) { - class_destroy(class); - unregister_chrdev(major, DEVICE_NAME); - ret = PTR_ERR(device); - goto initExit; - } - - // Allocate the Z80 memory to be shared between this kernel space driver and the controlling user space application. - Z80Ctrl = (t_Z80Ctrl *)kmalloc(sizeof(t_Z80Ctrl), GFP_KERNEL); - if (Z80Ctrl == NULL) - { - pr_info("z80drv: failed to allocate memory!"); - ret = -ENOMEM; - goto initExit; - } - - // Initialise the hardware to host interface. - z80io_init(); - - // 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. - 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->memory[idx] = z80io_PRL_Read8(1); - } else - { - Z80Ctrl->memory[idx] = 0x00; - } - } - Z80Ctrl->memory[0x1200] = 0x01; - Z80Ctrl->memory[0x1201] = 0x86; - Z80Ctrl->memory[0x1202] = 0xf2; - Z80Ctrl->memory[0x1203] = 0x3e; - Z80Ctrl->memory[0x1204] = 0x15; - Z80Ctrl->memory[0x1205] = 0x3d; - Z80Ctrl->memory[0x1206] = 0x20; - Z80Ctrl->memory[0x1207] = 0xfd; - Z80Ctrl->memory[0x1208] = 0x0b; - Z80Ctrl->memory[0x1209] = 0x78; - Z80Ctrl->memory[0x120a] = 0xb1; - Z80Ctrl->memory[0x120b] = 0x20; - Z80Ctrl->memory[0x120c] = 0xf6; - Z80Ctrl->memory[0x120d] = 0xc3; - Z80Ctrl->memory[0x120e] = 0x00; - Z80Ctrl->memory[0x120f] = 0x00; - - // Copy the host BIOS into the Virtual RAM. - for(idx=0; idx < 0x1000; idx++) - { - SPI_SEND32((uint32_t)idx << 16 | CPLD_CMD_READ_ADDR); - while(CPLD_READY() == 0); - Z80Ctrl->memory[Z80_VIRTUAL_RAM_SIZE+idx] = z80io_PRL_Read8(1); - } - - // Setup Refresh so that an automatic refresh mode is performed by the CPLD whilst running in Virtual memory. This is necessary as opcode fetches from - // host memory, by the CPLD, normally performs the refresh and when running in virtual memory, these refresh cycles arent performed. - Z80Ctrl->refreshDRAM = 1; - - // Setup the governor delay, it is the delay per opcode fetch to restrict the Z80 CPU to a given speed. - Z80Ctrl->cpuGovernorDelay = MZ700_INSTRUCTION_DELAY_3_54MHZ; - - // Setup the default Page Mode. This is needed if an event such as a reset occurs which needs to return the page and iotable back to default. - Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; - - // Setup memory profile to use internal virtual RAM (SOM kernel RAM rather than HOST DRAM). - setupMemory(Z80Ctrl->defaultPageMode); - - // Initialse run control. - mutex_init(&Z80RunModeMutex); - mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_STOP; mutex_unlock(&Z80RunModeMutex); - - // Setup the address and port history for communicating addresses to the CPLD. Used to shorten the instruction length to increase latency. - Z80Ctrl->z80PrevAddr = 0xFFFF; - Z80Ctrl->z80PrevPort = 0xFFFF; - - // Initialse hotkey detection variables. - Z80Ctrl->keyportStrobe = 0x00; - Z80Ctrl->keyportShiftCtrl = 0x00; - Z80Ctrl->keyportHotKey = 0x00; - - // PC to start and power on the CPU - Z80_PC(Z80CPU) = 0; - z80_power(&Z80CPU, TRUE); - - // Create thread to run the Z80 cpu. - kthread_z80 = kthread_create(thread_z80, &threadId_z80, "kthread_z80"); - if(kthread_z80 != NULL) - { - printk("kthread - Thread Z80 was created, waking...!\n"); - kthread_bind(kthread_z80, 1); - wake_up_process(kthread_z80); - } - else { - printk("kthread - Thread Z80 could not be created!\n"); - ret = -1; - goto initExit; - } - -initExit: - return ret; -} - -// Exit -// This method is called when the device driver is removed from the kernel with the rmmod command. -// It is responsible for closing and freeing all allocated memory, terminating all threads and removing -// the device from the /dev directory. -static void __exit ModuleExit(void) -{ - // Stop the internal threads. - //printk("kthread - Stop Z80 thread\n"); - kthread_stop(kthread_z80); - - // Return the memory used for the Z80 'virtual memory' and control variables. - kfree(Z80Ctrl); - - // Nothing to be done for the hardware. - - // Cleanup and remove the device. - mutex_destroy(&Z80DRV_MUTEX); - device_destroy(class, MKDEV(major, 0)); - class_unregister(class); - class_destroy(class); - unregister_chrdev(major, DEVICE_NAME); - - //pr_info("z80drv: unregistered!\n"); -} - -module_init(ModuleInit); -module_exit(ModuleExit); diff --git a/software/FusionX/src/z80drv/MZ700/z80driver.h b/software/FusionX/src/z80drv/MZ700/z80driver.h deleted file mode 100644 index ede3d0db0..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80driver.h +++ /dev/null @@ -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 -// (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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#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 = | | | | | | | | | | | | -// IN 0xE0 = | |CGROM* | |VRAM* | | |CGROM | |VRAM | | | -// IN 0xE1 = | |DRAM | |DRAM | | | | |DRAM | | | -// -// = 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 - physical host RAM - // 0x40 - physical host ROM - // 0x20 - physical host VRAM - // 0x10 - physical host hardware - // 0x08 - virtual host RAM - // 0x04 - virtual host ROM - // 0x02 - virtual host hardware - // 16bit Input Address -> map -> Pointer to 24bit memory address + type flag. - // -> Pointer+ 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 - physical host hardware - // <0x40>FF - 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 diff --git a/software/FusionX/src/z80drv/MZ700/z80io.c b/software/FusionX/src/z80drv/MZ700/z80io.c deleted file mode 100644 index 08286199d..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80io.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -//#include -//#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" - -#include -#include -#include -#include - -//------------------------------------------------------------------------------------------------------------------------------- -// -// 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 diff --git a/software/FusionX/src/z80drv/MZ700/z80io.h b/software/FusionX/src/z80drv/MZ700/z80io.h deleted file mode 100755 index 9e5c1b0f2..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80io.h +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#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 diff --git a/software/FusionX/src/z80drv/MZ700/z80io_test.c b/software/FusionX/src/z80drv/MZ700/z80io_test.c deleted file mode 100644 index a0b6899cf..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80io_test.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include -#include - -//-------------------------------------------------------- -// 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); -} diff --git a/software/FusionX/src/z80drv/MZ700/z80menu.c b/software/FusionX/src/z80drv/MZ700/z80menu.c deleted file mode 100644 index bcc28a2ca..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80menu.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" -#include "z80menu.h" - -#include -#include -#include -#include - -void z80menu(void) -{ - // Locals. - -} diff --git a/software/FusionX/src/z80drv/MZ700/z80menu.h b/software/FusionX/src/z80drv/MZ700/z80menu.h deleted file mode 100755 index 107583682..000000000 --- a/software/FusionX/src/z80drv/MZ700/z80menu.h +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef Z80MENU_H -#define Z80MENU_H - -#ifdef __cplusplus - extern "C" { -#endif - -// Function definitions. -// -void z80menu(void); - -#ifdef __cplusplus -} -#endif -#endif // Z80MENU_H diff --git a/software/FusionX/src/z80drv/MZ80A/optparse.h b/software/FusionX/src/z80drv/MZ80A/optparse.h deleted file mode 100644 index f96184add..000000000 --- a/software/FusionX/src/z80drv/MZ80A/optparse.h +++ /dev/null @@ -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 */ diff --git a/software/FusionX/src/z80drv/MZ80A/z80menu.c b/software/FusionX/src/z80drv/MZ80A/z80menu.c deleted file mode 100644 index bcc28a2ca..000000000 --- a/software/FusionX/src/z80drv/MZ80A/z80menu.c +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z80io.h" -#include "z80menu.h" - -#include -#include -#include -#include - -void z80menu(void) -{ - // Locals. - -} diff --git a/software/FusionX/src/z80drv/MZ80A/z80menu.h b/software/FusionX/src/z80drv/MZ80A/z80menu.h deleted file mode 100755 index 107583682..000000000 --- a/software/FusionX/src/z80drv/MZ80A/z80menu.h +++ /dev/null @@ -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 -// -// 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 . -///////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef Z80MENU_H -#define Z80MENU_H - -#ifdef __cplusplus - extern "C" { -#endif - -// Function definitions. -// -void z80menu(void); - -#ifdef __cplusplus -} -#endif -#endif // Z80MENU_H diff --git a/software/FusionX/src/z80drv/Makefile b/software/FusionX/src/z80drv/Makefile index b4a3ee8f9..271061c08 100644 --- a/software/FusionX/src/z80drv/Makefile +++ b/software/FusionX/src/z80drv/Makefile @@ -1,40 +1,85 @@ #MODEL := MZ2000 #MODEL := MZ700 -MODEL := MZ80A +#MODEL := MZ80A +#MODEL := PCW8XXX +#MODEL := PCW9XXX KERNEL := $(PWD)/../../../linux/kernel FUSIONX := $(PWD)/../.. 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__ -CTRLINC += -IZeta/API -IZ80/API +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 -DTARGET_HOST_$(MODEL)=1 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/mhal_gpio.o z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o z80drv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o -all: +all: + @echo "Specify target host, ie. make " + @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 "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 "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 "Build driver for host: $(MODEL)" make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules + +z80ctrl: @echo "" @echo "Build z80ctrl tool for host: $(MODEL)" - $(CROSS)gcc $(CTRLINC) $(MODEL)/z80ctrl.c -o z80ctrl + $(CROSS)gcc $(CTRLINC) src/z80ctrl.c -o z80ctrl install: @echo "Copy kernel driver..." @cp z80drv.ko $(FUSIONX)/modules/ @echo "Copy z80ctrl app..." @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: make -C $(KERNEL) M=$(PWD) clean + @rm -f sharpbiter k64fcpu z80ctrl diff --git a/software/FusionX/src/z80drv/Z80 b/software/FusionX/src/z80drv/Z80 index 75d01a9cc..ada1e2921 160000 --- a/software/FusionX/src/z80drv/Z80 +++ b/software/FusionX/src/z80drv/Z80 @@ -1 +1 @@ -Subproject commit 75d01a9ccace36b471e4514911edb5ab477f819c +Subproject commit ada1e2921fc37fb916736802ee376a34335db05e diff --git a/software/FusionX/src/z80drv/src/emumz.c b/software/FusionX/src/z80drv/src/emumz.c new file mode 100644 index 000000000..cddfe0d43 --- /dev/null +++ b/software/FusionX/src/z80drv/src/emumz.c @@ -0,0 +1,6127 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Name: emumz.c +// Created: May 2021 +// Version: v1.0 +// Author(s): Philip Smart +// Description: The MZ Emulator Control Logic +// This source file contains the logic to present an on screen display menu, interact +// with the user to set the config or perform machine actions (tape load) and provide +// overall control functionality in order to service the running Sharp MZ Series +// emulation. +// +// Credits: +// Copyright: (c) 2019-2021 Philip Smart +// +// History: v1.0 May 2021 - Initial write of the EmuMZ software. +// v1.1 Nov 2021 - Further work on Video Controller and infrastructure. +// v1.2 Nov 2021 - Adding MZ2000 logic. +// v1.3 Dec 2021 - Adding MZ800 logic. +// v1.4 Jan 2022 - Adding floppy disk support. +// v1.5 Mar 2022 - Consolidation and bug rectification. +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80io.h" + +#include +#include +#include +#include + +#include "osd.h" +#include "sharpz.h" +#include "emumz.h" + +// Debug enable. +#define __EMUMZ_DEBUG__ 1 + +// Debug macros +#define debugf(a, ...) if(emuControl.debug) { printf("\033[1;31m%s: " a "\033[0m\n", __func__, ##__VA_ARGS__); } +#define debugfx(a, ...) if(emuControl.debug) { printf("\033[1;32m%s: " a "\033[0m\n", __func__, ##__VA_ARGS__); } + +// Version data. +#define EMUMZ_VERSION 1.50 +#define EMUMZ_VERSION_DATE "16/03/2022" + +////////////////////////////////////////////////////////////// +// Sharp MZ Series Emulation Service Methods // +////////////////////////////////////////////////////////////// + +#ifndef __APP__ // Protected methods which should only reside in the kernel on zOS. + +// Global scope variables used within the emuMZ core. +// +// First the ROM based default constants. These are used in the initial startup/configuration or configuration reset. They are copied into working memory as needed. +const static t_emuControl emuControlDefault = { + .active = 0, .debug = 1, .activeDialog = DIALOG_MENU, + .activeMenu = { + .menu[0] = MENU_DISABLED, .activeRow[0] = 0, .menuIdx = 0 + }, + .activeDir = { + .dir[0] = NULL, .activeRow[0] = 0, .dirIdx = 0 + }, + .menu = { + .rowPixelStart = 15, .colPixelStart = 40, .padding = 2, .colPixelsEnd = 12, + .inactiveFgColour = WHITE, .inactiveBgColour = BLACK, .greyedFgColour = BLUE, .greyedBgColour = BLACK, .textFgColour = PURPLE, .textBgColour = BLACK, .activeFgColour = BLUE, .activeBgColour = WHITE, + .font = FONT_7X8, .rowFontptr = &font7x8extended, + .activeRow = -1 + }, + .fileList = { + .rowPixelStart = 15, .colPixelStart = 40, .padding = 2, .colPixelsEnd = 12, .selectDir = 0, + .inactiveFgColour = WHITE, .inactiveBgColour = BLACK, .activeFgColour = BLUE, .activeBgColour = WHITE, + .font = FONT_5X7, .rowFontptr = &font5x7extended, + .activeRow = -1 + } + }; + +// Default configuration values for each emulation. As the number of target hosts on which the tranZPUter and emuMZ increase this will increase. This initial aim is that one binary fits all targets, ie. upload this software into an MZ-700/MZ-80A/MZ-2000 +// hosted tranZPUter it will detect the hardware and adapt. This is fine so long as there is free resources in the MK64FX512's 512KB ROM and FPGA but may need to be revisited in future, ie. be stored on disk. +const static t_emuConfig emuConfigDefault_MZ700 = { + .machineModel = MZ80K, .machineGroup = GROUP_MZ80K, .machineChanged = 1, + .params[MZ80K] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80K", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80k_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_80K_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80C] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80C", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_80C_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_1200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80A] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80A", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\sa1510-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80a_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_80A_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_USER_ROM_ADDR, .loadSize = 0x00000800 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ700] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ700", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\1z-013a.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\1z-013a-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz700_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_700_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz-1e05.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00001000 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ800] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ800", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz800_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz800_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_800_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz1500_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz1500_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_1500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80B] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 2, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80B", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz80b_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80b_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_80B_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2000] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 4, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2000", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2000_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2000_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_2000_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2200-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2200_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_2200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2500-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2500_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\700_2500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + } + }; + +// Default configuration for an MZ-80A host. +const static t_emuConfig emuConfigDefault_MZ80A = { + .machineModel = MZ80K, .machineGroup = GROUP_MZ80K, .machineChanged = 1, + .params[MZ80K] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80K", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80k_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_80K_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80C] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80C", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_80C_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_1200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80A] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80A", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\sa1510-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80a_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_80A_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_USER_ROM_ADDR, .loadSize = 0x00000800 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ700] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ700", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\1z-013a.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\1z-013a-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz700_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_700_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz-1e05.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00001000 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ800] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ800", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz800_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz800_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_800_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz1500_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz1500_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_1500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80B] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 2, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80B", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz80b_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80b_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_80B_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2000] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 4, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2000", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2000_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2000_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_2000_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2200-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2200_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_2200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_640x480, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2500-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2500_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\80A_2500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + } + }; + +// Default configuration for an MZ-2000 host. +const static t_emuConfig emuConfigDefault_MZ2000 = { + .machineModel = MZ80K, .machineGroup = GROUP_MZ80K, .machineChanged = 1, + .params[MZ80K] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80K", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80k_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_80K_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80C] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80C", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 0, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_160K, .polarity = POLARITY_NORMAL, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sp1002.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_80C_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80kfdif.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000400 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80c_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_1200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80A] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80A", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\sa1510.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\sa1510-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80a_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_80A_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_USER_ROM_ADDR, .loadSize = 0x00000800 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz80a_fdc.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00000800 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ700] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ700", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\1z-013a.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "0:\\TZFS\\1z-013a-8.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz700_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_700_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "0:\\TZFS\\mz-1e05.rom", .romEnabled = 1, .loadAddr = MZ_EMU_FDC_ROM_ADDR, .loadSize = 0x00001000 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ800] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ800", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz800_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz800_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_800_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ1500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ1500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz1500_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00004000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz1500_cgrom.rom", .romEnabled = 0, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00001000 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_1500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00001000 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ80B] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 2, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ80B", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz80b_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00000800 }, + .romCG = { .romFileName = "0:\\TZFS\\mz80b_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_80B_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2000] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 4, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2000", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2000_ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2000_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_2000_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2200] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_MONO, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2200", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2200-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2200_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_2200_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + }, + .params[MZ2500] = { + .cpuSpeed = 0 , .memSize = 1, .audioSource = 0, .audioHardware = 1, .audioVolume = 1, .audioMute = 0, .audioMix = 0, .displayType = MZ_EMU_DISPLAY_COLOUR, .displayOption = 0, .displayOutput = VMMODE_VGA_INT, + .vramMode = 0, .vramWaitMode = 0, .gramMode = 0, .pcgMode = 0, .aspectRatio = 0, .scanDoublerFX = 0, .loadDirectFilter = 0, + .mz800Mode = 0, .mz800Printer = 0, .mz800TapeIn = 0, .queueTapeFilter = 0, .tapeButtons = 3, .fastTapeLoad = 2, .tapeSavePath = "0:\\MZF\\MZ2500", + .cmtAsciiMapping = 3, .cmtMode = 0, .fddEnabled = 1, .autoStart = 0, + .fdd[0] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[1] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[2] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .fdd[3] = { .fileName = "", .imgType = IMAGETYPE_IMG, .mounted = 0, .diskType = DISKTYPE_320K, .polarity = POLARITY_INVERTED, .updateMode = UPDATEMODE_READWRITE }, + .romMonitor40 = { .romFileName = "0:\\TZFS\\mz2500-ipl.rom", .romEnabled = 1, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romMonitor80 = { .romFileName = "", .romEnabled = 0, .loadAddr = MZ_EMU_ROM_ADDR, .loadSize = 0x00001000 }, + .romCG = { .romFileName = "0:\\TZFS\\mz2500_cgrom.rom", .romEnabled = 1, .loadAddr = MZ_EMU_CGROM_ADDR, .loadSize = 0x00000800 }, + .romKeyMap = { .romFileName = "0:\\TZFS\\2000_2500_km.rom", .romEnabled = 1, .loadAddr = MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_MAP_ADDR, .loadSize = 0x00000200 }, + .romUser = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .romFDC = { .romFileName = "", .romEnabled = 0, .loadAddr = 0x000000, .loadSize = 0x00000100 }, + .loadApp = { .appFileName = "", .appEnabled = 0, .preKeyInsertion = {}, .postKeyInsertion = {} } + } + }; + +const static t_scanMap mapToScanCode[] = { // MZ-80K MZ-80C MZ-1200 MZ-80A MZ-700 MZ-1500 MZ-800 MZ-80B MZ-2000 MZ-2200 MZ-2500 + { 'A', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0xf7, KEY_NOCTRL_BIT }, { 1, 0xf7, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'B', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xfe, KEY_NOCTRL_BIT }, { 3, 0xfe, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'C', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xfe, KEY_NOCTRL_BIT }, { 2, 0xfe, KEY_NOCTRL_BIT }, { 4, 0xdf, KEY_NOCTRL_BIT }, { 4, 0xdf, KEY_NOCTRL_BIT }, { 4, 0xdf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'D', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xf7, KEY_NOCTRL_BIT }, { 2, 0xf7, KEY_NOCTRL_BIT }, { 4, 0xef, KEY_NOCTRL_BIT }, { 4, 0xef, KEY_NOCTRL_BIT }, { 4, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'E', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xef, KEY_NOCTRL_BIT }, { 2, 0xef, KEY_NOCTRL_BIT }, { 4, 0xf7, KEY_NOCTRL_BIT }, { 4, 0xf7, KEY_NOCTRL_BIT }, { 4, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'F', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xfb, KEY_NOCTRL_BIT }, { 3, 0xfb, KEY_NOCTRL_BIT }, { 4, 0xfb, KEY_NOCTRL_BIT }, { 4, 0xfb, KEY_NOCTRL_BIT }, { 4, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'G', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xf7, KEY_NOCTRL_BIT }, { 3, 0xf7, KEY_NOCTRL_BIT }, { 4, 0xfd, KEY_NOCTRL_BIT }, { 4, 0xfd, KEY_NOCTRL_BIT }, { 4, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'H', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xfb, KEY_NOCTRL_BIT }, { 4, 0xfb, KEY_NOCTRL_BIT }, { 4, 0xfe, KEY_NOCTRL_BIT }, { 4, 0xfe, KEY_NOCTRL_BIT }, { 4, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'I', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xdf, KEY_NOCTRL_BIT }, { 4, 0xdf, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'J', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xf7, KEY_NOCTRL_BIT }, { 4, 0xf7, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'K', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 3, 0xdf, KEY_NOCTRL_BIT }, { 3, 0xdf, KEY_NOCTRL_BIT }, { 3, 0xdf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'L', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xf7, KEY_NOCTRL_BIT }, { 5, 0xf7, KEY_NOCTRL_BIT }, { 3, 0xef, KEY_NOCTRL_BIT }, { 3, 0xef, KEY_NOCTRL_BIT }, { 3, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'M', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xfe, KEY_NOCTRL_BIT }, { 5, 0xfe, KEY_NOCTRL_BIT }, { 3, 0xf7, KEY_NOCTRL_BIT }, { 3, 0xf7, KEY_NOCTRL_BIT }, { 3, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'N', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xfd, KEY_NOCTRL_BIT }, { 4, 0xfd, KEY_NOCTRL_BIT }, { 3, 0xfb, KEY_NOCTRL_BIT }, { 3, 0xfb, KEY_NOCTRL_BIT }, { 3, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'O', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 3, 0xfd, KEY_NOCTRL_BIT }, { 3, 0xfd, KEY_NOCTRL_BIT }, { 3, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'P', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xdf, KEY_NOCTRL_BIT }, { 5, 0xdf, KEY_NOCTRL_BIT }, { 3, 0xfe, KEY_NOCTRL_BIT }, { 3, 0xfe, KEY_NOCTRL_BIT }, { 3, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'Q', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'R', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xdf, KEY_NOCTRL_BIT }, { 2, 0xdf, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'S', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xfb, KEY_NOCTRL_BIT }, { 2, 0xfb, KEY_NOCTRL_BIT }, { 2, 0xdf, KEY_NOCTRL_BIT }, { 2, 0xdf, KEY_NOCTRL_BIT }, { 2, 0xdf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'T', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xef, KEY_NOCTRL_BIT }, { 3, 0xef, KEY_NOCTRL_BIT }, { 2, 0xef, KEY_NOCTRL_BIT }, { 2, 0xef, KEY_NOCTRL_BIT }, { 2, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'U', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xef, KEY_NOCTRL_BIT }, { 4, 0xef, KEY_NOCTRL_BIT }, { 2, 0xf7, KEY_NOCTRL_BIT }, { 2, 0xf7, KEY_NOCTRL_BIT }, { 2, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'V', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xfd, KEY_NOCTRL_BIT }, { 3, 0xfd, KEY_NOCTRL_BIT }, { 2, 0xfb, KEY_NOCTRL_BIT }, { 2, 0xfb, KEY_NOCTRL_BIT }, { 2, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'W', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0xdf, KEY_NOCTRL_BIT }, { 1, 0xdf, KEY_NOCTRL_BIT }, { 2, 0xfd, KEY_NOCTRL_BIT }, { 2, 0xfd, KEY_NOCTRL_BIT }, { 2, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'X', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xfd, KEY_NOCTRL_BIT }, { 2, 0xfd, KEY_NOCTRL_BIT }, { 2, 0xfe, KEY_NOCTRL_BIT }, { 2, 0xfe, KEY_NOCTRL_BIT }, { 2, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'Y', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xdf, KEY_NOCTRL_BIT }, { 3, 0xdf, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 'Z', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0xfe, KEY_NOCTRL_BIT }, { 1, 0xfe, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + + { '0', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '1', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '2', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '3', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xdf, KEY_NOCTRL_BIT }, { 5, 0xdf, KEY_NOCTRL_BIT }, { 5, 0xdf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '4', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '5', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xf7, KEY_NOCTRL_BIT }, { 5, 0xf7, KEY_NOCTRL_BIT }, { 5, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '6', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '7', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '8', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_NOCTRL_BIT }, { 5, 0xfe, KEY_NOCTRL_BIT }, { 5, 0xfe, KEY_NOCTRL_BIT }, { 5, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '9', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + + { '_', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0x7f, KEY_SHIFT_BIT }, { 5, 0x7f, KEY_SHIFT_BIT }, { 0, 0xdf, KEY_NOCTRL_BIT }, { 0, 0xdf, KEY_NOCTRL_BIT }, { 0, 0xdf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '!', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0xbf, KEY_SHIFT_BIT }, { 1, 0xbf, KEY_SHIFT_BIT }, { 5, 0x7f, KEY_SHIFT_BIT }, { 5, 0x7f, KEY_SHIFT_BIT }, { 5, 0x7f, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '"', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 1, 0x7f, KEY_SHIFT_BIT }, { 1, 0x7f, KEY_SHIFT_BIT }, { 5, 0xbf, KEY_SHIFT_BIT }, { 5, 0xbf, KEY_SHIFT_BIT }, { 5, 0xbf, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '#', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0xbf, KEY_SHIFT_BIT }, { 2, 0xbf, KEY_SHIFT_BIT }, { 5, 0xdf, KEY_SHIFT_BIT }, { 5, 0xdf, KEY_SHIFT_BIT }, { 5, 0xdf, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '$', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 2, 0x7f, KEY_SHIFT_BIT }, { 2, 0x7f, KEY_SHIFT_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 5, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '%', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0xbf, KEY_SHIFT_BIT }, { 3, 0xbf, KEY_SHIFT_BIT }, { 5, 0xf7, KEY_SHIFT_BIT }, { 5, 0xf7, KEY_SHIFT_BIT }, { 5, 0xf7, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '&', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 3, 0x7f, KEY_SHIFT_BIT }, { 3, 0x7f, KEY_SHIFT_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 5, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '\'', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xbf, KEY_SHIFT_BIT }, { 4, 0xbf, KEY_SHIFT_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '(', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0x7f, KEY_SHIFT_BIT }, { 4, 0x7f, KEY_SHIFT_BIT }, { 5, 0xfe, KEY_SHIFT_BIT }, { 5, 0xfe, KEY_SHIFT_BIT }, { 5, 0xfe, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ')', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xbf, KEY_SHIFT_BIT }, { 5, 0xbf, KEY_SHIFT_BIT }, { 6, 0xfb, KEY_SHIFT_BIT }, { 6, 0xfb, KEY_SHIFT_BIT }, { 6, 0xfb, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '^', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '~', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_SHIFT_BIT }, { 6, 0x7f, KEY_SHIFT_BIT }, { 6, 0xbf, KEY_SHIFT_BIT }, { 6, 0xbf, KEY_SHIFT_BIT }, { 6, 0xbf, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '-', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_NOCTRL_BIT }, { 1, 0xdf, KEY_SHIFT_BIT }, { 1, 0xdf, KEY_SHIFT_BIT }, { 1, 0xdf, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '=', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xbf, KEY_SHIFT_BIT }, { 6, 0xbf, KEY_SHIFT_BIT }, { 6, 0xdf, KEY_SHIFT_BIT }, { 6, 0xdf, KEY_SHIFT_BIT }, { 6, 0xdf, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '\\', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xbf, KEY_NOCTRL_BIT }, { 7, 0xbf, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 6, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '|', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xbf, KEY_SHIFT_BIT }, { 7, 0xbf, KEY_SHIFT_BIT }, { 6, 0x7f, KEY_SHIFT_BIT }, { 6, 0x7f, KEY_SHIFT_BIT }, { 6, 0x7f, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '[', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xdf, KEY_NOCTRL_BIT }, { 6, 0xdf, KEY_NOCTRL_BIT }, { 1, 0xef, KEY_NOCTRL_BIT }, { 1, 0xef, KEY_NOCTRL_BIT }, { 1, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '{', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xdf, KEY_SHIFT_BIT }, { 6, 0xdf, KEY_SHIFT_BIT }, { 1, 0xef, KEY_SHIFT_BIT }, { 1, 0xef, KEY_SHIFT_BIT }, { 1, 0xef, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ']', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xfb, KEY_NOCTRL_BIT }, { 7, 0xfb, KEY_NOCTRL_BIT }, { 1, 0xf7, KEY_NOCTRL_BIT }, { 1, 0xf7, KEY_NOCTRL_BIT }, { 1, 0xf7, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '}', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xfb, KEY_SHIFT_BIT }, { 7, 0xfb, KEY_SHIFT_BIT }, { 1, 0xf7, KEY_SHIFT_BIT }, { 1, 0xf7, KEY_SHIFT_BIT }, { 1, 0xf7, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ':', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_NOCTRL_BIT }, { 0, 0xfd, KEY_NOCTRL_BIT }, { 0, 0xfd, KEY_NOCTRL_BIT }, { 0, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '*', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xf7, KEY_SHIFT_BIT }, { 6, 0xf7, KEY_SHIFT_BIT }, { 0, 0xfd, KEY_SHIFT_BIT }, { 0, 0xfd, KEY_SHIFT_BIT }, { 0, 0xfd, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ';', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_NOCTRL_BIT }, { 0, 0xfb, KEY_NOCTRL_BIT }, { 0, 0xfb, KEY_NOCTRL_BIT }, { 0, 0xfb, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '+', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xfb, KEY_SHIFT_BIT }, { 6, 0xfb, KEY_SHIFT_BIT }, { 0, 0xfb, KEY_SHIFT_BIT }, { 0, 0xfb, KEY_SHIFT_BIT }, { 0, 0xfb, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ',', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_NOCTRL_BIT }, { 6, 0xfd, KEY_NOCTRL_BIT }, { 6, 0xfd, KEY_NOCTRL_BIT }, { 6, 0xfd, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '<', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 5, 0xfd, KEY_SHIFT_BIT }, { 5, 0xfd, KEY_SHIFT_BIT }, { 6, 0xfd, KEY_SHIFT_BIT }, { 6, 0xfd, KEY_SHIFT_BIT }, { 6, 0xfd, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '.', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '>', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xfe, KEY_SHIFT_BIT }, { 6, 0xfe, KEY_SHIFT_BIT }, { 6, 0xfe, KEY_SHIFT_BIT }, { 6, 0xfe, KEY_SHIFT_BIT }, { 6, 0xfe, KEY_SHIFT_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '/', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 6, 0xfd, KEY_NOCTRL_BIT }, { 6, 0xfd, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { '?', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 7, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 0x0d, { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 7, 0xf7, KEY_NOCTRL_BIT }, { 7, 0xf7, KEY_NOCTRL_BIT }, { 0, 0xfe, KEY_NOCTRL_BIT }, { 0, 0xfe, KEY_NOCTRL_BIT }, { 0, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { ' ', { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 4, 0xfe, KEY_NOCTRL_BIT }, { 4, 0xfe, KEY_NOCTRL_BIT }, { 6, 0xef, KEY_NOCTRL_BIT }, { 6, 0xef, KEY_NOCTRL_BIT }, { 6, 0xef, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 0xf8, { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0, 0xfe, KEY_NOCTRL_BIT }, { 0, 0xfe, KEY_NOCTRL_BIT }, { 8, 0xfe, KEY_NOCTRL_BIT }, { 8, 0xfe, KEY_NOCTRL_BIT }, { 8, 0xfe, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 0xf9, { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0, 0x7f, KEY_NOCTRL_BIT }, { 0, 0x7f, KEY_NOCTRL_BIT }, { 8, 0xbf, KEY_NOCTRL_BIT }, { 8, 0xbf, KEY_NOCTRL_BIT }, { 8, 0xbf, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + { 0xfa, { { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0, 0x7f, KEY_NOCTRL_BIT }, { 0, 0x7f, KEY_NOCTRL_BIT }, { 8, 0x7f, KEY_NOCTRL_BIT }, { 8, 0x7f, KEY_NOCTRL_BIT }, { 8, 0x7f, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT }, { 0xff, 0xff, KEY_NOCTRL_BIT } } }, + }; + +// Configuration working structures. Declared static rather than malloc'd as they are used so often and malloc doesnt offer any benefit for an integral data block. +static t_emuControl emuControl; +static t_emuConfig emuConfig; + +// Real time millisecond counter, interrupt driven. Needs to be volatile in order to prevent the compiler optimising it away. +uint32_t volatile *ms = &systick_millis_count; + +// Method to return the emulation control software version. +static const char version[8]; +const char *EMZGetVersion(void) +{ + sprintf(version, "v%.2f", EMUMZ_VERSION); + return(version); +} + +// Method to return the emulation control software version date. +static const char versionDate[sizeof(EMUMZ_VERSION_DATE)+1]; +const char *EMZGetVersionDate(void) +{ + sprintf(versionDate, "%s", EMUMZ_VERSION_DATE); + return(versionDate); +} + +// Method to lookup a given key for a given machine and if found return the keyboard row/col scan codes and any key modifier. +// +t_numCnv EMZMapToScanCode(enum MACHINE_HW_TYPES machine, uint8_t key) +{ + // Locals. + uint16_t idx; + uint8_t row = 0xff, shiftRow = 0xff, ctrlRow = 0xff, breakRow = 0xff; + uint8_t col = 0xff, shiftCol = 0xff, ctrlCol = 0xff, breakCol = 0xff; + uint8_t mod = 0; + t_numCnv result; + + // Loop through the lookup table, based on the host layout, and try to find a key match. + for(idx=0; idx < NUMELEM(mapToScanCode); idx++) + { + // Key matched? + if(mapToScanCode[idx].key == toupper(key)) + { + row = mapToScanCode[idx].code[machine].scanRow; + col = mapToScanCode[idx].code[machine].scanCol; + mod = mapToScanCode[idx].code[machine].scanCtrl; + } + + // Match SHIFT? + if(mapToScanCode[idx].key == 0xf8) + { + shiftRow = mapToScanCode[idx].code[machine].scanRow; + shiftCol = mapToScanCode[idx].code[machine].scanCol; + } + + // Match CTRL? + if(mapToScanCode[idx].key == 0xf9) + { + ctrlRow = mapToScanCode[idx].code[machine].scanRow; + ctrlCol = mapToScanCode[idx].code[machine].scanCol; + } + + // Match BREAK? + if(mapToScanCode[idx].key == 0xfa) + { + breakRow = mapToScanCode[idx].code[machine].scanRow; + breakCol = mapToScanCode[idx].code[machine].scanCol; + } + } + // Lower case keys arent stored in the table so update the shift modifier if lower case. + if(row != 0xff && key >= 'a' && key <= 'z') + { + mod = KEY_SHIFT_BIT; + } + + // Put data into correct part of the 32bit return word. 0 = Key Row, 1 = Key Col, 2 = Modifier Row, 3 = Modifier Col. 0xff = not valid. + result.b[0] = row; + result.b[1] = col; + result.b[2] = mod == KEY_SHIFT_BIT ? shiftRow : mod == KEY_CTRL_BIT ? ctrlRow : mod == KEY_BREAK_BIT ? breakRow : 0xff; + result.b[3] = mod == KEY_SHIFT_BIT ? shiftCol : mod == KEY_CTRL_BIT ? ctrlCol : mod == KEY_BREAK_BIT ? breakCol : 0xff; + + // Return result. + return(result); +} + +// Method to set the menu row padding (ie. pixel spacing above/below the characters). +void EMZSetMenuRowPadding(uint8_t padding) +{ + // Sanity check. + // + if(padding > ((uint16_t)OSDGet(ACTIVE_MAX_Y) / 8)) + return; + + // Store padding in private member. + emuControl.menu.padding = padding; + return; +} + +// Method to set the font for use in row characters. +// +void EMZSetMenuFont(enum FONTS font) +{ + emuControl.menu.rowFontptr = OSDGetFont(font); + emuControl.menu.font = font; +} + +// Method to change the row active colours. +// +void EMZSetRowColours(enum COLOUR rowFg, enum COLOUR rowBg, enum COLOUR greyedFg, enum COLOUR greyedBg, enum COLOUR textFg, enum COLOUR textBg, enum COLOUR activeFg, enum COLOUR activeBg) +{ + emuControl.menu.inactiveFgColour = rowFg; + emuControl.menu.inactiveBgColour = rowBg; + emuControl.menu.greyedFgColour = greyedFg; + emuControl.menu.greyedBgColour = greyedBg; + emuControl.menu.textFgColour = textFg; + emuControl.menu.textBgColour = textBg; + emuControl.menu.activeFgColour = activeFg; + emuControl.menu.activeBgColour = activeBg; +} + +// Method to get the maximum number of columns available for a menu row with the current selected font. +// +uint16_t EMZGetMenuColumnWidth(void) +{ + uint16_t maxPixels = OSDGet(ACTIVE_MAX_X); + return( (maxPixels - emuControl.menu.colPixelStart - emuControl.menu.colPixelsEnd) / (emuControl.menu.rowFontptr->width + emuControl.menu.rowFontptr->spacing) ); +} + +// Get the group to which the current machine belongs: +// 0 - MZ80K/C/A type +// 1 - MZ700 type +// 2 - MZ80B/2000 type +// +short EMZGetMachineGroup(void) +{ + short machineGroup = GROUP_MZ80K; + + // Set value according to machine model. + // + switch(emuConfig.machineModel) + { + // These machines currently underdevelopment, so fall through to MZ80K + case MZ80B: + case MZ2000: + case MZ2200: + case MZ2500: + machineGroup = GROUP_MZ80B; + break; + + case MZ80K: + case MZ80C: + case MZ1200: + case MZ80A: + machineGroup = GROUP_MZ80K; + break; + + case MZ700: + case MZ1500: + case MZ800: + machineGroup = GROUP_MZ700; + break; + + default: + machineGroup = GROUP_MZ80K; + break; + } + + return(machineGroup); +} + +// Method to return a char string which represents the current selected machine name. +const char *EMZGetMachineModelChoice(void) +{ + // Locals. + // + + return(MZMACHINES[emuConfig.machineModel]); +} + +// Method to make the side bar title from the active machine. +char *EMZGetMachineTitle(void) +{ + static char title[MAX_MACHINE_TITLE_LEN]; + + sprintf(title, "SHARP %s", EMZGetMachineModelChoice()); + return(title); +} + + +// Method to change the emulated machine, choice based on the actual implemented machines in the FPGA core. +void EMZNextMachineModel(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + // Forward to next active machine - skip machines under development or not instantiated. + do { + emuConfig.machineModel = (emuConfig.machineModel+1 >= MAX_MZMACHINES ? 0 : emuConfig.machineModel+1); + emuConfig.machineGroup = EMZGetMachineGroup(); + } while(MZ_ACTIVE[emuConfig.machineModel] == 0); + emuConfig.machineChanged = 1; + + // Need to rewrite the menu as the choice will affect displayed items. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + return; +} + +// Method to return a char string which represents the current selected CPU speed. +const char *EMZGetCPUSpeedChoice(void) +{ + // Locals. + // + + return(SHARPMZ_CPU_SPEED[emuConfig.machineGroup][emuConfig.params[emuConfig.machineModel].cpuSpeed]); +} + +// Method to change the CPU Speed, choice based on the actual selected machine. +void EMZNextCPUSpeed(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].cpuSpeed = (emuConfig.params[emuConfig.machineModel].cpuSpeed+1 >= NUMELEM(SHARPMZ_CPU_SPEED[emuConfig.machineGroup]) || SHARPMZ_CPU_SPEED[emuConfig.machineGroup][emuConfig.params[emuConfig.machineModel].cpuSpeed+1] == NULL ? 0 : emuConfig.params[emuConfig.machineModel].cpuSpeed+1); + } + return; +} + +// Method to return a char string which represents the current selected memory size. +const char *EMZGetMemSizeChoice(void) +{ + // Locals. + // + + return(SHARPMZ_MEM_SIZE[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].memSize]); +} + +// Method to change the memory size, choice based on the actual selected machine. +void EMZNextMemSize(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + // Move to the next valid entry, looping round as necessary. + do { + emuConfig.params[emuConfig.machineModel].memSize = (emuConfig.params[emuConfig.machineModel].memSize+1 >= NUMELEM(SHARPMZ_MEM_SIZE[emuConfig.machineModel]) ? 0 : emuConfig.params[emuConfig.machineModel].memSize+1); + } while(SHARPMZ_MEM_SIZE[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].memSize] == NULL); + } + return; +} + +// Method to convert the memory size into a bit value for uploading to hardware. Normally a 1:1 but allow leeway for deviations. +uint8_t EMZGetMemSizeValue(void) +{ + // Locals. + uint8_t result; + + // Decode according to machine selected. + // + switch(emuConfig.machineModel) + { + case MZ80K: + case MZ80C: + case MZ1200: + case MZ80A: + case MZ700: + case MZ1500: + case MZ800: + case MZ80B: + case MZ2000: + case MZ2200: + result = emuConfig.params[emuConfig.machineModel].memSize; + break; + + case MZ2500: + result = 0x00; + break; + + } + return(result); +} + +// Method to return a char string which represents the current selected MZ800 Mode. +const char *EMZGetMZ800ModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_MZ800_MODE[emuConfig.params[emuConfig.machineModel].mz800Mode]); +} + +// Method to change the MZ800 Mode. +void EMZNextMZ800Mode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].mz800Mode = (emuConfig.params[emuConfig.machineModel].mz800Mode+1 >= NUMELEM(SHARPMZ_MZ800_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].mz800Mode+1); + } + return; +} + +// Method to return a char string which represents the current selected MZ800 Printer setting. +const char *EMZGetMZ800PrinterChoice(void) +{ + // Locals. + // + return(SHARPMZ_MZ800_PRINTER[emuConfig.params[emuConfig.machineModel].mz800Printer]); +} + +// Method to change the MZ800 Printer setting. +void EMZNextMZ800Printer(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].mz800Printer = (emuConfig.params[emuConfig.machineModel].mz800Printer+1 >= NUMELEM(SHARPMZ_MZ800_PRINTER) ? 0 : emuConfig.params[emuConfig.machineModel].mz800Printer+1); + } + return; +} + +// Method to return a char string which represents the current selected MZ800 Printer setting. +const char *EMZGetMZ800TapeInChoice(void) +{ + // Locals. + // + return(SHARPMZ_MZ800_TAPEIN[emuConfig.params[emuConfig.machineModel].mz800TapeIn]); +} + +// Method to change the MZ800 Tape Input setting. +void EMZNextMZ800TapeIn(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].mz800TapeIn = (emuConfig.params[emuConfig.machineModel].mz800TapeIn+1 >= NUMELEM(SHARPMZ_MZ800_TAPEIN) ? 0 : emuConfig.params[emuConfig.machineModel].mz800TapeIn+1); + } + return; +} + +// Method to return a char string which represents the current selected Audio Source. +const char *EMZGetAudioSourceChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUDIO_SOURCE[emuConfig.params[emuConfig.machineModel].audioSource]); +} + +// Method to change the Audio Source, choice based on the actual selected machine. +void EMZNextAudioSource(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].audioSource = (emuConfig.params[emuConfig.machineModel].audioSource+1 >= NUMELEM(SHARPMZ_AUDIO_SOURCE) ? 0 : emuConfig.params[emuConfig.machineModel].audioSource+1); + + // Write the updated value immediately so as to change the audio source. + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[emuConfig.machineModel].audioHardware << 7 | emuConfig.params[emuConfig.machineModel].audioMix << 5 | (emuConfig.params[emuConfig.machineModel].audioMute == 1 ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume << 1) | emuConfig.params[emuConfig.machineModel].audioSource; + writeZ80Array(MZ_EMU_ADDR_REG_AUDIO, &emuConfig.emuRegisters[MZ_EMU_REG_AUDIO], 1, FPGA); + } + return; +} + +// Method to return a char string which represents the current selected Audio Hardware Driver. +const char *EMZGetAudioHardwareChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUDIO_HARDWARE[emuConfig.params[emuConfig.machineModel].audioHardware]); +} + +// Method to change the Audio Hardware Driver, choice based on the actual selected machine. +void EMZNextAudioHardware(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].audioHardware = (emuConfig.params[emuConfig.machineModel].audioHardware+1 >= NUMELEM(SHARPMZ_AUDIO_HARDWARE) ? 0 : emuConfig.params[emuConfig.machineModel].audioHardware+1); + + // Write the updated value immediately so as to change the sound hardware. + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[emuConfig.machineModel].audioHardware << 7 | emuConfig.params[emuConfig.machineModel].audioMix << 5 | (emuConfig.params[emuConfig.machineModel].audioMute == 1 ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume << 1) | emuConfig.params[emuConfig.machineModel].audioSource; + writeZ80Array(MZ_EMU_ADDR_REG_AUDIO, &emuConfig.emuRegisters[MZ_EMU_REG_AUDIO], 1, FPGA); + } + + // Need to rewrite the menu as the choice will affect displayed items. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + return; +} + +// Method to return a char string which represents the current selected Audio Volume. +const char *EMZGetAudioVolumeChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUDIO_VOLUME[emuConfig.params[emuConfig.machineModel].audioVolume]); +} + +// Method to change the Audio Volume, choice based on the actual selected machine. +void EMZNextAudioVolume(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].audioVolume = (emuConfig.params[emuConfig.machineModel].audioVolume+1 >= NUMELEM(SHARPMZ_AUDIO_VOLUME) ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume+1); + + // Write the updated value immediately so as to adjust the sound volume. + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[emuConfig.machineModel].audioHardware << 7 | emuConfig.params[emuConfig.machineModel].audioMix << 5 | (emuConfig.params[emuConfig.machineModel].audioMute == 1 ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume << 1) | emuConfig.params[emuConfig.machineModel].audioSource; + writeZ80Array(MZ_EMU_ADDR_REG_AUDIO, &emuConfig.emuRegisters[MZ_EMU_REG_AUDIO], 1, FPGA); + } + return; +} + +// Method to return a char string which represents the current selected Audio Mute. +const char *EMZGetAudioMuteChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUDIO_MUTE[emuConfig.params[emuConfig.machineModel].audioMute]); +} + +// Method to change the Audio Mute, choice based on the actual selected machine. +void EMZNextAudioMute(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].audioMute = (emuConfig.params[emuConfig.machineModel].audioMute+1 >= NUMELEM(SHARPMZ_AUDIO_MUTE) ? 0 : emuConfig.params[emuConfig.machineModel].audioMute+1); + + // Write the updated value immediately so as to mute the sound. + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[emuConfig.machineModel].audioHardware << 7 | emuConfig.params[emuConfig.machineModel].audioMix << 5 | (emuConfig.params[emuConfig.machineModel].audioMute == 1 ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume << 1) | emuConfig.params[emuConfig.machineModel].audioSource; + writeZ80Array(MZ_EMU_ADDR_REG_AUDIO, &emuConfig.emuRegisters[MZ_EMU_REG_AUDIO], 1, FPGA); + } + return; +} + +// Method to return a char string which represents the current selected Audio channel mix. +const char *EMZGetAudioMixChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUDIO_MIX[emuConfig.params[emuConfig.machineModel].audioMix]); +} + +// Method to change the Audio channel mix, choice based on the actual selected machine. +void EMZNextAudioMix(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].audioMix = (emuConfig.params[emuConfig.machineModel].audioMix+1 >= NUMELEM(SHARPMZ_AUDIO_MIX) ? 0 : emuConfig.params[emuConfig.machineModel].audioMix+1); + + // Write the updated value immediately so as to change the channel mix. + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[emuConfig.machineModel].audioHardware << 7 | emuConfig.params[emuConfig.machineModel].audioMix << 5 | (emuConfig.params[emuConfig.machineModel].audioMute == 1 ? 0 : emuConfig.params[emuConfig.machineModel].audioVolume << 1) | emuConfig.params[emuConfig.machineModel].audioSource; + writeZ80Array(MZ_EMU_ADDR_REG_AUDIO, &emuConfig.emuRegisters[MZ_EMU_REG_AUDIO], 1, FPGA); + } + return; +} + +// Method to return a char string which represents the current selected Display Type. +const char *EMZGetDisplayTypeChoice(void) +{ + // Locals. + // + return(SHARPMZ_DISPLAY_TYPE[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].displayType]); +} + +// Method to change the Display Type, choice based on the actual selected machine. +void EMZNextDisplayType(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + // Move to the next valid entry, looping round as necessary. + do { + emuConfig.params[emuConfig.machineModel].displayType = (emuConfig.params[emuConfig.machineModel].displayType+1 >= NUMELEM(SHARPMZ_DISPLAY_TYPE[emuConfig.machineModel]) ? 0 : emuConfig.params[emuConfig.machineModel].displayType+1); + } while(SHARPMZ_DISPLAY_TYPE[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].displayType] == NULL); + } + return; +} + +// Method to return a char string which represents the current selected Display Option installed. +const char *EMZGetDisplayOptionChoice(void) +{ + // Locals. + // + + return(SHARPMZ_DISPLAY_OPTION[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].displayOption]); +} + +// Method to change the installed display option, choice based on the actual selected machine. +void EMZNextDisplayOption(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + // Move to the next valid entry, looping round as necessary. + do { + emuConfig.params[emuConfig.machineModel].displayOption = (emuConfig.params[emuConfig.machineModel].displayOption+1 >= NUMELEM(SHARPMZ_DISPLAY_OPTION[emuConfig.machineModel]) ? 0 : emuConfig.params[emuConfig.machineModel].displayOption+1); + } while(SHARPMZ_DISPLAY_OPTION[emuConfig.machineModel][emuConfig.params[emuConfig.machineModel].displayOption] == NULL); + + // Need to rewrite the menu as the choice will affect displayed items. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + return; +} + +// Method to translate the selected options into an option byte which can be written to hardware. This mechanism needs to be table driven eventually! +// +uint8_t EMZGetDisplayOptionValue(void) +{ + // Locals. + uint8_t result; + + // Decode according to machine selected. + // + switch(emuConfig.machineModel) + { + case MZ80K: + case MZ80C: + case MZ1200: + result = 0; + break; + + case MZ80A: + case MZ700: + result |= emuConfig.params[emuConfig.machineModel].displayOption == 1 ? 0x08 : 0x00; + break; + + case MZ1500: + result = 0x08; + break; + + case MZ800: + result = emuConfig.params[emuConfig.machineModel].displayOption == 1 ? 0x10 : 0x00; + break; + + case MZ80B: + result = 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 1 ? 0x01 : 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 2 ? 0x03 : 0x00; + printf("displayOption=%d,%d\n", emuConfig.params[emuConfig.machineModel].displayOption, result); + break; + + case MZ2000: + result = 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 1 ? 0x01 : 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 2 ? 0x03 : 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 3 ? 0x05 : 0x00; + result |= emuConfig.params[emuConfig.machineModel].displayOption == 4 ? 0x07 : 0x00; + break; + + case MZ2200: + result = 0x07; + break; + + case MZ2500: + result = 0x00; + break; + + } + return(result); +} + +// Method to return a char string which represents the current selected Display Output. +const char *EMZGetDisplayOutputChoice(void) +{ + // Locals. + // + return(SHARPMZ_DISPLAY_OUTPUT[emuConfig.params[emuConfig.machineModel].displayOutput]); +} + +// Method to change the Display Output, choice based on the actual selected machine. +void EMZNextDisplayOutput(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].displayOutput = (emuConfig.params[emuConfig.machineModel].displayOutput+1 >= NUMELEM(SHARPMZ_DISPLAY_OUTPUT) ? 0 : emuConfig.params[emuConfig.machineModel].displayOutput+1); + } + return; +} + +// Method to return a char string which represents the current selected VRAM Mode. +const char *EMZGetVRAMModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_VRAMDISABLE_MODE[emuConfig.params[emuConfig.machineModel].vramMode]); +} + +// Method to change the VRAM Mode, choice based on the actual selected machine. +void EMZNextVRAMMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].vramMode = (emuConfig.params[emuConfig.machineModel].vramMode+1 >= NUMELEM(SHARPMZ_VRAMDISABLE_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].vramMode+1); + } + return; +} + +// Method to return a char string which represents the current selected GRAM Mode. +const char *EMZGetGRAMModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_GRAMDISABLE_MODE[emuConfig.params[emuConfig.machineModel].gramMode]); +} + +// Method to change the GRAM Mode, choice based on the actual selected machine. +void EMZNextGRAMMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].gramMode = (emuConfig.params[emuConfig.machineModel].gramMode+1 >= NUMELEM(SHARPMZ_GRAMDISABLE_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].gramMode+1); + } + return; +} + +// Method to return a char string which represents the current selected VRAM CPU Wait Mode. +const char *EMZGetVRAMWaitModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_VRAMWAIT_MODE[emuConfig.params[emuConfig.machineModel].vramWaitMode]); +} + +// Method to change the VRAM Wait Mode, choice based on the actual selected machine. +void EMZNextVRAMWaitMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].vramWaitMode = (emuConfig.params[emuConfig.machineModel].vramWaitMode+1 >= NUMELEM(SHARPMZ_VRAMWAIT_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].vramWaitMode+1); + } + return; +} + +// Method to return a char string which represents the current selected PCG Mode. +const char *EMZGetPCGModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_PCG_MODE[emuConfig.params[emuConfig.machineModel].pcgMode]); +} + +// Method to change the PCG Mode, choice based on the actual selected machine. +void EMZNextPCGMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].pcgMode = (emuConfig.params[emuConfig.machineModel].pcgMode+1 >= NUMELEM(SHARPMZ_PCG_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].pcgMode+1); + } + return; +} + +// Method to return a char string which represents the current selected Aspect Ratio. +const char *EMZGetAspectRatioChoice(void) +{ + // Locals. + // + return(SHARPMZ_ASPECT_RATIO[emuConfig.params[emuConfig.machineModel].aspectRatio]); +} + +// Method to change the Aspect Ratio, choice based on the actual selected machine. +void EMZNextAspectRatio(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].aspectRatio = (emuConfig.params[emuConfig.machineModel].aspectRatio+1 >= NUMELEM(SHARPMZ_ASPECT_RATIO) ? 0 : emuConfig.params[emuConfig.machineModel].aspectRatio+1); + } + return; +} + +// Method to return a char string which represents the current selected Scan Doubler. +const char *EMZGetScanDoublerFXChoice(void) +{ + // Locals. + // + return(SHARPMZ_SCANDOUBLER_FX[emuConfig.params[emuConfig.machineModel].scanDoublerFX]); +} + +// Method to change the Scan Doubler FX, choice based on the actual selected machine. +void EMZNextScanDoublerFX(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].scanDoublerFX = (emuConfig.params[emuConfig.machineModel].scanDoublerFX+1 >= NUMELEM(SHARPMZ_SCANDOUBLER_FX) ? 0 : emuConfig.params[emuConfig.machineModel].scanDoublerFX+1); + } + return; +} + +// Method to return a char string which represents the current file filter. +const char *EMZGetLoadDirectFileFilterChoice(void) +{ + // Locals. + // + return(SHARPMZ_FILE_FILTERS[emuConfig.params[emuConfig.machineModel].loadDirectFilter]); +} + +// Method to change the Load Direct to RAM file filter, choice based on the actual selected machine. +void EMZNextLoadDirectFileFilter(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].loadDirectFilter = (emuConfig.params[emuConfig.machineModel].loadDirectFilter+1 >= NUMELEM(SHARPMZ_FILE_FILTERS) ? 0 : emuConfig.params[emuConfig.machineModel].loadDirectFilter+1); + } + return; +} + +// Method to return a char string which represents the current Tape Queueing file filter. +const char *EMZGetQueueTapeFileFilterChoice(void) +{ + // Locals. + // + return(SHARPMZ_FILE_FILTERS[emuConfig.params[emuConfig.machineModel].queueTapeFilter]); +} + +// Method to change the Queue Tape file filter, choice based on the actual selected machine. +void EMZNextQueueTapeFileFilter(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].queueTapeFilter = (emuConfig.params[emuConfig.machineModel].queueTapeFilter+1 >= NUMELEM(SHARPMZ_FILE_FILTERS) ? 0 : emuConfig.params[emuConfig.machineModel].queueTapeFilter+1); + } + return; +} + +// Method to return a char string which represents the current selected tape save path. +const char *EMZGetTapeSaveFilePathChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].tapeSavePath); +} + +// Method to return a char string which represents the current selected cmt hardware selection setting. +const char *EMZGetCMTModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_TAPE_MODE[emuConfig.params[emuConfig.machineModel].cmtMode]); +} + +// Method to change the cmt hardware setting, choice based on the actual selected machine. +void EMZNextCMTMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].cmtMode = (emuConfig.params[emuConfig.machineModel].cmtMode+1 >= NUMELEM(SHARPMZ_TAPE_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].cmtMode+1); + } + return; +} + +// Method to select the FPGA based CMT or the hardware CMT. +// +void EMZChangeCMTMode(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + // Need to change choice then rewrite the menu as the choice will affect displayed items. + EMZNextCMTMode(mode); + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + +// Method to return a char string which represents the current selected fast tape load mode. +const char *EMZGetFastTapeLoadChoice(void) +{ + // Locals. + // + + return(SHARPMZ_FAST_TAPE[emuConfig.machineGroup][emuConfig.params[emuConfig.machineModel].fastTapeLoad]); +} + +// Method to change the Fast Tape Load mode, choice based on the actual selected machine. +void EMZNextFastTapeLoad(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].fastTapeLoad = (emuConfig.params[emuConfig.machineModel].fastTapeLoad+1 >= NUMELEM(SHARPMZ_FAST_TAPE[emuConfig.machineGroup]) || SHARPMZ_FAST_TAPE[emuConfig.machineGroup][emuConfig.params[emuConfig.machineModel].fastTapeLoad+1] == NULL ? 0 : emuConfig.params[emuConfig.machineModel].fastTapeLoad+1); + } + return; +} + +// Method to return a char string which represents the current selected tape button setting. +const char *EMZGetTapeButtonsChoice(void) +{ + // Locals. + // + return(SHARPMZ_TAPE_BUTTONS[emuConfig.params[emuConfig.machineModel].tapeButtons]); +} + +// Method to change the tape button setting, choice based on the actual selected machine. +void EMZNextTapeButtons(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].tapeButtons = (emuConfig.params[emuConfig.machineModel].tapeButtons+1 >= NUMELEM(SHARPMZ_TAPE_BUTTONS) ? 0 : emuConfig.params[emuConfig.machineModel].tapeButtons+1); + } + return; +} + +// Method to return a char string which represents the current selected Sharp<->ASCII mapping for CMT operations. +const char *EMZGetCMTAsciiMappingChoice(void) +{ + // Locals. + // + return(SHARPMZ_ASCII_MAPPING[emuConfig.params[emuConfig.machineModel].cmtAsciiMapping]); +} + +// Method to change the Sharp<->ASCII mapping setting, choice based on the actual selected machine. +void EMZNextCMTAsciiMapping(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].cmtAsciiMapping = (emuConfig.params[emuConfig.machineModel].cmtAsciiMapping+1 >= NUMELEM(SHARPMZ_ASCII_MAPPING) ? 0 : emuConfig.params[emuConfig.machineModel].cmtAsciiMapping+1); + } + return; +} + +// Method to return a char string which represents the current selected floppy disk drive hardware selection setting. +const char *EMZGetFDDModeChoice(void) +{ + // Locals. + // + return(SHARPMZ_FDD_MODE[emuConfig.params[emuConfig.machineModel].fddEnabled]); +} + +// Method to change the floppy disk drive hardware setting, choice based on the actual selected machine. +void EMZNextFDDMode(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].fddEnabled = (emuConfig.params[emuConfig.machineModel].fddEnabled+1 >= NUMELEM(SHARPMZ_FDD_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].fddEnabled+1); + } + return; +} + +// Method to enable or disable the FDD hardware within the FPGA. +// +void EMZChangeFDDMode(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + // Need to change choice then rewrite the menu as the choice will affect displayed items. + EMZNextFDDMode(mode); + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + + +// Method to change the type of disk the WD1793 controller reports. Also used to aportion the disk image correctly. +// +void EMZNextFDDDriveType(enum ACTIONMODE mode, uint8_t drive) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + do { + emuConfig.params[emuConfig.machineModel].fdd[drive].diskType = (emuConfig.params[emuConfig.machineModel].fdd[drive].diskType+1 >= NUMELEM(SHARPMZ_FDD_DISK_TYPE) ? 0 : emuConfig.params[emuConfig.machineModel].fdd[drive].diskType+1); + } while(SHARPMZ_FDD_DISK_TYPE[emuConfig.params[emuConfig.machineModel].fdd[drive].diskType] == NULL); + } + return; +} +void EMZNextFDDDriveType0(enum ACTIONMODE mode) +{ + EMZNextFDDDriveType(mode, 0); +} +void EMZNextFDDDriveType1(enum ACTIONMODE mode) +{ + EMZNextFDDDriveType(mode, 1); +} +void EMZNextFDDDriveType2(enum ACTIONMODE mode) +{ + EMZNextFDDDriveType(mode, 2); +} +void EMZNextFDDDriveType3(enum ACTIONMODE mode) +{ + EMZNextFDDDriveType(mode, 3); +} + +// Method to return a string to indicate the current Disk Type setting. +const char *EMZGetFDDDriveTypeChoice(uint8_t drive) +{ + // Locals. + // + return(SHARPMZ_FDD_DISK_TYPE[emuConfig.params[emuConfig.machineModel].fdd[drive].diskType]); +} +const char *EMZGetFDDDriveType0Choice(void) +{ + return(EMZGetFDDDriveTypeChoice(0)); +} +const char *EMZGetFDDDriveType1Choice(void) +{ + return(EMZGetFDDDriveTypeChoice(1)); +} +const char *EMZGetFDDDriveType2Choice(void) +{ + return(EMZGetFDDDriveTypeChoice(2)); +} +const char *EMZGetFDDDriveType3Choice(void) +{ + return(EMZGetFDDDriveTypeChoice(3)); +} + +// Method to change the image polarity. The underlying controller expects inverted data due to the original MB8866 controller IC using an inverted data bus +// but it is hard without tools to work with or create new images. Thus an option exists to use a non-standard image which has non-inverted data and this +// option inverts it prior to sending to the controller. +// +void EMZNextFDDImagePolarity(enum ACTIONMODE mode, uint8_t drive) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].fdd[drive].polarity = (emuConfig.params[emuConfig.machineModel].fdd[drive].polarity+1 >= NUMELEM(SHARPMZ_FDD_IMAGE_POLARITY) ? 0 : emuConfig.params[emuConfig.machineModel].fdd[drive].polarity+1); + } + return; +} +void EMZNextFDDImagePolarity0(enum ACTIONMODE mode) +{ + EMZNextFDDImagePolarity(mode, 0); +} +void EMZNextFDDImagePolarity1(enum ACTIONMODE mode) +{ + EMZNextFDDImagePolarity(mode, 1); +} +void EMZNextFDDImagePolarity2(enum ACTIONMODE mode) +{ + EMZNextFDDImagePolarity(mode, 2); +} +void EMZNextFDDImagePolarity3(enum ACTIONMODE mode) +{ + EMZNextFDDImagePolarity(mode, 3); +} + +// Method to return a string to indicate the current Disk Polarity setting. +const char *EMZGetFDDImagePolarityChoice(uint8_t drive) +{ + // Locals. + // + return(SHARPMZ_FDD_IMAGE_POLARITY[emuConfig.params[emuConfig.machineModel].fdd[drive].polarity]); +} +const char *EMZGetFDDImagePolarity0Choice(void) +{ + return(EMZGetFDDImagePolarityChoice(0)); +} +const char *EMZGetFDDImagePolarity1Choice(void) +{ + return(EMZGetFDDImagePolarityChoice(1)); +} +const char *EMZGetFDDImagePolarity2Choice(void) +{ + return(EMZGetFDDImagePolarityChoice(2)); +} +const char *EMZGetFDDImagePolarity3Choice(void) +{ + return(EMZGetFDDImagePolarityChoice(3)); +} + +// Nethod to change the floppy disk update mode to enable/disable writes. +// +void EMZNextFDDUpdateMode(enum ACTIONMODE mode, uint8_t drive) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].fdd[drive].updateMode = (emuConfig.params[emuConfig.machineModel].fdd[drive].updateMode+1 >= NUMELEM(SHARPMZ_FDD_UPDATE_MODE) ? 0 : emuConfig.params[emuConfig.machineModel].fdd[drive].updateMode+1); + } + return; +} +void EMZNextFDDUpdateMode0(enum ACTIONMODE mode) +{ + EMZNextFDDUpdateMode(mode, 0); +} +void EMZNextFDDUpdateMode1(enum ACTIONMODE mode) +{ + EMZNextFDDUpdateMode(mode, 1); +} +void EMZNextFDDUpdateMode2(enum ACTIONMODE mode) +{ + EMZNextFDDUpdateMode(mode, 2); +} +void EMZNextFDDUpdateMode3(enum ACTIONMODE mode) +{ + EMZNextFDDUpdateMode(mode, 3); +} + +// Method to return a string to indicate the current Disk Update Mode setting. +const char *EMZGetFDDUpdateModeChoice(uint8_t drive) +{ + // Locals. + // + return(SHARPMZ_FDD_UPDATE_MODE[emuConfig.params[emuConfig.machineModel].fdd[drive].updateMode]); +} +const char *EMZGetFDDUpdateMode0Choice(void) +{ + return(EMZGetFDDUpdateModeChoice(0)); +} +const char *EMZGetFDDUpdateMode1Choice(void) +{ + return(EMZGetFDDUpdateModeChoice(1)); +} +const char *EMZGetFDDUpdateMode2Choice(void) +{ + return(EMZGetFDDUpdateModeChoice(2)); +} +const char *EMZGetFDDUpdateMode3Choice(void) +{ + return(EMZGetFDDUpdateModeChoice(3)); +} + +// Method to select the disk image to be used for a Floppy Disk Drive. +// +void EMZFDDSetDriveImage(enum ACTIONMODE mode, uint8_t drive) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, EMZGetFDDDriveFileFilterChoice()); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + switch(drive) + { + case 1: + emuControl.fileList.returnCallback = EMZFDDDriveImage1Set; + break; + case 2: + emuControl.fileList.returnCallback = EMZFDDDriveImage2Set; + break; + case 3: + emuControl.fileList.returnCallback = EMZFDDDriveImage3Set; + break; + case 0: + default: + emuControl.fileList.returnCallback = EMZFDDDriveImage0Set; + break; + } + } +} +void EMZFDDSetDriveImage0(enum ACTIONMODE mode) +{ + EMZFDDSetDriveImage(mode, 0); +} +void EMZFDDSetDriveImage1(enum ACTIONMODE mode) +{ + EMZFDDSetDriveImage(mode, 1); +} +void EMZFDDSetDriveImage2(enum ACTIONMODE mode) +{ + EMZFDDSetDriveImage(mode, 2); +} +void EMZFDDSetDriveImage3(enum ACTIONMODE mode) +{ + EMZFDDSetDriveImage(mode, 3); +} + +// Method to store the selected file name. This method is called as a callback when the user selects a disk image file. +void EMZFDDDriveImageSet(char *param, uint8_t driveNo) +{ + // Locals. + short imgType; + + // If a filename has been provided, check it and store details. + if(strlen(param) < MAX_FILENAME_LEN) + { + // Validate file selected. + if((imgType = EMZCheckFDDImage(param)) != -1) + { + // If the image is valid, store the information and set mounted flag. + if(EMZSetFDDImageParams(param, driveNo, (enum IMAGETYPES)imgType) != -1) + { + emuConfig.params[emuConfig.machineModel].fdd[driveNo].mounted = 1; + emuConfig.params[emuConfig.machineModel].fdd[driveNo].imgType = (enum IMAGETYPES)imgType; + } + } else + { + // Raise error message here. + } + } +} +void EMZFDDDriveImage0Set(char *param) +{ + EMZFDDDriveImageSet(param, 0); +} +void EMZFDDDriveImage1Set(char *param) +{ + EMZFDDDriveImageSet(param, 1); +} +void EMZFDDDriveImage2Set(char *param) +{ + EMZFDDDriveImageSet(param, 2); +} +void EMZFDDDriveImage3Set(char *param) +{ + EMZFDDDriveImageSet(param, 3); +} + +// Method to return a char string which represents the current floppy image file filter. +const char *EMZGetFDDDriveFileFilterChoice(void) +{ + // Locals. + // + return(SHARPMZ_FDD_FILE_FILTERS[emuConfig.params[emuConfig.machineModel].fddImageFilter]); +} + +// Method to return a char string which represents the current floppy image file filter. +const char *EMZGetFDDDriveFileChoice(uint8_t drive) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].fdd[drive].fileName); +} +const char *EMZGetFDDDrive0FileChoice(void) +{ + return(EMZGetFDDDriveFileChoice(0)); +} +const char *EMZGetFDDDrive1FileChoice(void) +{ + return(EMZGetFDDDriveFileChoice(1)); +} +const char *EMZGetFDDDrive2FileChoice(void) +{ + return(EMZGetFDDDriveFileChoice(2)); +} +const char *EMZGetFDDDrive3FileChoice(void) +{ + return(EMZGetFDDDriveFileChoice(3)); +} + +// Method to change the floppy image selection filter, choice based on the actual selected machine. +void EMZNextDriveImageFilter(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].fddImageFilter = (emuConfig.params[emuConfig.machineModel].fddImageFilter+1 >= NUMELEM(SHARPMZ_FDD_FILE_FILTERS) ? 0 : emuConfig.params[emuConfig.machineModel].fddImageFilter+1); + } + return; +} + +// Method to eject/unmount the current floppy image. +void EMZMountDrive(enum ACTIONMODE mode, uint8_t drive, uint8_t mount) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + // Only mount the disk image if a file has been selected. + emuConfig.params[emuConfig.machineModel].fdd[drive].mounted = mount == 1 && strlen(emuConfig.params[emuConfig.machineModel].fdd[drive].fileName) > 0 ? 1 : 0; + } + return; +} +void EMZNextMountDrive0(enum ACTIONMODE mode) +{ + EMZMountDrive(mode, 0, emuConfig.params[emuConfig.machineModel].fdd[0].mounted == 0 ? 1 : 0); +} +void EMZNextMountDrive1(enum ACTIONMODE mode) +{ + EMZMountDrive(mode, 1, emuConfig.params[emuConfig.machineModel].fdd[1].mounted == 0 ? 1 : 0); +} +void EMZNextMountDrive2(enum ACTIONMODE mode) +{ + EMZMountDrive(mode, 2, emuConfig.params[emuConfig.machineModel].fdd[2].mounted == 0 ? 1 : 0); +} +void EMZNextMountDrive3(enum ACTIONMODE mode) +{ + EMZMountDrive(mode, 3, emuConfig.params[emuConfig.machineModel].fdd[3].mounted == 0 ? 1 : 0); +} + +const char *EMZGetFDDMountChoice(uint8_t drive) +{ + // Locals. + // + return(SHARPMZ_FDD_MOUNT[emuConfig.params[emuConfig.machineModel].fdd[drive].mounted]); +} +const char *EMZGetFDDMount0Choice(void) +{ + // Locals. + // + return(EMZGetFDDMountChoice(0)); +} +const char *EMZGetFDDMount1Choice(void) +{ + // Locals. + // + return(EMZGetFDDMountChoice(1)); +} +const char *EMZGetFDDMount2Choice(void) +{ + // Locals. + // + return(EMZGetFDDMountChoice(2)); +} +const char *EMZGetFDDMount3Choice(void) +{ + // Locals. + // + return(EMZGetFDDMountChoice(3)); +} + +// Method to return a char string which represents the current selected 40x25 Monitor ROM setting. +const char *EMZGetMonitorROM40Choice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romMonitor40.romEnabled ? emuConfig.params[emuConfig.machineModel].romMonitor40.romFileName : "Disabled"); +} + +// Method to change the 40x25 Monitor ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextMonitorROM40(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romMonitor40.romEnabled = (emuConfig.params[emuConfig.machineModel].romMonitor40.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current selected 80x25 Monitor ROM setting. +const char *EMZGetMonitorROM80Choice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romMonitor80.romEnabled ? emuConfig.params[emuConfig.machineModel].romMonitor80.romFileName : "Disabled"); +} + +// Method to change the 80x25 Monitor ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextMonitorROM80(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romMonitor80.romEnabled = (emuConfig.params[emuConfig.machineModel].romMonitor80.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current selected character generator ROM setting. +const char *EMZGetCGROMChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romCG.romEnabled ? emuConfig.params[emuConfig.machineModel].romCG.romFileName : "Disabled"); +} + +// Method to change the character generator ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextCGROM(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romCG.romEnabled = (emuConfig.params[emuConfig.machineModel].romCG.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current selected key mapping ROM setting. +const char *EMZGetKeyMappingROMChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romKeyMap.romEnabled ? emuConfig.params[emuConfig.machineModel].romKeyMap.romFileName : "Disabled"); +} + +// Method to change the key mapping ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextKeyMappingROM(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romKeyMap.romEnabled = (emuConfig.params[emuConfig.machineModel].romKeyMap.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current selected User ROM setting. +const char *EMZGetUserROMChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romUser.romEnabled ? emuConfig.params[emuConfig.machineModel].romUser.romFileName : "Disabled"); +} + +// Method to change the User ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextUserROM(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romUser.romEnabled = (emuConfig.params[emuConfig.machineModel].romUser.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current selected Floppy Disk ROM setting. +const char *EMZGetFloppyDiskROMChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].romFDC.romEnabled ? emuConfig.params[emuConfig.machineModel].romFDC.romFileName : "Disabled"); +} + +// Method to change the Floppy Disk ROM setting, disabled or selected file, choice based on the actual selected machine. +void EMZNextFloppyDiskROM(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].romFDC.romEnabled = (emuConfig.params[emuConfig.machineModel].romFDC.romEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a string representation of the tape type held in the last tape accessed buffer. +const char *EMZGetTapeType(void) +{ + // Locals. + // + return(SHARPMZ_TAPE_TYPE[emuControl.tapeHeader.dataType >= NUMELEM(SHARPMZ_TAPE_TYPE) ? NUMELEM(SHARPMZ_TAPE_TYPE) - 1 : emuControl.tapeHeader.dataType]); +} + +// Method to return a char string which represents the current selected application autostart setting. +const char *EMZGetLoadApplicationChoice(void) +{ + // Locals. + // + return(emuConfig.params[emuConfig.machineModel].loadApp.appEnabled ? emuConfig.params[emuConfig.machineModel].loadApp.appFileName : "Disabled"); +} + +// Method to change the current select application autostart setting. +void EMZNextLoadApplication(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].loadApp.appEnabled = (emuConfig.params[emuConfig.machineModel].loadApp.appEnabled == 1 ? 0 : 1); + } + return; +} + +// Method to return a char string which represents the current state of the autostart feature. +const char *EMZGetAutoStartChoice(void) +{ + // Locals. + // + return(SHARPMZ_AUTOSTART[emuConfig.params[emuConfig.machineModel].autoStart]); +} + +// Method to change the start of the autostart feature, choice based on the actual selected machine. +void EMZNextAutoStart(enum ACTIONMODE mode) +{ + // Locals. + // + + if(mode == ACTION_DEFAULT || mode == ACTION_TOGGLECHOICE) + { + emuConfig.params[emuConfig.machineModel].autoStart = (emuConfig.params[emuConfig.machineModel].autoStart+1 >= NUMELEM(SHARPMZ_AUTOSTART) ? 0 : emuConfig.params[emuConfig.machineModel].autoStart+1); + } + return; +} + +// Method to select the autostart mode. +// +void EMZChangeAutoStart(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + // Need to change choice then rewrite the menu as the choice will affect displayed items. + EMZNextAutoStart(mode); + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + +// Method to add a line into the displayed menu. +// +void EMZAddToMenu(uint8_t row, uint8_t active, char *text, char hotKey, enum MENUTYPES type, enum MENUSTATE state, t_menuCallback mcb, enum MENUCALLBACK cbAction, t_choiceCallback ccb, t_viewCallback vcb) +{ + // Locals. + uint32_t textLen = strlen(text); + uint32_t idx; + + // Sanity check. + if(row >= MAX_MENU_ROWS) + return; + + if(emuControl.menu.data[row] != NULL) + { + free(emuControl.menu.data[row]); + emuControl.menu.data[row] = NULL; + } + emuControl.menu.data[row] = (t_menuItem *)malloc(sizeof(t_menuItem)); + if(emuControl.menu.data[row] == NULL) + { + debugf("Failed to allocate %d bytes on heap for menu row data %d\n", sizeof(t_menuItem), row); + return; + } + idx = textLen; + if(textLen > 0) + { + // Scan the text for a hotkey, case insensitive. + for(idx=0; idx < textLen; idx++) + { + if(text[idx] == hotKey) + break; + } + strcpy(emuControl.menu.data[row]->text, text); + } + else + { + emuControl.menu.data[row]->text[0] = 0x00; + } + // Store hotkey if given and found. + if(hotKey != 0x00 && (idx < textLen || state == MENUSTATE_HIDDEN)) + { + // Store the hotkey case independent. + emuControl.menu.data[row]->hotKey = hotKey; + } else + { + emuControl.menu.data[row]->hotKey = 0x00; + } + emuControl.menu.data[row]->type = type; + emuControl.menu.data[row]->state = state; + emuControl.menu.data[row]->menuCallback = mcb; + emuControl.menu.data[row]->choiceCallback = ccb; + emuControl.menu.data[row]->viewCallback = vcb; + emuControl.menu.data[row]->cbAction = cbAction; + + if(active && state == MENUSTATE_ACTIVE) + { + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = row; + } + return; +} + +// Method to get the boundaries of the current menu, ie. first item, last item and number of visible rows. +void EMZGetMenuBoundaries(int16_t *firstMenuRow, int16_t *lastMenuRow, int16_t *firstActiveRow, int16_t *lastActiveRow, int16_t *visibleRows) +{ + // Set defaults to indicate an invalid Menu structure. + *firstMenuRow = *lastMenuRow = *firstActiveRow = *lastActiveRow = -1; + *visibleRows = 0; + + // Npw scan the menu elements and work out first, last and number of visible rows. + for(int16_t idx=0; idx < MAX_MENU_ROWS; idx++) + { + if(emuControl.menu.data[idx] != NULL) + { + if(*firstMenuRow == -1) { *firstMenuRow = idx; } + *lastMenuRow = idx; + if(emuControl.menu.data[idx]->state != MENUSTATE_HIDDEN && emuControl.menu.data[idx]->state != MENUSTATE_INACTIVE) { *visibleRows += 1; } + if(emuControl.menu.data[idx]->state == MENUSTATE_ACTIVE && *firstActiveRow == -1) { *firstActiveRow = idx; } + if(emuControl.menu.data[idx]->state == MENUSTATE_ACTIVE) { *lastActiveRow = idx; } + } + } + return; +} + +// Method to update the framebuffer with current Menu contents and active line selection. +// The active row is used from the control structure when activeRow = -1 otherwise it is updated. +// +int16_t EMZDrawMenu(int16_t activeRow, uint8_t direction, enum MENUMODE mode) +{ + uint16_t xpad = 0; + uint16_t ypad = 1; + uint16_t rowPixelDepth = (emuControl.menu.rowFontptr->height + emuControl.menu.rowFontptr->spacing + emuControl.menu.padding + 2*ypad); + uint16_t maxCol = (uint16_t)OSDGet(ACTIVE_MAX_X); + uint16_t colPixelEnd = maxCol - emuControl.menu.colPixelsEnd; + uint16_t maxRow = ((uint16_t)OSDGet(ACTIVE_MAX_Y)/rowPixelDepth) + 1; + uint8_t textChrX = (emuControl.menu.colPixelStart / (emuControl.menu.rowFontptr->width + emuControl.menu.rowFontptr->spacing)); + char activeBuf[MENU_ROW_WIDTH]; + uint16_t attrBuf[MENU_ROW_WIDTH]; + int16_t firstMenuRow; + int16_t firstActiveMenuRow; + int16_t lastMenuRow; + int16_t lastActiveMenuRow; + int16_t visibleRows; + + // Get menu boundaries. + EMZGetMenuBoundaries(&firstMenuRow, &lastMenuRow, &firstActiveMenuRow, &lastActiveMenuRow, &visibleRows); + + // Sanity check. + if(firstMenuRow == -1 || lastMenuRow == -1 || firstActiveMenuRow == -1 || lastActiveMenuRow == -1 || visibleRows == 0) + return(activeRow); + + // Clear out old menu. + OSDClearArea(emuControl.menu.colPixelStart, emuControl.menu.rowPixelStart, colPixelEnd, ((uint16_t)OSDGet(ACTIVE_MAX_Y) - 2), emuControl.menu.inactiveBgColour); + + // Forward to the next active row if the one provided isnt active. + if(activeRow <= -1) + { + activeRow = (emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] < 0 || emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] >= MAX_MENU_ROWS ? 0 : emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx]); + } + + // Sanity check. + if(activeRow > MAX_MENU_ROWS-1) + activeRow = lastMenuRow; + + if(emuControl.menu.data[activeRow] == NULL || emuControl.menu.data[activeRow]->state != MENUSTATE_ACTIVE) + { + // Get the next or previous active menu item row. + uint16_t loopCheck = MAX_MENU_ROWS; + while((emuControl.menu.data[activeRow] == NULL || (emuControl.menu.data[activeRow] != NULL && emuControl.menu.data[activeRow]->state != MENUSTATE_ACTIVE)) && loopCheck > 0) + { + activeRow += (direction == 1 ? 1 : -1); + if(activeRow <= 0 && mode == MENU_NORMAL) activeRow = firstActiveMenuRow; + if(activeRow <= 0 && mode == MENU_WRAP) activeRow = lastActiveMenuRow; + if(activeRow >= MAX_MENU_ROWS && mode == MENU_NORMAL) activeRow = lastActiveMenuRow; + if(activeRow >= MAX_MENU_ROWS && mode == MENU_WRAP) activeRow = firstActiveMenuRow; + loopCheck--; + } + } + + // Loop through all the visible rows and output. + for(uint16_t dspRow=0, menuRow=activeRow < maxRow-1 ? 0 : activeRow - (maxRow-1); menuRow < MAX_MENU_ROWS; menuRow++) + { + // Skip inactive or hidden rows. + if(emuControl.menu.data[menuRow] == NULL) + continue; + if(emuControl.menu.data[menuRow]->state == MENUSTATE_HIDDEN || emuControl.menu.data[menuRow]->state == MENUSTATE_INACTIVE) + continue; + if(dspRow >= maxRow) + continue; + + // Skip rendering blank lines! + if(emuControl.menu.data[menuRow]->state != MENUSTATE_BLANK) + { + // Zero out attributes buffer. + memset(attrBuf, NOATTR, MENU_ROW_WIDTH*sizeof(uint16_t)); + + // For read only text, no choice or directory indicator required. + if(emuControl.menu.data[menuRow]->state == MENUSTATE_TEXT) + { + sprintf(activeBuf, " %-s", emuControl.menu.data[menuRow]->text); + } else + { + // Format the data into a single buffer for output according to type. The sprintf character limiter and left justify clash so manually cut the choice buffer to max length. + uint16_t selectionWidth = EMZGetMenuColumnWidth() - MENU_CHOICE_WIDTH - 2; + char *ptr; + sprintf(activeBuf, " %-*s", selectionWidth, emuControl.menu.data[menuRow]->text); + ptr=&activeBuf[strlen(activeBuf)]; + sprintf(ptr, "%-*s", MENU_CHOICE_WIDTH, (emuControl.menu.data[menuRow]->type & MENUTYPE_CHOICE && emuControl.menu.data[menuRow]->choiceCallback != NULL) ? emuControl.menu.data[menuRow]->choiceCallback() : ""); + sprintf(ptr+MENU_CHOICE_WIDTH, "%c", (emuControl.menu.data[menuRow]->type & MENUTYPE_SUBMENU) && !(emuControl.menu.data[menuRow]->type & MENUTYPE_ACTION) ? 0x10 : ' '); + + // Prepare any needed attributes. + for(uint16_t idx=0; idx < strlen(activeBuf); idx++) + { + // Highlight the Hot Key. + if(activeBuf[idx] == emuControl.menu.data[menuRow]->hotKey) + { + attrBuf[idx] = HILIGHT_FG_CYAN; + break; + } + } + } + + // Output the row according to type. + if(activeRow == menuRow) + { + OSDWriteString(textChrX, dspRow, 0, emuControl.menu.rowPixelStart, xpad, ypad, emuControl.menu.font, NORMAL, activeBuf, attrBuf, emuControl.menu.activeFgColour, emuControl.menu.activeBgColour); + if(activeRow != -1) + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = activeRow; + } + else if(emuControl.menu.data[menuRow]->state == MENUSTATE_GREYED) + { + OSDWriteString(textChrX, dspRow, 0, emuControl.menu.rowPixelStart, xpad, ypad, emuControl.menu.font, NORMAL, activeBuf, attrBuf, emuControl.menu.greyedFgColour, emuControl.menu.greyedBgColour); + } + else if(emuControl.menu.data[menuRow]->state == MENUSTATE_TEXT) + { + OSDWriteString(textChrX, dspRow, 0, emuControl.menu.rowPixelStart, xpad, ypad, emuControl.menu.font, NORMAL, activeBuf, attrBuf, emuControl.menu.textFgColour, emuControl.menu.textBgColour); + } + else + { + OSDWriteString(textChrX, dspRow, 0, emuControl.menu.rowPixelStart, xpad, ypad, emuControl.menu.font, NORMAL, activeBuf, attrBuf, emuControl.menu.inactiveFgColour, emuControl.menu.inactiveBgColour); + } + // Once the menu entry has been rendered, render view data if callback given. + if(emuControl.menu.data[menuRow]->viewCallback != NULL) + { + emuControl.menu.data[menuRow]->viewCallback(); + } + } + dspRow++; + } + + // If this is a submenu, place a back arrow to indicate you can go back. + if(emuControl.activeMenu.menuIdx != 0) + { + OSDWriteString(textChrX+1, 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "\x1b back", NULL, CYAN, BLACK); + } + // Place scroll arrows if we span a page. + if(activeRow >= maxRow && visibleRows > maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 71), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x17", NULL, CYAN, BLACK); + } else + if(activeRow >= maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 71), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x18 ", NULL, CYAN, BLACK); + } + else if(visibleRows > maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 71), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x19", NULL, CYAN, BLACK); + } else + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 71), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, " ", NULL, CYAN, BLACK); + } + + return(activeRow); +} + +// Method to free up menu heap allocated memory. +// +void EMZReleaseMenuMemory(void) +{ + // Loop through menu, free previous memory and initialise pointers. + // + for(uint16_t idx=0; idx < MAX_MENU_ROWS; idx++) + { + if(emuControl.menu.data[idx] != NULL) + { + free(emuControl.menu.data[idx]); + emuControl.menu.data[idx] = NULL; + } + } +} + +// Method to setup the intial menu screen and prepare for menu creation. +// +void EMZSetupMenu(char *sideTitle, char *menuTitle, enum FONTS font) +{ + // Locals. + // + fontStruct *fontptr = OSDGetFont(font); + uint16_t fontWidth = fontptr->width+fontptr->spacing; + uint16_t menuStartX = (((((uint16_t)OSDGet(ACTIVE_MAX_X) / fontWidth) - (30/fontWidth)) / 2) - strlen(menuTitle)/2) + 2; + uint16_t menuTitleLineLeft = (menuStartX * fontWidth) - 3; + uint16_t menuTitleLineRight = ((menuStartX+strlen(menuTitle))*fontWidth) + 1; + + // Release previous memory as creation of a new menu will reallocate according to requirements. + EMZReleaseMenuMemory(); + + // Prepare screen background. + OSDClearScreen(WHITE); + OSDClearArea(30, -1, -1, -1, BLACK); + + // Side and Top menu titles. + OSDWriteString(0, 0, 2, 8, 0, 0, FONT_9X16, DEG270, sideTitle, NULL, BLACK, WHITE); + OSDWriteString(menuStartX, 0, 0, 0, 0, 0, font, NORMAL, menuTitle, NULL, WHITE, BLACK); + + // Top line indenting menu title. + OSDDrawLine( 0, 0, menuTitleLineLeft, 0, WHITE); + OSDDrawLine( menuTitleLineLeft, 0, menuTitleLineLeft, fontWidth, WHITE); + OSDDrawLine( menuTitleLineLeft, fontWidth, menuTitleLineRight, fontWidth, WHITE); + OSDDrawLine( menuTitleLineRight, 0, menuTitleLineRight, fontWidth, WHITE); + OSDDrawLine( menuTitleLineRight, 0, -1, 0, WHITE); + + // Right and bottom lines to complete menu outline. + OSDDrawLine( 0, -1, -1, -1, WHITE); + OSDDrawLine(-1, 0, -1, -1, WHITE); + +// emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + return; +} + + +// Method to setup the OSD for output of a list of paths or files ready for selection. +void EMZSetupDirList(char *sideTitle, char *menuTitle, enum FONTS font) +{ + // Locals. + // + fontStruct *fontptr = OSDGetFont(font); + uint16_t fontWidth = fontptr->width+fontptr->spacing; + uint16_t menuStartX = (((((uint16_t)OSDGet(ACTIVE_MAX_X) / fontWidth) - (30/fontWidth)) / 2) - strlen(menuTitle)/2) + 1; + uint16_t menuTitleWidth = ((uint16_t)OSDGet(ACTIVE_MAX_X) / fontWidth) - (30/fontWidth); + uint16_t menuTitleLineLeft = (menuStartX * fontWidth) - 5; + uint16_t menuTitleLineRight = ((menuStartX+strlen(menuTitle))*fontWidth) + 3; + + // Prepare screen background. + OSDClearScreen(WHITE); + OSDClearArea(30, -1, -1, -1, BLACK); + + // Side and Top menu titles. + OSDWriteString(0, 0, 8, 8, 0, 0, FONT_9X16, DEG270, sideTitle, NULL, BLUE, WHITE); + // Adjust the title start location when it is larger than the display area. + OSDWriteString(menuStartX, 0, 0, 0, 0, 0, font, NORMAL, (strlen(menuTitle) >= menuTitleWidth - 2) ? &menuTitle[menuTitleWidth - strlen(menuTitle) - 2] : menuTitle, NULL, WHITE, BLACK); + + // Top line indenting menu title. + OSDDrawLine( 0, 0, menuTitleLineLeft, 0, WHITE); + OSDDrawLine( menuTitleLineLeft, 0, menuTitleLineLeft, fontWidth, WHITE); + OSDDrawLine( menuTitleLineLeft, fontWidth, menuTitleLineRight, fontWidth, WHITE); + OSDDrawLine( menuTitleLineRight, 0, menuTitleLineRight, fontWidth, WHITE); + OSDDrawLine( menuTitleLineRight, 0, -1, 0, WHITE); + + // Right and bottom lines to complete menu outline. + OSDDrawLine( 0, -1, -1, -1, WHITE); + OSDDrawLine(-1, 0, -1, -1, WHITE); + return; +} + +// Method to process a key event which is intended for the on-screen menu. +// +void EMZProcessMenuKey(uint8_t data, uint8_t ctrl) +{ + // Locals. + uint16_t menuRow = MAX_MENU_ROWS; + int16_t activeRow = emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx]; + + // Does the key match a hotkey? If it does, modify active row and action. + for(menuRow=0; menuRow < MAX_MENU_ROWS; menuRow++) + { + // Skip inactive rows. + if(emuControl.menu.data[menuRow] == NULL) + continue; + if(emuControl.menu.data[menuRow]->state != MENUSTATE_ACTIVE && emuControl.menu.data[menuRow]->state != MENUSTATE_HIDDEN) + continue; + // Key match? + if(toupper(emuControl.menu.data[menuRow]->hotKey) == toupper(data)) + break; + } + + // If key matched with a hotkey then modify action according to row type. + if(menuRow != MAX_MENU_ROWS) + { + // If the row isnt hidden, update the active row to the matched hotkey row. + if(emuControl.menu.data[menuRow]->state != MENUSTATE_HIDDEN) + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = menuRow; + + // Set the active row to the menu row, the active row is to invoke the callback functions. + activeRow = menuRow; + + // Update the action. + if(emuControl.menu.data[menuRow]->type & MENUTYPE_ACTION) + data = 0x0D; + else if(emuControl.menu.data[menuRow]->type & MENUTYPE_CHOICE) + data = ' '; + else if(emuControl.menu.data[menuRow]->type & MENUTYPE_SUBMENU) + data = 0xA3; + } + + // Process the data received. + switch(data) + { + // Up key. + case 0xA0: + if(emuControl.menu.data[emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx]] != NULL) + { + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = EMZDrawMenu(--emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 0, MENU_WRAP); + OSDRefreshScreen(); + } + break; + + // Down key. + case 0xA1: + if(emuControl.menu.data[emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx]] != NULL) + { + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = EMZDrawMenu(++emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 1, MENU_WRAP); + OSDRefreshScreen(); + } + break; + + // Left key. + case 0xA4: + if(emuControl.activeMenu.menuIdx != 0) + { + emuControl.activeMenu.menuIdx = emuControl.activeMenu.menuIdx-1; + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + break; + + // Toggle choice + case ' ': + if(emuControl.menu.data[activeRow] != NULL && emuControl.menu.data[activeRow]->type & MENUTYPE_CHOICE && emuControl.menu.data[activeRow]->menuCallback != NULL) + { + emuControl.menu.data[activeRow]->menuCallback(ACTION_TOGGLECHOICE); + if(emuControl.menu.data[activeRow]->cbAction == MENUCB_REFRESH) + { + EMZDrawMenu(emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 0, MENU_WRAP); + OSDRefreshScreen(); + } + } + break; + + // Carriage Return - action or select sub menu. + case 0x0D: + case 0xA3: + // Sub Menu select. + if(emuControl.menu.data[activeRow] != NULL && emuControl.menu.data[activeRow]->type & MENUTYPE_SUBMENU && emuControl.menu.data[activeRow]->menuCallback != NULL) + { + emuControl.activeMenu.menuIdx = emuControl.activeMenu.menuIdx >= MAX_MENU_DEPTH - 1 ? MAX_MENU_DEPTH - 1 : emuControl.activeMenu.menuIdx+1; + emuControl.menu.data[emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx-1]]->menuCallback(ACTION_SELECT); + } else + // Action select. + if(data == 0x0D && emuControl.menu.data[activeRow] != NULL) + { + if(emuControl.menu.data[activeRow]->menuCallback != NULL) + emuControl.menu.data[activeRow]->menuCallback(ACTION_SELECT); + if(emuControl.menu.data[activeRow]->cbAction == MENUCB_REFRESH) + { + EMZDrawMenu(emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 0, MENU_WRAP); + OSDRefreshScreen(); + } + } + break; + + default: + printf("%02x",data); + break; + } + return; +} + +// Method to release all memory used by the on screen file/path selection screen. +// +void EMZReleaseDirMemory(void) +{ + // Free up memory used by the directory name buffer from previous use prior to allocating new buffers. + // + for(uint16_t idx=0; idx < NUMELEM(emuControl.fileList.dirEntries); idx++) + { + if(emuControl.fileList.dirEntries[idx].name != NULL) + { + free(emuControl.fileList.dirEntries[idx].name); + emuControl.fileList.dirEntries[idx].name = NULL; + } + } +} + +// Method to cache a directory contents with suitable filters in order to present a perusable list to the user via the OSD. +// +uint8_t EMZReadDirectory(const char *path, const char *filter) +{ + // Locals. + uint16_t dirCnt = 0; + uint8_t result = 0; + static DIR dirFp; + static FILINFO fno; + + // Release any previous memory. + // + EMZReleaseDirMemory(); + + // Read and process the directory contents. + result = f_opendir(&dirFp, (char *)path); + if(result == FR_OK) + { + while(dirCnt < MAX_DIRENTRY) + { + result = f_readdir(&dirFp, &fno); + if(result != FR_OK || fno.fname[0] == 0) break; + + // Filter out none-files. + if(strlen(fno.fname) == 0) + continue; + + // Filter out all files if we are just selecting directories. + if(!(fno.fattrib & AM_DIR) && strcmp(fno.fname, ".") == 0) + continue; + + // Filter out files not relevant to the caller based on the provided filter. + const char *ext = strrchr(fno.fname, '.'); + const char *filterExt = strrchr(filter, '.'); +printf("ext=%s, filterExt=%s, filter=%s, file=%s\n", ext, filterExt, filter, fno.fname); +//FIX ME: Doesnt process wildcard filename +// ext=.dsk, filterExt=.*, filter=*.*, file=z80bcpm.dsk + // Is a File Is not a Wildcard Filter doesnt match the extension + if(!(fno.fattrib & AM_DIR) && !(filterExt != NULL && strcmp(filterExt, "\.\*") == 0) && (ext == NULL || strcasecmp(++ext, (filterExt == NULL ? filter : ++filterExt)) != 0)) + continue; + + // Filter out hidden directories. + if((fno.fattrib & AM_DIR) && fno.fname[0] == '.') + continue; + + // Allocate memory for the filename and fill in the required information. + if((emuControl.fileList.dirEntries[dirCnt].name = (char *)malloc(strlen(fno.fname)+1)) == NULL) + { + printf("Memory exhausted, aborting!\n"); + return(1); + } + strcpy(emuControl.fileList.dirEntries[dirCnt].name, fno.fname); + emuControl.fileList.dirEntries[dirCnt].isDir = fno.fattrib & AM_DIR ? 1 : 0; + dirCnt++; + } + + // Pre sort the list so it is alphabetic to speed up short key indexing. + // + uint16_t idx, idx2, idx3; + for(idx=0; idx < MAX_DIRENTRY; idx++) + { + if(emuControl.fileList.dirEntries[idx].name == NULL) + continue; + + for(idx2=0; idx2 < MAX_DIRENTRY; idx2++) + { + if(emuControl.fileList.dirEntries[idx2].name == NULL) + continue; + + // Locate the next slot just in case the list is fragmented. + for(idx3=idx2+1; idx3 < MAX_DIRENTRY && emuControl.fileList.dirEntries[idx3].name == NULL; idx3++); + if(idx3 == MAX_DIRENTRY) + break; + + // Compare the 2 closest strings and swap if needed. Priority to directories as they need to appear at the top of the list. + if( (!emuControl.fileList.dirEntries[idx2].isDir && emuControl.fileList.dirEntries[idx3].isDir) || + (((emuControl.fileList.dirEntries[idx2].isDir && emuControl.fileList.dirEntries[idx3].isDir) || (!emuControl.fileList.dirEntries[idx2].isDir && !emuControl.fileList.dirEntries[idx3].isDir)) && strcasecmp(emuControl.fileList.dirEntries[idx2].name, emuControl.fileList.dirEntries[idx3].name) > 0) ) + { + t_dirEntry temp = emuControl.fileList.dirEntries[idx2]; + emuControl.fileList.dirEntries[idx2].name = emuControl.fileList.dirEntries[idx3].name; + emuControl.fileList.dirEntries[idx2].isDir = emuControl.fileList.dirEntries[idx3].isDir; + emuControl.fileList.dirEntries[idx3].name = temp.name; + emuControl.fileList.dirEntries[idx3].isDir = temp.isDir; + } + } + } + } + if(dirCnt == 0 && result != FR_OK) + f_closedir(&dirFp); + return(result); +} + +// Method to get the boundaries of the displayed file list screen, ie. first item, last item and number of visible rows. +void EMZGetFileListBoundaries(int16_t *firstFileListRow, int16_t *lastFileListRow, int16_t *visibleRows) +{ + // Set defaults to indicate an invalid Menu structure. + *firstFileListRow = *lastFileListRow = -1; + *visibleRows = 0; + + // Npw scan the file list elements and work out first, last and number of visible rows. + for(uint16_t idx=0; idx < MAX_DIRENTRY; idx++) + { + if(emuControl.fileList.dirEntries[idx].name != NULL && *firstFileListRow == -1) { *firstFileListRow = idx; } + if(emuControl.fileList.dirEntries[idx].name != NULL) { *lastFileListRow = idx; } + if(emuControl.fileList.dirEntries[idx].name != NULL) { *visibleRows += 1; } + } + return; +} + +// Method to get the maximum number of columns available for a file row with the current selected font. +// +uint16_t EMZGetFileListColumnWidth(void) +{ + uint16_t maxPixels = OSDGet(ACTIVE_MAX_X); + return( (maxPixels - emuControl.fileList.colPixelStart - emuControl.fileList.colPixelsEnd) / (emuControl.fileList.rowFontptr->width + emuControl.fileList.rowFontptr->spacing) ); +} + + +// Method to take a cached list of directory entries and present them to the user via the OSD. The current row is highlighted and allows for scrolling up or down within the list. +// +int16_t EMZDrawFileList(int16_t activeRow, uint8_t direction) +{ + uint8_t xpad = 0; + uint8_t ypad = 1; + uint16_t rowPixelDepth = (emuControl.fileList.rowFontptr->height + emuControl.fileList.rowFontptr->spacing + emuControl.fileList.padding + 2*ypad); + uint16_t maxCol = (uint16_t)OSDGet(ACTIVE_MAX_X); + uint16_t colPixelEnd = maxCol - emuControl.fileList.colPixelsEnd; + uint16_t maxRow = ((uint16_t)OSDGet(ACTIVE_MAX_Y)/rowPixelDepth) + 1; + uint8_t textChrX = (emuControl.fileList.colPixelStart / (emuControl.fileList.rowFontptr->width + emuControl.fileList.rowFontptr->spacing)); + char activeBuf[MENU_ROW_WIDTH]; + int16_t firstFileListRow; + int16_t lastFileListRow; + int16_t visibleRows; + + // Get file list boundaries. + EMZGetFileListBoundaries(&firstFileListRow, &lastFileListRow, &visibleRows); + + // Clear out old file list. + OSDClearArea(emuControl.fileList.colPixelStart, emuControl.fileList.rowPixelStart, colPixelEnd, ((uint16_t)OSDGet(ACTIVE_MAX_Y) - 2), emuControl.fileList.inactiveBgColour); + + // If this is a sub directory, place a back arrow to indicate you can go back. + if(emuControl.activeDir.dirIdx != 0) + { + OSDWriteString(textChrX, 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "\x1b back", NULL, CYAN, BLACK); + } + // Place scroll arrows if we span a page. + if(activeRow >= maxRow && visibleRows > maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 70), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x17", NULL, CYAN, BLACK); + } else + if(activeRow >= maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 70), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x18 ",NULL, CYAN, BLACK); + } + else if(visibleRows > maxRow) + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 70), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, "scroll \x19", NULL, CYAN, BLACK); + } else + { + OSDWriteString(textChrX+(maxCol < 512 ? 38 : 70), 0, 0, 4, 0, 0, FONT_5X7, NORMAL, " ", NULL, CYAN, BLACK); + } + + // Sanity check, no files or parameters out of range just exit as though the directory is empty. + if(firstFileListRow == -1 || lastFileListRow == -1 || visibleRows == 0) + return(activeRow); + + // Forward to the next active row if the one provided isnt active. + if(activeRow <= -1) + { + activeRow = (emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] < 0 || emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] >= MAX_DIRENTRY ? 0 : emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]); + } + // Sanity check. + if(activeRow > MAX_DIRENTRY-1) + activeRow = lastFileListRow; + if(emuControl.fileList.dirEntries[activeRow].name == NULL) + { + // Get the next or previous active file list row. + uint16_t loopCheck = MAX_DIRENTRY; + while(emuControl.fileList.dirEntries[activeRow].name == NULL && loopCheck > 0) + { + activeRow += (direction == 1 ? 1 : -1); + if(activeRow < 0) activeRow = 0; + if(activeRow >= MAX_DIRENTRY) activeRow = MAX_DIRENTRY-1; + loopCheck--; + } + if(activeRow == 0 || activeRow == MAX_DIRENTRY-1) activeRow = firstFileListRow; + if(activeRow == 0 || activeRow == MAX_DIRENTRY-1) activeRow = lastFileListRow; + } + + // Loop through all the visible rows and output. + for(uint16_t dspRow=0, fileRow=activeRow < maxRow-1 ? 0 : activeRow - (maxRow-1); fileRow < MAX_DIRENTRY; fileRow++) + { + // Skip inactive or hidden rows. + if(emuControl.fileList.dirEntries[fileRow].name == NULL) + continue; + if(dspRow >= maxRow) + continue; + + // Format the data into a single buffer for output. + uint16_t selectionWidth = EMZGetFileListColumnWidth() - 9; + uint16_t nameStart = strlen(emuControl.fileList.dirEntries[fileRow].name) > selectionWidth ? strlen(emuControl.fileList.dirEntries[fileRow].name) - selectionWidth : 0; + sprintf(activeBuf, " %-*s%-7s ", selectionWidth, &emuControl.fileList.dirEntries[fileRow].name[nameStart], (emuControl.fileList.dirEntries[fileRow].isDir == 1) ? " \x10" : ""); + + // Finall output the row according to selection. + if(activeRow == fileRow) + { + OSDWriteString(textChrX, dspRow, 0, emuControl.fileList.rowPixelStart, xpad, ypad, emuControl.fileList.font, NORMAL, activeBuf, NULL, emuControl.fileList.activeFgColour, emuControl.fileList.activeBgColour); + if(activeRow != -1) + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = activeRow; + } + else + { + OSDWriteString(textChrX, dspRow, 0, emuControl.fileList.rowPixelStart, xpad, ypad, emuControl.fileList.font, NORMAL, activeBuf, NULL, emuControl.fileList.inactiveFgColour, emuControl.fileList.inactiveBgColour); + } + dspRow++; + } + + return(activeRow); +} + +void EMZGetFile(void) +{ + +} + +// Method to process a key event according to current state. If displaying a list of files for selection then this method is called to allow user interaction with the file +// list and ultimate selection. +// +void EMZProcessFileListKey(uint8_t data, uint8_t ctrl) +{ + // Locals. + // + char tmpbuf[MAX_FILENAME_LEN+1]; + uint16_t rowPixelDepth = (emuControl.fileList.rowFontptr->height + emuControl.fileList.rowFontptr->spacing + emuControl.fileList.padding + 2); + uint16_t maxRow = ((uint16_t)OSDGet(ACTIVE_MAX_Y)/rowPixelDepth) + 1; + + // data = ascii code/ctrl code for key pressed. + // ctrl = KEY_BREAK & KEY_CTRL & KEY_SHIFT & "000" & KEY_DOWN & KEY_UP + + // If the break key is pressed, this is equivalent to escape so exit file list selection. + if(ctrl & KEY_BREAK_BIT) + { + // Just switch back to active menu dont activate the callback for storing a selection. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } else + { + // Process according to pressed key. + // + switch(data) + { + // Short keys to index into the file list. + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + for(int16_t idx=0; idx < MAX_DIRENTRY; idx++) + { + if(emuControl.fileList.dirEntries[idx].name == NULL) + continue; + + // If the key pressed is the first letter of a filename then jump to it. + if((!emuControl.fileList.dirEntries[idx].isDir && emuControl.fileList.dirEntries[idx].name[0] == tolower(data)) || emuControl.fileList.dirEntries[idx].name[0] == toupper(data)) + { + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = idx; + EMZDrawFileList(emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx], 0); + OSDRefreshScreen(); + break; + } + } + break; + + // Up key. + case 0xA0: + // Shift UP pressed? + if(ctrl & KEY_SHIFT_BIT) + { + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] - maxRow -1 > 0 ? emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] - maxRow -1 : 0; + } + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = EMZDrawFileList(--emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx], 0); + OSDRefreshScreen(); + break; + + // Down key. + case 0xA1: + // Shift Down pressed? + if(ctrl & KEY_SHIFT_BIT) + { + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] + maxRow -1 > 0 ? emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] + maxRow -1 : MAX_DIRENTRY-1; + } + emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx] = EMZDrawFileList(++emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx], 1); + OSDRefreshScreen(); + break; + + // Left key. + case 0xA4: + if(emuControl.activeDir.dirIdx != 0) + { + emuControl.activeDir.dirIdx = emuControl.activeDir.dirIdx-1; + + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZDrawFileList(0, 1); + OSDRefreshScreen(); + } + break; + + // Carriage Return - action or select sub directory. + case 0x0D: + case 0xA3: // Right Key + if(emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].name != NULL) + { + // If selection is chosen by CR on a path, execute the return callback to process the path and return control to the menu system. + // + if(data == 0x0D && emuControl.fileList.selectDir && emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].isDir && emuControl.fileList.returnCallback != NULL) + { + sprintf(tmpbuf, "%s\%s", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].name); + emuControl.fileList.returnCallback(tmpbuf); + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + // If selection is on a directory, increase the menu depth, read the directory and refresh the file list. + // + else if(emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].isDir && emuControl.activeDir.dirIdx+1 < MAX_DIR_DEPTH) + { + emuControl.activeDir.dirIdx++; + if(emuControl.activeDir.dir[emuControl.activeDir.dirIdx] != NULL) + { + free(emuControl.activeDir.dir[emuControl.activeDir.dirIdx]); + emuControl.activeDir.dir[emuControl.activeDir.dirIdx] = NULL; + } + if(emuControl.activeDir.dirIdx == 1) + { + sprintf(tmpbuf, "0:\\%s", emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx-1]].name); + } + else + { + sprintf(tmpbuf, "%s\\%s", emuControl.activeDir.dir[emuControl.activeDir.dirIdx-1], emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx-1]].name); + } + if((emuControl.activeDir.dir[emuControl.activeDir.dirIdx] = (char *)malloc(strlen(tmpbuf)+1)) == NULL) + { + printf("Exhausted memory allocating file directory name buffer\n"); + return; + } + strcpy(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], tmpbuf); + // Bring up the OSD. + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + + // Check the directory, if it is valid and has contents then update the OSD otherwise wind back. + if(EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter) == 0) + { + EMZDrawFileList(0, 1); + OSDRefreshScreen(); + } else + { + free(emuControl.activeDir.dir[emuControl.activeDir.dirIdx]); + emuControl.activeDir.dir[emuControl.activeDir.dirIdx] = NULL; + emuControl.activeDir.dirIdx--; + } + } + // If the selection is on a file, execute the return callback to process the file and return control to the menu system. + else if(emuControl.fileList.returnCallback != NULL && !emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].isDir) + { + sprintf(tmpbuf, "%s\\%s", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.dirEntries[emuControl.activeDir.activeRow[emuControl.activeDir.dirIdx]].name); + emuControl.fileList.returnCallback(tmpbuf); + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + } + break; + + default: + printf("%02x",data); + break; + } + } + return; +} + +// Method to redraw/refresh the menu screen. +void EMZRefreshMenu(void) +{ + EMZDrawMenu(emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 0, MENU_WRAP); + OSDRefreshScreen(); +} + +// Method to redraw/refresh the file list. +void EMZRefreshFileList(void) +{ + EMZDrawFileList(emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx], 0); + OSDRefreshScreen(); +} + +// Initial method called to select a file which will be loaded directly into the emulation RAM. +// +void EMZLoadDirectToRAM(enum ACTIONMODE mode) +{ + // Locals. + // + + // Toggle choice? + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextLoadDirectFileFilter(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, EMZGetLoadDirectFileFilterChoice()); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + for(uint16_t idx=0; idx < MAX_DIRENTRY; idx++) + { + if(emuControl.fileList.dirEntries[idx].name != NULL) + { + printf("%-40s%s\n", emuControl.fileList.dirEntries[idx].name, emuControl.fileList.dirEntries[idx].isDir == 1 ? "" : ""); + } + } + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZLoadDirectToRAMSet; + } + return; +} + +// Method to print out the details of the last processed tape. +// +void EMZPrintTapeDetails(short errCode) +{ + // Locals. + // + uint8_t textChrX = (emuControl.menu.colPixelStart / (emuControl.menu.rowFontptr->width + emuControl.menu.rowFontptr->spacing)); + char strBuf[MENU_ROW_WIDTH]; + + // Use the menu framework to draw the borders and title but write the details directly. + // + if(errCode) + EMZSetupMenu(EMZGetMachineTitle(), "Tape Error", FONT_7X8); + else + EMZSetupMenu(EMZGetMachineTitle(), "Tape Details", FONT_7X8); + + sprintf(strBuf, "File Size: %04x", emuControl.tapeHeader.fileSize); + OSDWriteString(18, 4, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, WHITE, BLACK); + sprintf(strBuf, "File Type: %s", EMZGetTapeType()); + OSDWriteString(18, 5, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, WHITE, BLACK); + sprintf(strBuf, "File Name: %s", emuControl.tapeHeader.fileName); + OSDWriteString(18, 6, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, WHITE, BLACK); + sprintf(strBuf, "Load Addr: %04x", emuControl.tapeHeader.loadAddress); + OSDWriteString(18, 7, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, WHITE, BLACK); + sprintf(strBuf, "Exec Addr: %04x", emuControl.tapeHeader.execAddress); + OSDWriteString(18, 8, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, WHITE, BLACK); + + if(errCode > 0 && errCode < 0x20) + { + sprintf(strBuf, "FAT FileSystem error code: %02x", errCode); + } + else if(errCode == 0x20) + { + sprintf(strBuf, "File header contains insufficient bytes."); + } + else if(errCode == 0x21) + { + sprintf(strBuf, "Tape Data Type is invalid: %02x", emuControl.tapeHeader.dataType); + } + else if(errCode == 0x22) + { + sprintf(strBuf, "Tape is not machine code, cannot load to RAM directly."); + } + else if(errCode == 0x23 || errCode == 0x24) + { + sprintf(strBuf, "File read error. directly."); + } + else + { + sprintf(strBuf, "Unknown error (%02x) processing tape file.", errCode); + } + if(errCode > 0) + { + OSDWriteString(((VC_MENU_MAX_X_PIXELS/7) - 4 - strlen(strBuf))/2, 12, 0, 2, 0, 0, FONT_7X8, NORMAL, strBuf, NULL, RED, BLACK); + } + EMZRefreshMenu(); + + return; +} + +// Secondary method called after a file has been chosen. +// +void EMZLoadDirectToRAMSet(char *fileName) +{ + // Locals. + // + short errCode; + + // Simply call tape load with the RAM option to complete request. + errCode = EMZLoadTapeToRAM(fileName, 0); + + // Print out the tape details. + EMZPrintTapeDetails(errCode); + + // Slight delay to give user chance to see the details. + delay(8000); + + return; +} + +// Method to push a tape filename onto the queue. +// +void EMZTapeQueuePushFile(char *fileName) +{ + // Locals. + char *ptr = (char *)malloc(strlen(fileName)+1); + + if(emuControl.tapeQueue.elements > MAX_TAPE_QUEUE) + { + free(ptr); + } else + { + // Copy filename into queue. + strcpy(ptr, fileName); + emuControl.tapeQueue.queue[emuControl.tapeQueue.elements] = ptr; + emuControl.tapeQueue.elements++; + } + + return; +} + +// Method to read the oldest tape filename entered and return it. +// +char *EMZTapeQueuePopFile(uint8_t popFile) +{ + // Pop the bottom most item and shift queue down. + // + emuControl.tapeQueue.fileName[0] = 0; + if(emuControl.tapeQueue.elements > 0) + { + strcpy(emuControl.tapeQueue.fileName, emuControl.tapeQueue.queue[0]); + + // Pop file off queue? + if(popFile) + { + free(emuControl.tapeQueue.queue[0]); + emuControl.tapeQueue.elements--; + for(int i= 1; i < MAX_TAPE_QUEUE; i++) + { + emuControl.tapeQueue.queue[i-1] = emuControl.tapeQueue.queue[i]; + } + emuControl.tapeQueue.queue[MAX_TAPE_QUEUE-1] = NULL; + } + } + + // Return filename. + return(emuControl.tapeQueue.fileName[0] == 0 ? 0 : emuControl.tapeQueue.fileName); +} + +// Method to virtualise a tape and shift the position up and down the queue according to actions given. +// direction: 0 = rotate left (Rew), 1 = rotate right (Fwd) +// update: 0 = dont update tape position, 1 = update tape position +// +char *EMZTapeQueueAPSSSearch(char direction, uint8_t update) +{ + emuControl.tapeQueue.fileName[0] = 0; + if(emuControl.tapeQueue.elements > 0) + { + if(direction == 0) + { +printf("tapePos REW enter:%d,Max:%d\n", emuControl.tapeQueue.tapePos, emuControl.tapeQueue.elements); + // Position is ahead of last, then shift down and return file. + // + if(emuControl.tapeQueue.tapePos > 0) + { + strcpy(emuControl.tapeQueue.fileName, emuControl.tapeQueue.queue[emuControl.tapeQueue.tapePos-1]); + if(update) emuControl.tapeQueue.tapePos--; +printf("tapePos REW exit:%d,Max:%d\n", emuControl.tapeQueue.tapePos, emuControl.tapeQueue.elements); + } + + } else + { +printf("tapePos FFWD enter:%d,Max:%d\n", emuControl.tapeQueue.tapePos, emuControl.tapeQueue.elements); + // Position is below max, then return current and forward. + // + if(emuControl.tapeQueue.tapePos < MAX_TAPE_QUEUE && emuControl.tapeQueue.tapePos < emuControl.tapeQueue.elements) + { + strcpy(emuControl.tapeQueue.fileName, emuControl.tapeQueue.queue[emuControl.tapeQueue.tapePos]); + if(update) emuControl.tapeQueue.tapePos++; +printf("tapePos FFWD exit:%d,Max:%d\n", emuControl.tapeQueue.tapePos, emuControl.tapeQueue.elements); + } + } + } + + // Return filename. + return(emuControl.tapeQueue.fileName[0] == 0 ? 0 : emuControl.tapeQueue.fileName); +} + + +// Method to iterate through the list of filenames. +// +char *EMZNextTapeQueueFilename(char reset) +{ + static unsigned short pos = 0; + + // Reset is active, start at beginning of list. + // + if(reset) + { + pos = 0; + } + emuControl.tapeQueue.fileName[0] = 0x00; + + // If we reach the queue limit or the max elements stored, cycle the pointer + // round to the beginning. + // + if(pos >= MAX_TAPE_QUEUE || pos >= emuControl.tapeQueue.elements) + { + pos = 0; + } else + + // Get the next element in the queue, if available. + // + if(emuControl.tapeQueue.elements > 0) + { + if(pos < MAX_TAPE_QUEUE && pos < emuControl.tapeQueue.elements) + { + strcpy(emuControl.tapeQueue.fileName, emuControl.tapeQueue.queue[pos++]); + } + } + // Return filename if available. + // + return(emuControl.tapeQueue.fileName[0] == 0x00 ? NULL : &emuControl.tapeQueue.fileName); +} + +// Method to clear the queued tape list. +// +uint16_t EMZClearTapeQueue(void) +{ + // Locals. + uint16_t entries = emuControl.tapeQueue.elements; + + // Clear the queue through iteration, freeing allocated memory per entry. + if(emuControl.tapeQueue.elements > 0) + { + for(int i=0; i < MAX_TAPE_QUEUE; i++) + { + if(emuControl.tapeQueue.queue[i] != NULL) + { + free(emuControl.tapeQueue.queue[i]); + } + emuControl.tapeQueue.queue[i] = NULL; + } + } + // Reset control variables. + emuControl.tapeQueue.elements = 0; + emuControl.tapeQueue.tapePos = 0; + emuControl.tapeQueue.fileName[0] = 0; + + // Finally, return number of entries in the queue which have been cleared out. + return(entries); +} + +// Method to select a tape file which is added to the tape queue. The tape queue is a virtual tape where +// each file is presented to the CMT hardware sequentially or in a loop for the more advanced APSS decks +// of the MZ80B/2000. +void EMZQueueTape(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextQueueTapeFileFilter(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, EMZGetQueueTapeFileFilterChoice()); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZQueueTapeSet; + } +} + +// Method to store the selected file into the tape queue. +void EMZQueueTapeSet(char *param) +{ + EMZTapeQueuePushFile(param); +} + +// Method to set the active queue entry to the next entry. +void EMZQueueNext(enum ACTIONMODE mode) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Fast forward to next tape entry. + EMZTapeQueueAPSSSearch(1, 1); + + // Need to redraw the menu as read only text has changed. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + +// Method to set the active queue entry to the previous entry. +void EMZQueuePrev(enum ACTIONMODE mode) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Fast forward to next tape entry. + EMZTapeQueueAPSSSearch(0, 1); + + // Need to redraw the menu as read only text has changed. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + +// Simple wrapper method to clear the tape queue. +void EMZQueueClear(enum ACTIONMODE mode) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + uint16_t deletedEntries = EMZClearTapeQueue(); + + // Update the active row to account for number of entries deleted. + if(emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] - deletedEntries > 0) + emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] -= deletedEntries; + + // Need to redraw the menu as read only text has changed. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } +} + +// Method to select a path where tape images will be saved. +void EMZTapeSave(enum ACTIONMODE mode) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Prompt to enter the path in which incoming files will be saved. + EMZSetupDirList("Select Path", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "."); + emuControl.fileList.selectDir = 1; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZTapeSaveSet; + } +} + +// Method to save the entered directory path in the configuration. +void EMZTapeSaveSet(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + strcpy(emuConfig.params[emuConfig.machineModel].tapeSavePath, param); + emuControl.fileList.selectDir = 0; +} + +// Method to reset the emulation logic by issuing an MCTRL command. +void EMZReset(void) +{ + // Initiate a machine RESET via the control registers. + // + emuConfig.emuRegisters[MZ_EMU_REG_CTRL] |= 0x01; + + // Write the control register to initiate RESET command. + writeZ80Array(MZ_EMU_ADDR_REG_MODEL+emuConfig.emuRegisters[MZ_EMU_REG_CTRL], &emuConfig.emuRegisters[emuConfig.emuRegisters[MZ_EMU_REG_CTRL]], 1, FPGA); + + // Clear any reset command from the internal register copy so the command isnt accidentally sent again. + emuConfig.emuRegisters[MZ_EMU_REG_CTRL] &= 0xFE; + +} + +// Method to reset the machine. This can be done via the hardware, load up the configuration and include the machine definition and the hardware will force a reset. +// +void EMZResetMachine(enum ACTIONMODE mode) +{ + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Force a reload of the machine ROM's and reset. + EMZSwitchToMachine(emuConfig.machineModel, 1); + + // Refresh menu to update cursor. + EMZRefreshMenu(); + } +} + +// Method to read in the header of an MZF file and populate the tapeHeader structure. +// +short EMZReadTapeDetails(const char *tapeFile) +{ + // Locals. + // + char loadName[MAX_FILENAME_LEN+1]; + uint32_t actualReadSize; + FIL fileDesc; + FRESULT result; + + // If a relative path has been given we need to expand it into an absolute path. + if(tapeFile[0] != '/' && tapeFile[0] != '\\' && (tapeFile[0] < 0x30 || tapeFile[0] > 0x32)) + { + sprintf(loadName, "%s\%s", TOPLEVEL_DIR, tapeFile); + } else + { + strcpy(loadName, tapeFile); + } + + // Attempt to open the file for reading. + result = f_open(&fileDesc, loadName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("EMZReadTapeDetails(open) File:%s, error: %d.\n", loadName, fileDesc); + return(result); + } + + // Read in the tape header, this indicates crucial data such as data type, size, exec address, load address etc. + // + result = f_read(&fileDesc, &emuControl.tapeHeader, MZF_HEADER_SIZE, &actualReadSize); + if(actualReadSize != 128) + { + debugf("Only read:%d bytes of header, aborting.\n", actualReadSize); + f_close(&fileDesc); + return(0x20); + } + + // Close the open file to complete, details stored in the tapeHeader structure. + f_close(&fileDesc); + + return result; +} + +// Method to load a tape (MZF) file directly into RAM. +// This involves reading the tape header, extracting the size and destination and loading +// the header and program into emulator ram. +// +short EMZLoadTapeToRAM(const char *tapeFile, unsigned char dstCMT) +{ + // Locals. + // + FIL fileDesc; + FRESULT result; + uint32_t loadAddress; + uint32_t actualReadSize; + uint32_t time = *ms; + uint32_t readSize; + char loadName[MAX_FILENAME_LEN+1]; + char sectorBuffer[512]; + #if defined __EMUMZ_DEBUG__ + char fileName[17]; + + debugf("Sending tape file:%s to emulator ram", tapeFile); + #endif + + // If a relative path has been given we need to expand it into an absolute path. + if(tapeFile[0] != '/' && tapeFile[0] != '\\' && (tapeFile[0] < 0x30 || tapeFile[0] > 0x32)) + { + sprintf(loadName, "%s\%s", TOPLEVEL_DIR, tapeFile); + } else + { + strcpy(loadName, tapeFile); + } + + // Attempt to open the file for reading. + result = f_open(&fileDesc, loadName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("EMZLoadTapeToRAM(open) File:%s, error: %d.\n", loadName, fileDesc); + return(result); + } + + // Read in the tape header, this indicates crucial data such as data type, size, exec address, load address etc. + // + result = f_read(&fileDesc, &emuControl.tapeHeader, MZF_HEADER_SIZE, &actualReadSize); + if(actualReadSize != 128) + { + debugf("Only read:%d bytes of header, aborting.\n", actualReadSize); + f_close(&fileDesc); + return(0x20); + } + + // Some sanity checks. + // + if(emuControl.tapeHeader.dataType == 0 || emuControl.tapeHeader.dataType > 5) return(0x21); + #if defined __EMUMZ_DEBUG__ + for(int i=0; i < 17; i++) + { + fileName[i] = emuControl.tapeHeader.fileName[i] == 0x0d ? 0x00 : emuControl.tapeHeader.fileName[i]; + } + + // Debug output to indicate the file loaded and information about the tape image. + // + switch(emuControl.tapeHeader.dataType) + { + case 0x01: + debugf("Binary File(Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + + case 0x02: + debugf("MZ-80 Basic Program(Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + + case 0x03: + debugf("MZ-80 Data File(Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + + case 0x04: + debugf("MZ-700 Data File(Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + + case 0x05: + debugf("MZ-700 Basic Program(Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + + default: + debugf("Unknown tape type(Type=%02x, Load Addr=%04x, Size=%04x, Exec Addr=%04x, FileName=%s)\n", emuControl.tapeHeader.dataType, emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.execAddress, fileName); + break; + } + #endif + + // Check the data type, only load machine code directly to RAM. + // + if(dstCMT == 0 && emuControl.tapeHeader.dataType != CMT_TYPE_OBJCD) + { + f_close(&fileDesc); + return(0x22); + } + + // Reset Emulator if loading direct to RAM. This clears out memory, resets monitor and places it in a known state. + // + if(dstCMT == 0) + EMZReset(); + + // Load the data from tape to RAM. + // + if(dstCMT == 0) // Load to emulators RAM + { + loadAddress = MZ_EMU_RAM_ADDR + emuControl.tapeHeader.loadAddress; + } else + { + loadAddress = MZ_EMU_CMT_DATA_ADDR; + } + for (unsigned short i = 0; i < emuControl.tapeHeader.fileSize && actualReadSize > 0; i += actualReadSize) + { + result = f_read(&fileDesc, sectorBuffer, 512, &actualReadSize); + if(result) + { + debugf("Failed to read data from file:%s @ addr:%08lx, aborting.\n", loadName, loadAddress); + f_close(&fileDesc); + return(0x23); + } + + debugf("Bytes to read, actual:%d, index:%d, sizeHeader:%d, load:%08lx", actualReadSize, i, emuControl.tapeHeader.fileSize, loadAddress); + + if(actualReadSize > 0) + { + // Write the sector (or part) to the fpga memory. + writeZ80Array(loadAddress, sectorBuffer, actualReadSize, FPGA); + loadAddress += actualReadSize; + } else + { + debugf("Bad tape or corruption, should never be 0, actual:%d, index:%d, sizeHeader:%d", actualReadSize, i, emuControl.tapeHeader.fileSize); + return(0x24); + } + } + + // Now load header - this is done last because the emulator monitor wipes the stack area on reset. + // + writeZ80Array(MZ_EMU_CMT_HDR_ADDR, &emuControl.tapeHeader, MZF_HEADER_SIZE, FPGA); + +#ifdef __EMUMZ_DEBUG__ + time = *ms - time; + debugf("Uploaded in %lu ms", time >> 20); +#endif + + // Tidy up. + f_close(&fileDesc); + + // Remove the LF from the header filename, not needed. + // + for(int i=0; i < 17; i++) + { + if(emuControl.tapeHeader.fileName[i] == 0x0d) emuControl.tapeHeader.fileName[i] = 0x00; + } + + // Success. + // + return(0); +} + +// Method to save the contents of the CMT buffer onto a disk based MZF file. +// The method reads the header, writes it and then reads the data (upto size specified in header) and write it. +// +short EMZSaveTapeFromCMT(const char *tapeFile) +{ + short dataSize = 0; + uint32_t readAddress; + uint32_t actualWriteSize; + uint32_t writeSize = 0; + char fileName[MAX_FILENAME_LEN+1]; + char saveName[MAX_FILENAME_LEN+1]; + FIL fileDesc; + FRESULT result; + uint32_t time = *ms; + char sectorBuffer[512]; + + // Read the header, then data, but limit data size to the 'file size' stored in the header. + // + for(unsigned int mb=0; mb <= 1; mb++) + { + // Setup according to block being read, header or data. + // + if(mb == 0) + { + dataSize = MZF_HEADER_SIZE; + readAddress = MZ_EMU_CMT_HDR_ADDR; + } else + { + dataSize = emuControl.tapeHeader.fileSize; + readAddress = MZ_EMU_CMT_DATA_ADDR; + debugf("mb=%d, tapesize=%04x\n", mb, emuControl.tapeHeader.fileSize); + } + for (; dataSize > 0; dataSize -= actualWriteSize) + { + if(mb == 0) + { + writeSize = MZF_HEADER_SIZE; + } else + { + writeSize = dataSize > 512 ? 512 : dataSize; + } + debugf("mb=%d, dataSize=%04x, writeSize=%04x\n", mb, dataSize, writeSize); + + // Read the next chunk from the CMT RAM. + readZ80Array(readAddress, §orBuffer, writeSize, FPGA); + + if(mb == 0) + { + memcpy(&emuControl.tapeHeader, §orBuffer, MZF_HEADER_SIZE); + + // Now open the file for writing. If no name provided, use the one stored in the header. + // + if(tapeFile == 0) + { + for(int i=0; i < 17; i++) + { + fileName[i] = emuControl.tapeHeader.fileName[i] == 0x0d ? 0x00 : emuControl.tapeHeader.fileName[i]; + } + strcat(fileName, ".mzf"); + debugf("File from tape:%s (%02x,%04x,%04x,%04x)\n", fileName, emuControl.tapeHeader.dataType, emuControl.tapeHeader.fileSize, emuControl.tapeHeader.loadAddress, emuControl.tapeHeader.execAddress); + } else + { + strcpy(fileName, tapeFile); + debugf("File provided:%s\n", fileName); + } + + // If a relative path has been given we need to expand it into an absolute path. + if(fileName[0] != '/' && fileName[0] != '\\' && (fileName[0] < 0x30 || fileName[0] > 0x32)) + { + sprintf(saveName, "%s\\%s", emuConfig.params[emuConfig.machineModel].tapeSavePath, fileName); + } else + { + strcpy(saveName, fileName); + } + debugf("File to write:%s\n", saveName); + + // Attempt to open the file for writing. + result = f_open(&fileDesc, saveName, FA_CREATE_ALWAYS | FA_WRITE); + if(result) + { + debugf("EMZSaveFromCMT(open) File:%s, error: %d.\n", saveName, fileDesc); + return(3); + } + } + result = f_write(&fileDesc, sectorBuffer, writeSize, &actualWriteSize); + readAddress += actualWriteSize; + if(result) + { + debugf("EMZSaveFromCMT(write) File:%s, error: %d.\n", saveName, result); + f_close(&fileDesc); + return(4); + } + } + } + + // Close file to complete dump. + f_close(&fileDesc); + + return(0); +} + + +// Method to enable/disable the 40char monitor ROM and select the image to be used in the ROM. +// +void EMZMonitorROM40(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextMonitorROM40(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZMonitorROM40Set; + } +} + +// Method to store the selected file name. +void EMZMonitorROM40Set(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romMonitor40.romFileName, param); + emuConfig.params[emuConfig.machineModel].romMonitor40.romEnabled = 1; + } +} + +// Method to enable/disable the 80char monitor ROM and select the image to be used in the ROM. +// +void EMZMonitorROM80(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextMonitorROM80(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZMonitorROM80Set; + } +} + +// Method to store the selected file name. +void EMZMonitorROM80Set(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romMonitor80.romFileName, param); + emuConfig.params[emuConfig.machineModel].romMonitor80.romEnabled = 1; + } +} + +// Method to enable/disable the Character Generator ROM and select the image to be used in the ROM. +// +void EMZCGROM(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextCGROM(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZCGROMSet; + } +} + +// Method to store the selected file name. +void EMZCGROMSet(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romCG.romFileName, param); + emuConfig.params[emuConfig.machineModel].romCG.romEnabled = 1; + } +} + +// Method to enable/disable the Keyboard Mapping ROM and select the image to be used in the ROM. +// +void EMZKeyMappingROM(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextKeyMappingROM(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZKeyMappingROMSet; + } +} + +// Method to store the selected file name. +void EMZKeyMappingROMSet(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romKeyMap.romFileName, param); + emuConfig.params[emuConfig.machineModel].romKeyMap.romEnabled = 1; + } +} + +// Method to enable/disable the User Option ROM and select the image to be used in the ROM. +// +void EMZUserROM(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextUserROM(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZUserROMSet; + } +} + +// Method to store the selected file name. +void EMZUserROMSet(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romUser.romFileName, param); + emuConfig.params[emuConfig.machineModel].romUser.romEnabled = 1; + } +} + +// Method to enable/disable the FDC Option ROM and select the image to be used in the ROM. +// +void EMZFloppyDiskROM(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextFloppyDiskROM(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.*"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZFloppyDiskROMSet; + } +} + +// Method to store the selected file name. +void EMZFloppyDiskROMSet(char *param) +{ + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].romFDC.romFileName, param); + emuConfig.params[emuConfig.machineModel].romFDC.romEnabled = 1; + } +} + +// Method to enable/disable the cold boot application load and select the image to be loaded. +// +void EMZLoadApplication(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + EMZNextLoadApplication(mode); + EMZRefreshMenu(); + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + EMZSetupDirList("Select File", emuControl.activeDir.dir[emuControl.activeDir.dirIdx], FONT_7X8); + strcpy(emuControl.fileList.fileFilter, "*.MZF"); + emuControl.fileList.selectDir = 0; + EMZReadDirectory(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], emuControl.fileList.fileFilter); + EMZRefreshFileList(); + + // Switch to the File List Dialog mode setting the return Callback which will be activated after a file has been chosen. + // + emuControl.activeDialog = DIALOG_FILELIST; + emuControl.fileList.returnCallback = EMZLoadApplicationSet; + } +} + +// Method to store the selected file name. +void EMZLoadApplicationSet(char *param) +{ + // Locals. + uint8_t tmpbuf[20]; + + // If a file has been selected, store it within the config and also extract the load address from the file to be used as the + // default Post load key injection sequence. + if(strlen(param) < MAX_FILENAME_LEN) + { + strcpy(emuConfig.params[emuConfig.machineModel].loadApp.appFileName, param); + emuConfig.params[emuConfig.machineModel].loadApp.appEnabled = 1; + + // Try read the tape, set tapeHeader structure to tape details. + if(EMZReadTapeDetails(emuConfig.params[emuConfig.machineModel].loadApp.appFileName) == 0) + { + // Clear out existing key entries. + for(uint16_t idx=0; idx < MAX_KEY_INS_BUFFER; idx++) { if(emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx].i == 0) { emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx].i = 0xffffffff; } } + + // First key injection is a pause, a delay needed for the monitor BIOS to startup and present the entry prompt. + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[0].b[0] = 0x00; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[0].b[1] = 0x00; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[0].b[2] = 0x7f; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[0].b[3] = 0x82; + + // Default command based on target machine. + // + switch(emuConfig.machineModel) + { + case MZ80K: + case MZ80C: + sprintf(tmpbuf, "GOTO$%04x\r", emuControl.tapeHeader.execAddress); + break; + + default: + sprintf(tmpbuf, "J%04x\r", emuControl.tapeHeader.execAddress); + break; + } + + for(uint16_t idx=0; idx < strlen(tmpbuf); idx++) + { + // Map the ASCII key and insert into the key injection buffer. + t_numCnv map = EMZMapToScanCode(emuControl.hostMachine, tmpbuf[idx]); + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+1].b[0] = map.b[0]; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+1].b[1] = map.b[1]; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+1].b[2] = 0x7f; + emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+1].b[3] = 0x7f; + + } + } + } +} + +void EMZMainMenu(void) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_MAIN; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Main Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "Tape Storage", 'T', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZTapeStorageMenu, MENUCB_REFRESH, NULL, NULL ); + + // The MZ80K/MZ80C Floppy Disk drives use a different controller to the rest of the models, so not yet implemented. + if(emuConfig.machineModel == MZ80K || emuConfig.machineModel == MZ80C) + { + EMZAddToMenu(row++, 0, "Floppy Storage", 'F', MENUTYPE_SUBMENU, MENUSTATE_GREYED, EMZFloppyStorageMenu, MENUCB_REFRESH, NULL, NULL ); + } else + { + EMZAddToMenu(row++, 0, "Floppy Storage", 'F', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZFloppyStorageMenu, MENUCB_REFRESH, NULL, NULL ); + } + + EMZAddToMenu(row++, 0, "Machine", 'M', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZMachineMenu, MENUCB_REFRESH, NULL, NULL ); + EMZAddToMenu(row++, 0, "Display", 'D', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZDisplayMenu, MENUCB_REFRESH, NULL, NULL ); + EMZAddToMenu(row++, 0, "Audio", 'A', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZAudioMenu, MENUCB_REFRESH, NULL, NULL ); + EMZAddToMenu(row++, 0, "System", 'S', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZSystemMenu, MENUCB_REFRESH, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Reset Machine", 'R', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZResetMachine, MENUCB_DONOTHING, NULL, NULL ); + EMZRefreshMenu(); +} + +void EMZTapeStorageMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + char *fileName; + char lineBuf[MENU_ROW_WIDTH+1]; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_TAPE_STORAGE; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Tape Storage Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "CMT Hardware", 'C', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZChangeCMTMode, MENUCB_REFRESH, EMZGetCMTModeChoice, NULL ); + EMZAddToMenu(row++, 0, "Load tape to RAM", 'L', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZLoadDirectToRAM, MENUCB_DONOTHING, EMZGetLoadDirectFileFilterChoice, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Queue Tape", 'Q', MENUTYPE_ACTION | MENUTYPE_CHOICE, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZQueueTape, MENUCB_DONOTHING, EMZGetQueueTapeFileFilterChoice, NULL ); + + // List out the current tape queue. + if(!emuConfig.params[emuConfig.machineModel].cmtMode) + { + uint16_t fileCount = 0; + while((fileName = EMZNextTapeQueueFilename(0)) != NULL) + { + // Place an indicator on the active queue file. + if((EMZGetMachineGroup() == GROUP_MZ80B && emuControl.tapeQueue.tapePos == fileCount) || + (EMZGetMachineGroup() != GROUP_MZ80B && fileCount == 0)) + sprintf(lineBuf, " >%d %.50s", fileCount++, fileName); + else + sprintf(lineBuf, " %d %.50s", fileCount++, fileName); + EMZAddToMenu(row++, 0, lineBuf, 0x00, MENUTYPE_TEXT, MENUSTATE_TEXT, NULL, MENUCB_DONOTHING, NULL, NULL ); + } + } + + // Hidden function entries to set the active queue position. + EMZAddToMenu(row++, 0, "", '+', MENUTYPE_ACTION, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_HIDDEN : MENUSTATE_INACTIVE, EMZQueueNext, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", '-', MENUTYPE_ACTION, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_HIDDEN : MENUSTATE_INACTIVE, EMZQueuePrev, MENUCB_DONOTHING, NULL, NULL ); + // + EMZAddToMenu(row++, 0, "Clear Queue", 'e', MENUTYPE_ACTION, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZQueueClear, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "File Name Map Ascii", 'F', MENUTYPE_ACTION | MENUTYPE_CHOICE, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextCMTAsciiMapping, MENUCB_REFRESH, EMZGetCMTAsciiMappingChoice, NULL ); + EMZAddToMenu(row++, 0, "Save Tape Directory", 'T', MENUTYPE_ACTION | MENUTYPE_CHOICE, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZTapeSave, MENUCB_DONOTHING, EMZGetTapeSaveFilePathChoice, NULL ); + EMZAddToMenu(row++, 0, "Fast Tape Load", 'd', MENUTYPE_CHOICE, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFastTapeLoad, MENUCB_REFRESH, EMZGetFastTapeLoadChoice, NULL ); + if(EMZGetMachineGroup() != GROUP_MZ80B) + { + // EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK, NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Tape Buttons", 'B', MENUTYPE_CHOICE, !emuConfig.params[emuConfig.machineModel].cmtMode ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextTapeButtons, MENUCB_REFRESH, EMZGetTapeButtonsChoice, NULL ); + } + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZFloppyStorageMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + char *fileName; + char lineBuf[MENU_ROW_WIDTH+1]; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_FLOPPY_STORAGE; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Floppy Storage Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "FDD Hardware", 'F', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZChangeFDDMode, MENUCB_REFRESH, EMZGetFDDModeChoice, NULL ); + EMZAddToMenu(row++, 0, "File Selection Filter", 'S', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextDriveImageFilter, MENUCB_REFRESH, EMZGetFDDDriveFileFilterChoice, NULL ); + + EMZAddToMenu(row++, 0, "Disk 0", '0', MENUTYPE_ACTION | MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZFDDSetDriveImage0, MENUCB_DONOTHING, EMZGetFDDDrive0FileChoice, NULL ); + EMZAddToMenu(row++, 0, " Type", 'T', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDDriveType0, MENUCB_REFRESH, EMZGetFDDDriveType0Choice, NULL ); + EMZAddToMenu(row++, 0, " Image Polarity", 'P', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDImagePolarity0, MENUCB_REFRESH, EMZGetFDDImagePolarity0Choice, NULL ); + EMZAddToMenu(row++, 0, " Update Mode", 'U', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDUpdateMode0, MENUCB_REFRESH, EMZGetFDDUpdateMode0Choice, NULL ); + EMZAddToMenu(row++, 0, " Mount/Eject", 'E', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextMountDrive0, MENUCB_REFRESH, EMZGetFDDMount0Choice, NULL ); + + EMZAddToMenu(row++, 0, "Disk 1", '1', MENUTYPE_ACTION | MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZFDDSetDriveImage1, MENUCB_DONOTHING, EMZGetFDDDrive1FileChoice, NULL ); + EMZAddToMenu(row++, 0, " Type", 'y', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDDriveType1, MENUCB_REFRESH, EMZGetFDDDriveType1Choice, NULL ); + EMZAddToMenu(row++, 0, " Image Polarity", 'i', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDImagePolarity1, MENUCB_REFRESH, EMZGetFDDImagePolarity1Choice, NULL ); + EMZAddToMenu(row++, 0, " Update Mode", 'd', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDUpdateMode1, MENUCB_REFRESH, EMZGetFDDUpdateMode1Choice, NULL ); + EMZAddToMenu(row++, 0, " Mount/Eject", 'j', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextMountDrive1, MENUCB_REFRESH, EMZGetFDDMount1Choice, NULL ); + + EMZAddToMenu(row++, 0, "Disk 2", '2', MENUTYPE_ACTION | MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZFDDSetDriveImage2, MENUCB_DONOTHING, EMZGetFDDDrive2FileChoice, NULL ); + EMZAddToMenu(row++, 0, " Type", 'p', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDDriveType2, MENUCB_REFRESH, EMZGetFDDDriveType2Choice, NULL ); + EMZAddToMenu(row++, 0, " Image Polarity", 'l', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDImagePolarity2, MENUCB_REFRESH, EMZGetFDDImagePolarity2Choice, NULL ); + EMZAddToMenu(row++, 0, " Update Mode", 'M', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDUpdateMode2, MENUCB_REFRESH, EMZGetFDDUpdateMode2Choice, NULL ); + EMZAddToMenu(row++, 0, " Mount/Eject", 'c', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextMountDrive2, MENUCB_REFRESH, EMZGetFDDMount2Choice, NULL ); + + EMZAddToMenu(row++, 0, "Disk 3", '3', MENUTYPE_ACTION | MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZFDDSetDriveImage3, MENUCB_DONOTHING, EMZGetFDDDrive3FileChoice, NULL ); + EMZAddToMenu(row++, 0, " Type", 'e', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDDriveType3, MENUCB_REFRESH, EMZGetFDDDriveType3Choice, NULL ); + EMZAddToMenu(row++, 0, " Image Polarity", 'a', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDImagePolarity3, MENUCB_REFRESH, EMZGetFDDImagePolarity3Choice, NULL ); + EMZAddToMenu(row++, 0, " Update Mode", 'o', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextFDDUpdateMode3, MENUCB_REFRESH, EMZGetFDDUpdateMode3Choice, NULL ); + EMZAddToMenu(row++, 0, " Mount/Eject", 't', MENUTYPE_CHOICE, emuConfig.params[emuConfig.machineModel].fddEnabled ? MENUSTATE_ACTIVE : MENUSTATE_INACTIVE, EMZNextMountDrive3, MENUCB_REFRESH, EMZGetFDDMount3Choice, NULL ); + + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZMachineMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_MACHINE; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Machine Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "Machine Model", 'M', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextMachineModel, MENUCB_REFRESH, EMZGetMachineModelChoice, NULL ); + EMZAddToMenu(row++, 0, "CPU Speed", 'C', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextCPUSpeed, MENUCB_REFRESH, EMZGetCPUSpeedChoice, NULL ); + EMZAddToMenu(row++, 0, "Memory Size", 'S', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextMemSize, MENUCB_REFRESH, EMZGetMemSizeChoice, NULL ); + if(emuConfig.machineModel == MZ800) + { + EMZAddToMenu(row++, 0, "Mode", 'o', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextMZ800Mode, MENUCB_REFRESH, EMZGetMZ800ModeChoice, NULL ); + EMZAddToMenu(row++, 0, "Printer", 'P', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextMZ800Printer, MENUCB_REFRESH, EMZGetMZ800PrinterChoice, NULL ); + EMZAddToMenu(row++, 0, "Tape In", 'T', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextMZ800TapeIn, MENUCB_REFRESH, EMZGetMZ800TapeInChoice, NULL ); + } + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Rom Management", 'R', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZRomManagementMenu, MENUCB_REFRESH, NULL, NULL ); + EMZAddToMenu(row++, 0, "AutoStart Application", 'A', MENUTYPE_SUBMENU, MENUSTATE_ACTIVE, EMZAutoStartApplicationMenu, MENUCB_REFRESH, NULL, NULL ); + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZDisplayMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_DISPLAY; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Display Menu", FONT_7X8); + switch(emuConfig.machineModel) + { + case MZ80K: + case MZ80C: + case MZ1200: + case MZ80A: + case MZ700: + case MZ1500: + EMZAddToMenu(row++, 0, "Display Type", 'T', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextDisplayType, MENUCB_REFRESH, EMZGetDisplayTypeChoice, NULL ); + break; + + default: + break; + } + switch(emuConfig.machineModel) + { + case MZ80A: + case MZ700: + case MZ800: + case MZ1500: + case MZ80B: + case MZ2000: + case MZ2200: + case MZ2500: + EMZAddToMenu(row++, 0, "Display Option", 'D', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextDisplayOption, MENUCB_REFRESH, EMZGetDisplayOptionChoice, NULL ); + break; + + default: + break; + } + EMZAddToMenu(row++, 0, "Display Output", 'O', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextDisplayOutput, MENUCB_REFRESH, EMZGetDisplayOutputChoice, NULL ); + EMZAddToMenu(row++, 0, "Video", 'V', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextVRAMMode, MENUCB_REFRESH, EMZGetVRAMModeChoice, NULL ); + switch(emuConfig.machineModel) + { + case MZ800: + case MZ80B: + case MZ2000: + case MZ2200: + case MZ2500: + EMZAddToMenu(row++, 0, "Graphics", 'G', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextGRAMMode, MENUCB_REFRESH, EMZGetGRAMModeChoice, NULL ); + break; + + default: + break; + } + if(emuConfig.machineModel == MZ80A) + { + EMZAddToMenu(row++, 0, "VRAM CPU Wait", 'W', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextVRAMWaitMode, MENUCB_REFRESH, EMZGetVRAMWaitModeChoice, NULL ); + } + if(strcmp(EMZGetDisplayOptionChoice(), "PCG") == 0) + { + EMZAddToMenu(row++, 0, "PCG Mode", 'P', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextPCGMode, MENUCB_REFRESH, EMZGetPCGModeChoice, NULL ); + } + //EMZAddToMenu(row++, 0, "Aspect Ratio", 'R', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAspectRatio, MENUCB_REFRESH, EMZGetAspectRatioChoice, NULL ); + //EMZAddToMenu(row++, 0, "Scandoubler", 'S', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextScanDoublerFX, MENUCB_REFRESH, EMZGetScanDoublerFXChoice, NULL ); + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZAudioMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_AUDIO; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Audio Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "Source", 'S', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAudioSource, MENUCB_REFRESH, EMZGetAudioSourceChoice, NULL ); + EMZAddToMenu(row++, 0, "Hardware", 'H', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAudioHardware, MENUCB_REFRESH, EMZGetAudioHardwareChoice, NULL ); + // If FPGA sound hardware is selected show configuration options. + if(emuConfig.params[emuConfig.machineModel].audioHardware != 0) + { + EMZAddToMenu(row++, 0, "Volume", 'V', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAudioVolume, MENUCB_REFRESH, EMZGetAudioVolumeChoice, NULL ); + EMZAddToMenu(row++, 0, "Mute", 'M', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAudioMute, MENUCB_REFRESH, EMZGetAudioMuteChoice, NULL ); + EMZAddToMenu(row++, 0, "Channel Mix", 'C', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZNextAudioMix, MENUCB_REFRESH, EMZGetAudioMixChoice, NULL ); + } + + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZSystemMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_SYSTEM; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "System Menu", FONT_7X8); + EMZAddToMenu(row++, 0, "Reload config", 'R', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZReadConfig, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Save config", 'S', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZWriteConfig, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "Reset config", 'e', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZResetConfig, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "About", 'A', MENUTYPE_SUBMENU | MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZAbout, MENUCB_REFRESH, NULL, NULL ); + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZAbout(enum ACTIONMODE mode) +{ + // Locals. + // + uint16_t maxX = OSDGet(ACTIVE_MAX_X); + uint8_t textChrX = (emuControl.menu.colPixelStart / (emuControl.menu.rowFontptr->width + emuControl.menu.rowFontptr->spacing)); + + // Use the menu framework to draw the borders and title but write a bitmap and text directly. + // + EMZSetupMenu(EMZGetMachineTitle(), "About", FONT_7X8); + OSDWriteBitmap(48, 15, BITMAP_ARGO_MEDIUM, RED, BLACK); + OSDWriteString(22, 9, 0, 2, 0, 0, (maxX < 512 ? FONT_5X7 : FONT_7X8), NORMAL, "Sharp MZ Series v2.01", NULL, CYAN, BLACK); + OSDWriteString(19, 10, 0, 2, 0, 0, (maxX < 512 ? FONT_5X7 : FONT_7X8), NORMAL, "(C) Philip Smart, 2018-2021", NULL, CYAN, BLACK); + OSDWriteString(21, 11, 0, 2, 0, 0, (maxX < 512 ? FONT_5X7 : FONT_7X8), NORMAL, "MZ-700 Embedded Version", NULL, CYAN, BLACK); + OSDWriteString(textChrX+1, 0, 0, 4, 0, 0, (maxX < 512 ? FONT_5X7 : FONT_5X7), NORMAL, "\x1b back", NULL, CYAN, BLACK); + EMZRefreshMenu(); +} + +void EMZRomManagementMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_ROMMANAGEMENT; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "Rom Management Menu", FONT_7X8); + + EMZAddToMenu(row++, 0, "Monitor ROM (40x25)", '4', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZMonitorROM40, MENUCB_DONOTHING, EMZGetMonitorROM40Choice, NULL ); + EMZAddToMenu(row++, 0, "Monitor ROM (80x25)", '8', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZMonitorROM80, MENUCB_DONOTHING, EMZGetMonitorROM80Choice, NULL ); + EMZAddToMenu(row++, 0, "Char Generator ROM", 'G', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZCGROM, MENUCB_DONOTHING, EMZGetCGROMChoice, NULL ); + EMZAddToMenu(row++, 0, "Key Mapping ROM", 'K', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZKeyMappingROM, MENUCB_DONOTHING, EMZGetKeyMappingROMChoice, NULL ); + EMZAddToMenu(row++, 0, "User ROM", 'U', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZUserROM, MENUCB_DONOTHING, EMZGetUserROMChoice, NULL ); + EMZAddToMenu(row++, 0, "Floppy Disk ROM", 'F', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZFloppyDiskROM, MENUCB_DONOTHING, EMZGetFloppyDiskROMChoice, NULL ); + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZAutoStartApplicationMenu(enum ACTIONMODE mode) +{ + // Locals. + // + uint8_t row = 0; + char lineBuf[MENU_ROW_WIDTH+1]; + + // Set active menu for the case when this method is invoked as a menu callback. + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_AUTOSTART; + emuControl.activeDialog = DIALOG_MENU; + + EMZSetupMenu(EMZGetMachineTitle(), "AutoStart Menu", FONT_7X8); + + EMZAddToMenu(row++, 0, "Enable AutoStart", 'E', MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZChangeAutoStart, MENUCB_DONOTHING, EMZGetAutoStartChoice, NULL ); + if(emuConfig.params[emuConfig.machineModel].autoStart) + { + EMZAddToMenu(row++, 0, "Application to Load", 'A', MENUTYPE_ACTION | MENUTYPE_CHOICE, MENUSTATE_ACTIVE, EMZLoadApplication, MENUCB_DONOTHING, EMZGetLoadApplicationChoice, NULL ); + EMZAddToMenu(row++, 0, "Pre-load key injection", 'r', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZPreKeyEntry, MENUCB_DONOTHING, NULL, EMZRenderPreKeyViewTop ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + + EMZAddToMenu(row++, 0, "Post-load key injection",'o', MENUTYPE_ACTION, MENUSTATE_ACTIVE, EMZPostKeyEntry, MENUCB_DONOTHING, NULL, EMZRenderPostKeyViewTop ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + EMZAddToMenu(row++, 0, "", 0x00, MENUTYPE_BLANK, MENUSTATE_BLANK , NULL, MENUCB_DONOTHING, NULL, NULL ); + } + + // When called as a select callback then the menus are moving forward so start the active row at the top. + if(mode == ACTION_SELECT) emuControl.activeMenu.activeRow[emuControl.activeMenu.menuIdx] = 0; + EMZRefreshMenu(); +} + +void EMZRenderPreKeyViewTop(void) +{ + EMZRenderPreKeyView(0); +} +void EMZRenderPreKeyView(uint16_t startpos) +{ + // Locals. + // + uint16_t maxX = OSDGet(ACTIVE_MAX_X); + char lineBuf[MAX_INJEDIT_COLS*KEY_INJEDIT_NIBBLES+MENU_ROW_WIDTH+1]; + + // Adjust start position to get 4 view rows. + startpos=startpos > KEY_INJEDIT_ROWS-MAX_INJEDIT_ROWS ? (KEY_INJEDIT_ROWS-MAX_INJEDIT_ROWS)*MAX_INJEDIT_COLS : startpos*MAX_INJEDIT_COLS; + + // Now add in the key entry text in a smaller font. + for(uint16_t idx=startpos; idx < startpos+(MAX_INJEDIT_ROWS*MAX_INJEDIT_COLS); idx+=MAX_INJEDIT_COLS) + { + lineBuf[0] = 0x00; + for(uint16_t idx2=0; idx2 < MAX_INJEDIT_COLS && idx+idx2 < MAX_KEY_INS_BUFFER; idx2++) + { + sprintf(&lineBuf[strlen(lineBuf)], "%s%02x%02x%02x%02x", idx2 == 0 ? "" : " ", + (emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[idx+idx2].b[0]), + (emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[idx+idx2].b[1]), + (emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[idx+idx2].b[2]), + (emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[idx+idx2].b[3])); + } + OSDWriteString(10 - (maxX < 512 ? 2 : 0), 6+((idx-startpos)/MAX_INJEDIT_COLS)+(maxX < 512 ? 1 : 0), 0, 0, 0, 0, maxX < 512 ? FONT_3X6 : FONT_5X7, NORMAL, lineBuf, NULL, PURPLE, BLACK); + } +} +void EMZRenderPostKeyViewTop(void) +{ + EMZRenderPostKeyView(0); +} +void EMZRenderPostKeyView(uint16_t startpos) +{ + // Locals. + // + uint16_t maxX = OSDGet(ACTIVE_MAX_X); + char lineBuf[MAX_INJEDIT_COLS*KEY_INJEDIT_NIBBLES+MENU_ROW_WIDTH+1]; + + // Adjust start position to get 4 view rows. + startpos=startpos > KEY_INJEDIT_ROWS-MAX_INJEDIT_ROWS ? (KEY_INJEDIT_ROWS-MAX_INJEDIT_ROWS)*MAX_INJEDIT_COLS : startpos*MAX_INJEDIT_COLS; + + for(uint16_t idx=startpos; idx < startpos+(MAX_INJEDIT_ROWS*MAX_INJEDIT_COLS); idx+=MAX_INJEDIT_COLS) + { + lineBuf[0] = 0x00; + for(uint16_t idx2=0; idx2 < MAX_INJEDIT_COLS && idx+idx2 < MAX_KEY_INS_BUFFER; idx2++) + { + sprintf(&lineBuf[strlen(lineBuf)], "%s%02x%02x%02x%02x", idx2 == 0 ? "" : " ", + (emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+idx2].b[0]), + (emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+idx2].b[1]), + (emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+idx2].b[2]), + (emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[idx+idx2].b[3])); + } + OSDWriteString(10 - (maxX < 512 ? 2 : 0), 11+((idx-startpos)/MAX_INJEDIT_COLS)+(maxX < 512 ? 1 : 0), 0, 4, 0, 0, maxX < 512 ? FONT_3X6 : FONT_5X7, NORMAL, lineBuf, NULL, GREEN, BLACK); + } +} + +void EMZPreKeyEntry(void) +{ + // Locals. + uint16_t maxX = OSDGet(ACTIVE_MAX_X); + uint8_t lineBuf[10]; + + // Setup the control structure to be used by the key call back eventhandler to correctly update the displayed and stored buffer. + emuControl.keyInjEdit.bufptr = emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion; + emuControl.keyInjEdit.editptr = 0; + emuControl.keyInjEdit.cursorAttr = HILIGHT_BG_WHITE; + emuControl.keyInjEdit.fg = PURPLE; + emuControl.keyInjEdit.bg = BLACK; + emuControl.keyInjEdit.font = maxX < 512 ? FONT_3X6 : FONT_5X7; + emuControl.keyInjEdit.startRow = 6+(maxX < 512 ? 1 : 0); + emuControl.keyInjEdit.startCol = 10 - (maxX < 512 ? 2 : 0); + emuControl.keyInjEdit.offsetRow = 0; + emuControl.keyInjEdit.offsetCol = 0; + emuControl.keyInjEdit.cursorFlashRate = 250; + emuControl.keyInjEdit.curView = 0; + emuControl.keyInjEdit.render = EMZRenderPreKeyView; + + // Setup cursor on the first nibble in buffer. + sprintf(lineBuf, "%01x", (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0]>>4)); + OSDSetCursorFlash(emuControl.keyInjEdit.startCol, emuControl.keyInjEdit.startRow, emuControl.keyInjEdit.offsetCol, emuControl.keyInjEdit.offsetRow, emuControl.keyInjEdit.font, lineBuf[0], emuControl.keyInjEdit.fg, emuControl.keyInjEdit.bg, emuControl.keyInjEdit.cursorAttr, emuControl.keyInjEdit.cursorFlashRate); + + // Setup to use the key injection editor event handler so all keys entered are redirected into this handler. + emuControl.activeDialog = DIALOG_KEYENTRY; + + return; +} + +void EMZPostKeyEntry(void) +{ + // Locals. + uint16_t maxX = OSDGet(ACTIVE_MAX_X); + uint8_t lineBuf[10]; + + // Setup the control structure to be used by the key call back eventhandler to correctly update the displayed and stored buffer. + emuControl.keyInjEdit.bufptr = emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion; + emuControl.keyInjEdit.editptr = 0; + emuControl.keyInjEdit.cursorAttr = HILIGHT_BG_WHITE; + emuControl.keyInjEdit.fg = GREEN; + emuControl.keyInjEdit.bg = BLACK; + emuControl.keyInjEdit.font = maxX < 512 ? FONT_3X6 : FONT_5X7; + emuControl.keyInjEdit.startRow = 11 +(maxX < 512 ? 1 : 0); + emuControl.keyInjEdit.startCol = 10 - (maxX < 512 ? 2 : 0); + emuControl.keyInjEdit.offsetRow = 4; + emuControl.keyInjEdit.offsetCol = 0; + emuControl.keyInjEdit.cursorFlashRate = 250; + emuControl.keyInjEdit.curView = 0; + emuControl.keyInjEdit.render = EMZRenderPostKeyView; + + // Setup cursor on the first nibble in buffer. + sprintf(lineBuf, "%01x", (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0]>>4)); + OSDSetCursorFlash(emuControl.keyInjEdit.startCol, emuControl.keyInjEdit.startRow, emuControl.keyInjEdit.offsetCol, emuControl.keyInjEdit.offsetRow, emuControl.keyInjEdit.font, lineBuf[0], emuControl.keyInjEdit.fg, emuControl.keyInjEdit.bg, emuControl.keyInjEdit.cursorAttr, emuControl.keyInjEdit.cursorFlashRate); + + // Setup to use the key injection editor event handler so all keys entered are redirected into this handler. + emuControl.activeDialog = DIALOG_KEYENTRY; + + return; +} +void EMZKeyInjectionEdit(uint8_t data, uint8_t ctrl) +{ + // Locals. + // + uint8_t lineBuf[10]; + uint8_t row; + uint8_t col; + uint8_t key; + + char tmpbuf[MAX_FILENAME_LEN+1]; + uint16_t rowPixelDepth = (emuControl.fileList.rowFontptr->height + emuControl.fileList.rowFontptr->spacing + emuControl.fileList.padding + 2); + uint16_t maxRow = ((uint16_t)OSDGet(ACTIVE_MAX_Y)/rowPixelDepth) + 1; + + // data = ascii code/ctrl code for key pressed. + // ctrl = KEY_BREAK & KEY_CTRL & KEY_SHIFT & "000" & KEY_DOWN & KEY_UP + + // If the break key is pressed, this is equivalent to escape, ie. exit edit mode. + if(ctrl & KEY_BREAK_BIT) + { + // Disable the cursor. + OSDClearCursorFlash(); + + // Just switch back to active menu dont activate the callback for storing a selection. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } else + { + // Process according to pressed key. + // + switch(data) + { + // Up key. + case 0xA0: + if(emuControl.keyInjEdit.editptr >= KEY_INJEDIT_NIBBLES_PER_ROW) + { + emuControl.keyInjEdit.editptr-=KEY_INJEDIT_NIBBLES_PER_ROW; + } + break; + + // Down key. + case 0xA1: + if(emuControl.keyInjEdit.editptr < ((MAX_KEY_INS_BUFFER*KEY_INJEDIT_NIBBLES)-KEY_INJEDIT_NIBBLES_PER_ROW)) + { + emuControl.keyInjEdit.editptr+=KEY_INJEDIT_NIBBLES_PER_ROW; + } + break; + + // Left key. + case 0xA4: + if(ctrl & KEY_SHIFT_BIT) + { + if(emuControl.keyInjEdit.editptr > 1) + { + emuControl.keyInjEdit.editptr = emuControl.keyInjEdit.editptr >= KEY_INJEDIT_NIBBLES ? ((emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES)-1)*KEY_INJEDIT_NIBBLES : 0; + } + } + else + { + if(emuControl.keyInjEdit.editptr > 0) + { + emuControl.keyInjEdit.editptr--; + } + } + break; + + case 0xA3: // Right Key + if(ctrl & KEY_SHIFT_BIT) + { + if(emuControl.keyInjEdit.editptr < ((MAX_KEY_INS_BUFFER*KEY_INJEDIT_NIBBLES)-KEY_INJEDIT_NIBBLES)) + { + emuControl.keyInjEdit.editptr = ((emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES)+1)*KEY_INJEDIT_NIBBLES; + } + } + else + { + if(emuControl.keyInjEdit.editptr < (MAX_KEY_INS_BUFFER*KEY_INJEDIT_NIBBLES)-1) + { + emuControl.keyInjEdit.editptr++; + } + } + break; + + // All other keys are processed as data. + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + default: + // When CTRL is pressed we insert a nibble based on the key pressed. + if(ctrl & KEY_CTRL_BIT) + { + key = toupper(data); + if(key >= '0' && key <= '9') + key -= '0'; + else if(key >= 'A' && key <= 'F') + key = key - 'A' + 10; + else + // Npt a hex value. + break; + + // Update the nibble according to edit pointer. + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 0) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] & 0x0f | key << 4; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 1) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] & 0xf0 | key; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 2) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] & 0x0f | key << 4; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 3) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] & 0xf0 | key; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 4) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] & 0x0f | key << 4; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 5) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] & 0xf0 | key; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 6) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] & 0x0f | key << 4; + if(emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 7) emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] = emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] & 0xf0 | key; + + // Move onto next nibble. + emuControl.keyInjEdit.editptr += emuControl.keyInjEdit.editptr < (MAX_KEY_INS_BUFFER*KEY_INJEDIT_NIBBLES)-1 ? 1 : 0; + } else + { + emuControl.keyInjEdit.editptr = (emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES) * KEY_INJEDIT_NIBBLES; + t_numCnv map = EMZMapToScanCode(emuControl.hostMachine, data); + + // If a modifier key is required, it must be inserted first with 0 delay in the down/up pause timers. To avoid clash with other valid combinations, + // the delay is set to 0ms down key, 0S up key (stipulated with bit 7 set for seconds). + if(map.b[2] != 0xff && map.b[3] != 0xff) + { + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] = map.b[2]; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] = map.b[3]; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] = 0x00; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] = 0x80; + emuControl.keyInjEdit.editptr += emuControl.keyInjEdit.editptr < (MAX_KEY_INS_BUFFER-1)*KEY_INJEDIT_NIBBLES ? KEY_INJEDIT_NIBBLES : 0; + } + // Now insert the actual key. If the buffer is full we overwrite the last position. + if(map.b[0] != 0xff && map.b[1] != 0xff) + { + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0] = map.b[0]; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1] = map.b[1]; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2] = 0x7f; + emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3] = 0x7f; + emuControl.keyInjEdit.editptr += emuControl.keyInjEdit.editptr < (MAX_KEY_INS_BUFFER-1)*KEY_INJEDIT_NIBBLES ? KEY_INJEDIT_NIBBLES : 0; + } + } + break; + } + + // Set the view portal (to allow for bigger key buffers than the display allows) according to position of edit pointer. + emuControl.keyInjEdit.curView = (emuControl.keyInjEdit.editptr / KEY_INJEDIT_NIBBLES_PER_ROW) > MAX_INJEDIT_ROWS-1 ? (emuControl.keyInjEdit.editptr / KEY_INJEDIT_NIBBLES_PER_ROW) - MAX_INJEDIT_ROWS +1 : 0; + + col = ((emuControl.keyInjEdit.editptr) % KEY_INJEDIT_NIBBLES_PER_ROW) + ((emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES) % MAX_INJEDIT_COLS); + row = emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES_PER_ROW > MAX_INJEDIT_ROWS-1 ? MAX_INJEDIT_ROWS-1 : emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES_PER_ROW; + + sprintf(lineBuf, "%01x", emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 0 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0]&0xf0)>>4 : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 1 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[0]&0x0f) : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 2 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1]&0xf0)>>4 : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 3 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[1]&0x0f) : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 4 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2]&0xf0)>>4 : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 5 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[2]&0x0f) : + emuControl.keyInjEdit.editptr % KEY_INJEDIT_NIBBLES == 6 ? (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3]&0xf0)>>4 : (emuControl.keyInjEdit.bufptr[emuControl.keyInjEdit.editptr/KEY_INJEDIT_NIBBLES].b[3]&0x0f) ); + + OSDSetCursorFlash(emuControl.keyInjEdit.startCol+col, emuControl.keyInjEdit.startRow+row, emuControl.keyInjEdit.offsetCol, emuControl.keyInjEdit.offsetRow, emuControl.keyInjEdit.font, lineBuf[0], emuControl.keyInjEdit.fg, emuControl.keyInjEdit.bg, emuControl.keyInjEdit.cursorAttr, emuControl.keyInjEdit.cursorFlashRate); + if(emuControl.keyInjEdit.render != NULL) + emuControl.keyInjEdit.render(emuControl.keyInjEdit.curView); + } + return; +} + +// Method to switch to a menu given its integer Id. +// +void EMZSwitchToMenu(int8_t menu) +{ + switch(menu) + { + case MENU_MAIN: + EMZMainMenu(); + break; + + case MENU_TAPE_STORAGE: + EMZTapeStorageMenu(ACTION_DEFAULT); + break; + + case MENU_FLOPPY_STORAGE: + EMZFloppyStorageMenu(ACTION_DEFAULT); + break; + + case MENU_MACHINE: + EMZMachineMenu(ACTION_DEFAULT); + break; + + case MENU_DISPLAY: + EMZDisplayMenu(ACTION_DEFAULT); + break; + + case MENU_AUDIO: + EMZAudioMenu(ACTION_DEFAULT); + break; + + case MENU_SYSTEM: + EMZSystemMenu(ACTION_DEFAULT); + break; + + case MENU_ROMMANAGEMENT: + EMZRomManagementMenu(ACTION_DEFAULT); + break; + + case MENU_AUTOSTART: + EMZAutoStartApplicationMenu(ACTION_DEFAULT); + break; + + default: + break; + } + return; +} + + +// Method to write out a complete file with the given name and data. +// +int EMZFileSave(const char *fileName, void *data, int size) +{ + // Locals. + // + FIL fileDesc; + FRESULT result; + unsigned int writeSize; + char saveName[MAX_FILENAME_LEN+1]; + + // If a relative path has been given we need to expand it into an absolute path. + if(fileName[0] != '/' && fileName[0] != '\\' && (fileName[0] < 0x30 || fileName[0] > 0x32)) + { + sprintf(saveName, "%s\%s", TOPLEVEL_DIR, fileName); + } else + { + strcpy(saveName, fileName); + } +printf("Save to File:%s,%s\n", saveName, fileName); + + // Attempt to open the file for writing. + result = f_open(&fileDesc, saveName, FA_CREATE_ALWAYS | FA_WRITE); + if(result) + { + debugf("EMZFileSave(open) File:%s, error: %d.\n", saveName, fileDesc); + } else + { + // Write out the complete buffer. + result = f_write(&fileDesc, data, size, &writeSize); +printf("Written:%d, result:%d\n", writeSize, result); + f_close(&fileDesc); + if(result) + { + debugf("FileSave(write) File:%s, error: %d.\n", saveName, result); + } + } + return(result); +} + +// Method to read into memory a complete file with the given name and data. +// +int EMZFileLoad(const char *fileName, void *data, int size) +{ + // Locals. + // + FIL fileDesc; + FRESULT result; + unsigned int readSize; + char loadName[MAX_FILENAME_LEN+1]; + + // If a relative path has been given we need to expand it into an absolute path. + if(fileName[0] != '/' && fileName[0] != '\\' && (fileName[0] < 0x30 || fileName[0] > 0x32)) + { + sprintf(loadName, "%s\%s", TOPLEVEL_DIR, fileName); + } else + { + strcpy(loadName, fileName); + } + + // Attempt to open the file for reading. + result = f_open(&fileDesc, loadName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("EMZFileLoad(open) File:%s, error: %d.\n", loadName, fileDesc); + } else + { + // Read in the complete buffer. + result = f_read(&fileDesc, data, size, &readSize); + f_close(&fileDesc); + if(result) + { + debugf("FileLoad(read) File:%s, error: %d.\n", loadName, result); + } + } + + return(result); +} + +// Method to load the stored configuration, update the hardware and refresh the menu to reflect changes. +// +void EMZReadConfig(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Load config, if the load fails then we remain with the current config. + // + EMZLoadConfig(); + + // Setup the hardware with the config values. + EMZSwitchToMachine(emuConfig.machineModel, 0); + + // Recreate the menu with the new config values. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + return; +} + +// Method to write the stored configuration onto the SD card persistence. +// +void EMZWriteConfig(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Load config, if the load fails then we remain with the current config. + // + EMZSaveConfig(); + + // Refresh menu to update cursor. + EMZRefreshMenu(); + } + return; +} + +// Method to reset the configuration. This is achieved by copying the power on values into the working set. +// +void EMZResetConfig(enum ACTIONMODE mode) +{ + if(mode == ACTION_TOGGLECHOICE) + { + } else + if(mode == ACTION_DEFAULT || mode == ACTION_SELECT) + { + // Restore the reset parameters into the working set. + memcpy(emuConfig.params, emuControl.hostMachine == HW_MZ2000 ? emuConfigDefault_MZ2000.params : emuControl.hostMachine == HW_MZ80A ? emuConfigDefault_MZ80A.params : emuConfigDefault_MZ700.params, sizeof(emuConfig.params)); + for(uint16_t idx=0; idx < MAX_MZMACHINES; idx++) { for(uint16_t idx2=0; idx2 < MAX_KEY_INS_BUFFER; idx2++) { if(emuConfig.params[idx].loadApp.preKeyInsertion[idx2].i == 0) { emuConfig.params[idx].loadApp.preKeyInsertion[idx2].i = 0xffffffff; } } } + for(uint16_t idx=0; idx < MAX_MZMACHINES; idx++) { for(uint16_t idx2=0; idx2 < MAX_KEY_INS_BUFFER; idx2++) { if(emuConfig.params[idx].loadApp.postKeyInsertion[idx2].i == 0) { emuConfig.params[idx].loadApp.postKeyInsertion[idx2].i = 0xffffffff; } } } + + // Setup the hardware with the config values. + EMZSwitchToMachine(emuConfig.machineModel, 0); + + // Recreate the menu with the new config values. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + return; +} + + +// Method to read in from disk the persisted configuration. +// +void EMZLoadConfig(void) +{ + if(EMZFileLoad(CONFIG_FILENAME, &emuConfig.params, sizeof(emuConfig.params))) + { + debugf("EMZLoadConfig error reading: %s.\n", CONFIG_FILENAME); + } +} + +// Method to save the current configuration onto disk for persistence. +// +void EMZSaveConfig(void) +{ + if(EMZFileSave(CONFIG_FILENAME, emuConfig.params, sizeof(emuConfig.params))) + { + debugf("EMZSaveConfig error writing: %s.\n", CONFIG_FILENAME); + } +} + +// Method to switch and configure the emulator according to the values input by the user OSD interaction. +// +void EMZSwitchToMachine(uint8_t machineModel, uint8_t forceROMLoad) +{ + // Locals. + // + uint8_t result = 0; + + // Suspend the T80. + writeZ80IO(IO_TZ_CPUCFG, CPUMODE_SET_EMU_MZ, TRANZPUTER); + +printf("Machine model:%d, old:%d, change:%d, force:%d, memory:%d\n", machineModel, emuConfig.machineModel, emuConfig.machineChanged, forceROMLoad, emuConfig.params[machineModel].memSize); + // Go through the active configuration, convert to register values and upload the register values into the FPGA. + // + //emuConfig.emuRegisters[MZ_EMU_REG_MODEL] = (emuConfig.emuRegisters[MZ_EMU_REG_MODEL] & 0xF0) | emuConfig.params[machineModel].vramMode << 4 | machineModel; + emuConfig.emuRegisters[MZ_EMU_REG_MODEL] = (EMZGetMemSizeValue() << 4) | (machineModel&0x0f); + +printf("DisplayType:%02x, VRAM:%d, GRAM:%d, WAIT:%d, PCG:%d\n", emuConfig.params[machineModel].displayType, emuConfig.params[machineModel].vramMode, emuConfig.params[machineModel].gramMode, emuConfig.params[machineModel].vramWaitMode, emuConfig.params[machineModel].pcgMode); + emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY] = emuConfig.params[machineModel].pcgMode << 7 | + emuConfig.params[machineModel].vramWaitMode << 6 | + emuConfig.params[machineModel].gramMode << 5 | + emuConfig.params[machineModel].vramMode << 4 | + (emuConfig.params[machineModel].displayType & 0x0f); + + // Display register 2 configures the output type and run time options such as Wait states. +printf("DisplayOutput:%02x,%02x\n", emuConfig.params[machineModel].displayOutput, emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2]); + emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] = (emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] & 0xF0) | emuConfig.params[machineModel].displayOutput; + + // Display register 3 configures the graphics options such as GRAM/PCG. + emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY3] = EMZGetDisplayOptionValue(); + + emuConfig.emuRegisters[MZ_EMU_REG_CPU] = (emuConfig.emuRegisters[MZ_EMU_REG_CPU] & 0xF8) | emuConfig.params[machineModel].cpuSpeed; + emuConfig.emuRegisters[MZ_EMU_REG_AUDIO] = emuConfig.params[machineModel].audioHardware << 7 | emuConfig.params[machineModel].audioMix << 5 | (emuConfig.params[machineModel].audioMute == 1 ? 0 : emuConfig.params[machineModel].audioVolume << 1) | emuConfig.params[machineModel].audioSource; + emuConfig.emuRegisters[MZ_EMU_REG_CMT] = emuConfig.params[machineModel].cmtMode << 7 | + (emuConfig.params[machineModel].cmtAsciiMapping & 0x03) << 5 | + emuConfig.params[machineModel].tapeButtons << 3 | + (emuConfig.params[machineModel].fastTapeLoad & 0x07); + // No current changes to CMT register 2, just a place holder until needed. + emuConfig.emuRegisters[MZ_EMU_REG_CMT2] = emuConfig.emuRegisters[MZ_EMU_REG_CMT2]; + + // Floppy disk configuration register. + emuConfig.emuRegisters[MZ_EMU_REG_FDD] = emuConfig.params[machineModel].fdd[3].mounted << 7 | emuConfig.params[machineModel].fdd[2].mounted << 6 | emuConfig.params[machineModel].fdd[1].mounted << 5 | emuConfig.params[machineModel].fdd[0].mounted << 4 | emuConfig.params[machineModel].fddEnabled; + emuConfig.emuRegisters[MZ_EMU_REG_FDD2] = emuConfig.params[machineModel].fdd[3].updateMode << 7 | emuConfig.params[machineModel].fdd[3].polarity << 6 | + emuConfig.params[machineModel].fdd[2].updateMode << 5 | emuConfig.params[machineModel].fdd[2].polarity << 4 | + emuConfig.params[machineModel].fdd[1].updateMode << 3 | emuConfig.params[machineModel].fdd[1].polarity << 2 | + emuConfig.params[machineModel].fdd[0].updateMode << 1 | emuConfig.params[machineModel].fdd[0].polarity; + + // Option ROMS configuration register. + emuConfig.emuRegisters[MZ_EMU_REG_ROMS] = emuConfig.params[machineModel].romFDC.romEnabled << 1 | emuConfig.params[machineModel].romUser.romEnabled; + + // Setup the hardware switches. + if(machineModel == MZ800) + { + emuConfig.emuRegisters[MZ_EMU_REG_SWITCHES] = 0x0 << 4 | emuConfig.params[machineModel].mz800TapeIn << 3 | emuConfig.params[machineModel].mz800Printer << 2 | emuConfig.params[machineModel].mz800Printer << 1 | emuConfig.params[machineModel].mz800Mode; + } else + { + emuConfig.emuRegisters[MZ_EMU_REG_SWITCHES] = 0x00; + } + + // Set the model and group according to parameter provided. + emuConfig.machineModel = machineModel; + emuConfig.machineGroup = EMZGetMachineGroup(); + + // Based on the machine, upload the required ROMS into the emulator. Although the logic is the same for many machines it is seperated below in case specific implementations/configurations are needed. + // + if(emuConfig.machineChanged || forceROMLoad) + { +printf("%s load\n", MZMACHINES[machineModel]); + if(emuConfig.params[machineModel].romMonitor40.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romMonitor40.romFileName) > 0 && (emuConfig.params[machineModel].displayType == MZ_EMU_DISPLAY_MONO || emuConfig.params[machineModel].displayType == MZ_EMU_DISPLAY_COLOUR)) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romMonitor40.romFileName, 0, emuConfig.params[machineModel].romMonitor40.loadAddr, emuConfig.params[machineModel].romMonitor40.loadSize, 0, FPGA, 1); + if(emuConfig.params[machineModel].romMonitor80.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romMonitor80.romFileName) > 0 && (emuConfig.params[machineModel].displayType == MZ_EMU_DISPLAY_MONO80 || emuConfig.params[machineModel].displayType == MZ_EMU_DISPLAY_COLOUR80)) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romMonitor80.romFileName, 0, emuConfig.params[machineModel].romMonitor80.loadAddr, emuConfig.params[machineModel].romMonitor80.loadSize, 0, FPGA, 1); + if(emuConfig.params[machineModel].romCG.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romCG.romFileName) > 0) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romCG.romFileName, 0, emuConfig.params[machineModel].romCG.loadAddr, emuConfig.params[machineModel].romCG.loadSize, 0, FPGA, 1); + if(emuConfig.params[machineModel].romKeyMap.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romKeyMap.romFileName) > 0) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romKeyMap.romFileName, 0, emuConfig.params[machineModel].romKeyMap.loadAddr, emuConfig.params[machineModel].romKeyMap.loadSize, 0, FPGA, 1); + if(machineModel == MZ80A && emuConfig.params[machineModel].romUser.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romUser.romFileName) > 0) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romUser.romFileName, 0, emuConfig.params[machineModel].romUser.loadAddr, emuConfig.params[machineModel].romUser.loadSize, 0, FPGA, 1); + if(emuConfig.params[machineModel].romFDC.romEnabled == 1 && strlen((char *)emuConfig.params[machineModel].romFDC.romFileName) > 0) + result |= loadZ80Memory((char *)emuConfig.params[machineModel].romFDC.romFileName, 0, emuConfig.params[machineModel].romFDC.loadAddr, emuConfig.params[machineModel].romFDC.loadSize, 0, FPGA, 1); + if(result) + { + printf("Error: Failed to load a ROM into the Sharp MZ Series Emulation ROM memory.\n"); + } + + // As the machine changed force a machine reset. + // + emuConfig.emuRegisters[MZ_EMU_REG_CTRL] |= 0x01; + + // Next invocation we wont load the roms unless machine changes again. + emuConfig.machineChanged = 0; + + // Write the updated registers into the emulator to configure it. + writeZ80Array(MZ_EMU_ADDR_REG_MODEL, emuConfig.emuRegisters, MZ_EMU_MAX_REGISTERS, FPGA); + + // Clear any reset command in the internal register copy, the hardware automatically clears the reset flag. + emuConfig.emuRegisters[MZ_EMU_REG_CTRL] &= 0xFE; + + // Clear the graphics and text screen - this is needed if switching between machines as some machines are not graphic aware and others clear text areas + // differently. This ensures a machine switch will be properly initialised. + fillZ80Memory(MZ_EMU_RED_FB_ADDR, MAX_FB_LEN, 0x00, FPGA); + fillZ80Memory(MZ_EMU_BLUE_FB_ADDR, MAX_FB_LEN, 0x00, FPGA); + fillZ80Memory(MZ_EMU_GREEN_FB_ADDR, MAX_FB_LEN, 0x00, FPGA); + fillZ80Memory(MZ_EMU_TEXT_VRAM_ADDR, MAX_TEXT_VRAM_LEN, 0x00, FPGA); + fillZ80Memory(MZ_EMU_ATTR_VRAM_ADDR, MAX_ATTR_VRAM_LEN, 0x71, FPGA); + } else + { + // Write the updated registers into the emulator to configure it. + writeZ80Array(MZ_EMU_ADDR_REG_MODEL, emuConfig.emuRegisters, MZ_EMU_MAX_REGISTERS, FPGA); + } + + printf("WriteReg: "); for(uint8_t idx=0; idx < 16; idx++) { printf("%02x,", emuConfig.emuRegisters[idx]); } printf("\n"); + readZ80Array(MZ_EMU_ADDR_REG_MODEL, emuConfig.emuRegisters, MZ_EMU_MAX_REGISTERS, FPGA); + printf("ReadReg: "); for(uint8_t idx=0; idx < 16; idx++) { printf("%02x,", emuConfig.emuRegisters[idx]); } printf("\n"); + + // If the floppy module is enabled, set READY so that the disk controller can process commands. + // + if(emuConfig.params[machineModel].fddEnabled) + { + emuControl.fdd.ctrlReg |= FDD_CTRL_READY; + writeZ80Array(MZ_EMU_FDD_CTRL_ADDR+MZ_EMU_FDD_CTRL_REG, &emuControl.fdd.ctrlReg, 1, FPGA); + + // Update disk loaded state (ie. close or flush any ejected disks). + EMZProcessFDDRequest(0, 0, 0, 0, 0, 0); + } + + // Reenable the T80 as it needs to be running prior to reset. + writeZ80IO(IO_TZ_CPUCFG, CPUMODE_CLK_EN | CPUMODE_SET_EMU_MZ, TRANZPUTER); + return; +} + +// Method to handle and process the tape unit, loading files as requested from the queue, saving recorded files to disk and implementing the APSS +// tape drive mechanisms. +// This method is called by interrupt (cmt status change) and periodically (process tape queue). +// +void EMZProcessTapeQueue(uint8_t force) +{ + // Locals. + static unsigned long time = 0; + uint32_t timeElapsed; + char *fileName; + + // Get elapsed time since last service poll. + timeElapsed = *ms - time; + + // If the elapsed time is too short, ie. because a manual call and a scheduled call occur almost simultaneously, exit as it is not needed. + if(timeElapsed < 1000 && !force) + return; + + // MZ80B APSS functionality. + // + if(emuConfig.machineGroup == GROUP_MZ80B) + { + // If Eject set, clear queue then issue CMT Register Clear. + // + if(emuConfig.emuRegisters[MZ_EMU_REG_CMT2] & MZ_EMU_CMT2_EJECT ) + { + debugf("APSS Eject Cassette (%02x:%02x).", emuConfig.emuRegisters[MZ_EMU_REG_CMT2], MZ_EMU_CMT2_EJECT); + EMZClearTapeQueue(); + } else + + // If APSS set, rotate queue forward (DIRECTION = 1) or backward (DIRECTION = 0). + // + if(emuConfig.emuRegisters[MZ_EMU_REG_CMT2] & MZ_EMU_CMT2_APSS ) + { + debugf("APSS Search %s (%02x:%02x).", emuConfig.emuRegisters[MZ_EMU_REG_CMT2] & MZ_EMU_CMT2_DIRECTION ? "Forward" : "Reverse", emuConfig.emuRegisters[MZ_EMU_REG_CMT2], MZ_EMU_CMT2_APSS ); + EMZTapeQueueAPSSSearch(emuConfig.emuRegisters[MZ_EMU_REG_CMT2] & MZ_EMU_CMT2_DIRECTION ? 1 : 0, 1); + } + + // If Play is active, the cache is empty and we are not recording, load into cache the next tape image. + // + if((emuConfig.emuRegisters[MZ_EMU_REG_CMT2] & MZ_EMU_CMT2_PLAY) && !(emuConfig.emuRegisters[MZ_EMU_REG_CMT3] & MZ_EMU_CMT_PLAY_READY) && !(emuConfig.emuRegisters[MZ_EMU_REG_CMT3] & MZ_EMU_CMT_RECORDING) ) + { + // Check the tape queue, if items available, read oldest,upload and rotate. + // + if(emuControl.tapeQueue.elements > 0) + { + // Get the filename from the queue. On the MZ2000 we dont adjust the tape position as the logic will issue an APSS as required. + if(emuConfig.machineModel == MZ80B) + { + fileName = EMZTapeQueueAPSSSearch(1, 1); + } else + { + fileName = EMZTapeQueueAPSSSearch(1, 0); + } + + // If a file was found, upload it into the CMT buffer. + if(fileName != 0) + { + debugf("APSS Play, loading tape: %s\n", fileName); + EMZLoadTapeToRAM(fileName, 1); + + // Need to redraw the menu as the tape queue may have changed. + if(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] == MENU_TAPE_STORAGE) + { + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + } + } + } + } else + + // Standard Tape Deck functionality. + { + // Check to see if the Tape READY signal is inactive, if it is and we have items in the queue, load up the next + // tape file and send it. + // + if((emuConfig.emuRegisters[MZ_EMU_REG_CMT3] & MZ_EMU_CMT_SENSE) && !(emuConfig.emuRegisters[MZ_EMU_REG_CMT3] & MZ_EMU_CMT_PLAY_READY)) + { + // Check the tape queue, if items available, pop oldest and upload. + // + if(emuControl.tapeQueue.elements > 0) + { + // Get the filename from the queue. + fileName = EMZTapeQueuePopFile(1); + + // If a file was found, upload it into the CMT buffer. + if(fileName != 0) + { + debugf("Loading tape: %s\n", fileName); + EMZLoadTapeToRAM(fileName, 1); + + // Need to redraw the menu as the tape queue has changed. + EMZSwitchToMenu(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx]); + } + } + } + } + + // Check to see if the RECORD_READY flag is set. + if( (emuConfig.emuRegisters[MZ_EMU_REG_CMT3] & MZ_EMU_CMT_RECORD_READY) ) + { + EMZSaveTapeFromCMT((const char *)0); + } + + // Reset the timer. + time = *ms; +} + +// Method to check a given file image to ensure it is valid. This method is normally called when the file is +// selected to save time during IRQ processing. +// +short EMZCheckFDDImage(char *fileName) +{ + // Locals. + FIL fileDesc; + FRESULT result; + uint8_t imgType; + uint8_t tmpBuf[35]; + uint32_t actualReadSize; + + // Check extension. + // + const char *ext = strrchr(fileName, '.'); + + // Extended CPC Disk Image. + if(strcasecmp(++ext, "DSK") == 0) + { + imgType = IMAGETYPE_EDSK; + } else + // Raw unformatted binary image. Sector and track defined by user are used to locate FDC requested sector. + if(strcasecmp(ext, "IMG") == 0) + { + imgType = IMAGETYPE_IMG; + } else + { + debugf("Image:%s has no handler.", fileName); + return(-1); + } + + // Open the image. + result = f_open(&fileDesc, fileName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("Image cannot be opened:%d,%s", imgType, fileName); + return(-1); + } + + // Check to see if this is a valid EDSK image. + if(imgType == IMAGETYPE_EDSK) + { + result = f_read(&fileDesc, tmpBuf, 34, &actualReadSize); + if(result) + { + debugf("Cannot read image description block:%d,%s", imgType, fileName); + f_close(&fileDesc); + return(-1); + } + + // Terminate description block header and compare to validate it is an Extended CPC Disk. + tmpBuf[34] = 0x00; + if(strcmp((char*)tmpBuf, "EXTENDED CPC DSK File\r\nDisk-Info\r\n") != 0) + { + debugf("Disk image (%s) is not a valid EDSK file.", fileName); + f_close(&fileDesc); + return(-1); + } + } else + // Not much you can check on a binary image! + if(imgType == IMAGETYPE_IMG) + { + ; + } else + { + f_close(&fileDesc); + return(-1); + } + + // Close image to exit. + f_close(&fileDesc); + + // All checks out, return image type as success flag to caller. + // + return(imgType); +} + +// A method to retrieve the floppy definitions from the image and if valid, store in the configuration. +short EMZSetFDDImageParams(char *fileName, uint8_t driveNo, enum IMAGETYPES imgType) +{ + // Locals. + // + FIL fileDesc; + FRESULT result; + uint8_t tmpBuf[35]; + uint8_t idx; + uint8_t noTracks; + uint8_t noSides; + uint8_t noSectors; + uint16_t sectorSize; + uint32_t offset; + uint32_t actualReadSize; + + // Open the image. + result = f_open(&fileDesc, fileName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("Image cannot be opened:%d,%s", imgType, fileName); + return(-1); + } + + // Check to see if this is a valid EDSK image. + if(imgType == IMAGETYPE_EDSK) + { + // result = f_read(&fileDesc, tmpBuf, 34, &actualReadSize); + // if(result) + // { + // debugf("Cannot read image description block:%d,%s", fileName); + // f_close(&fileDesc); + // return(-1); + // } + + result = f_lseek(&fileDesc, 0x30); + if(!result) + result = f_read(&fileDesc, &tmpBuf, 8, &actualReadSize); + if(result) + { + debugf("Failed to obtain Track/Side info:%s", fileName); + f_close(&fileDesc); + return(-1); + + } + noTracks = tmpBuf[0]; + noSides = tmpBuf[1]; + + offset = 0x100; // Disk Information Block size. + // Go to track 1 and retrieve the sector size. + // The offset is built up by the track entries x 100H added together. The size of tracks can vary hence having to calculate in a loop. + for(idx = 0; idx < (1 * noSides); idx++) + { + result = f_lseek(&fileDesc, offset + 0x14); + if(!result) + result = f_read(&fileDesc, &tmpBuf[10], 2, &actualReadSize); + if(actualReadSize != 2) + { + debugf("Failed to traverse track structure:%s", fileName); + f_close(&fileDesc); + return(-1); + } + if(tmpBuf == 0x00) + { + debugf("Track doesnt exist (%d), bad image:%s", idx/noSides, fileName); + f_close(&fileDesc); + return(-1); + } + + // Get sector size. + sectorSize = tmpBuf[10] == 0x00 ? 128 : tmpBuf[10] == 0x01 ? 256 : tmpBuf[10] == 0x02 ? 512 : 1024; +printf("Sector Size:%d,%02x:%02x\n", sectorSize, tmpBuf[10], tmpBuf[4+idx]); + // Bug on HD images, track reports 0x25 sectors but this is only applicable for the first track. + if(idx > 0 && tmpBuf[4+idx] == 0x25) tmpBuf[4+idx] = 0x11; + + // Position 0 = number of tracks x sides -> Should be constant but it isnt! + offset += tmpBuf[4+idx] * 0x100; //sectorSize; +printf("Loop Offset:%08lx\n", offset); + } +printf("Offset:%08lx\n", offset); + // Go to the first sector information list on track 1 to retrieve sector size. + result = f_lseek(&fileDesc, offset+0x10); + if(!result) + result = f_read(&fileDesc, &tmpBuf, 6, &actualReadSize); + if(result) + { + debugf("Failed to read track 1 info:%s", fileName); + f_close(&fileDesc); + return(-1); + } + noSectors = tmpBuf[5] == 0x25 ? 0x11 : tmpBuf[5]; + sectorSize = tmpBuf[4] == 0x00 ? 128 : tmpBuf[4] == 0x01 ? 256 : tmpBuf[4] == 0x02 ? 512 : 1024; +printf("%dT %dH %dS\n", noTracks, noSides, noSectors); +printf("SectorSize:%08lx,%02x\n", sectorSize, tmpBuf[4]); + + // A lot of conversions on the internet are not accurate, for example a 40T 2H 16S 256B image can be encapsulated in a DSK image with 41T 2H 17S 256B, so need to cater for these images here, mapping + // to a known image definition. This list needs to be updated as more unusual images are encountered or alternatively use samDisk or HxC 2000 to convert them correctly. + if(noTracks > 40 && noTracks < 42) noTracks = 0x28; + if(noTracks > 80 && noTracks < 82) noTracks = 0x50; + if(noSectors > 16 && noSectors <= 18 && sectorSize == 256) noSectors = 16; + + // Now match what we have read with known disks. + debugf("EDISK File(%s) has format:%dT, %dH, %dS, %dB", fileName, noTracks, noSides, noSectors, sectorSize); + for(idx= 0; idx < NUMELEM(FLOPPY_DEFINITIONS); idx++) + { + if(FLOPPY_DEFINITIONS[idx].tracks == noTracks && FLOPPY_DEFINITIONS[idx].heads == noSides && FLOPPY_DEFINITIONS[idx].sectors == noSectors && FLOPPY_DEFINITIONS[idx].sectorSize == sectorSize) + break; + } + if(idx == NUMELEM(FLOPPY_DEFINITIONS)) + { + debugf("Couldnt match image definition to known floppy definition: %dT %dH %dS %dB:%s", noTracks, noSides, noSectors, sectorSize, fileName); + f_close(&fileDesc); + return(-1); + } + + // Save type to configuration list. + emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType = idx; + } else + // Not much you can set on a binary image - apart from filename, the disk type must be set manually as it cannot be determined. + if(imgType == IMAGETYPE_IMG) + { + ; + } else + { + f_close(&fileDesc); + return(-1); + } + + // Set parameters on success. + strcpy(emuConfig.params[emuConfig.machineModel].fdd[driveNo].fileName, fileName); + + // Close image to exit. + f_close(&fileDesc); + + // Success. + return(0); +} + +// Method to process an interrupt request from the Floppy Disk Drive unit, generally raised by the WD1793 controller. +// +enum FLOPPYERRORCODES EMZProcessFDDRequest(uint8_t ctrlReg, uint8_t trackNo, uint8_t sectorNo, uint8_t fdcReg, uint16_t *sectorSize, uint16_t *rotationalSpeed) +{ + // Locals. + // + static FIL fileDesc[MZ_EMU_FDD_MAX_DISKS]; + static uint8_t opened; + static uint8_t dirty; + static uint8_t lastTrack[MZ_EMU_FDD_MAX_DISKS] = {0xff, 0xff, 0xff, 0xff}; + static uint8_t lastSide[MZ_EMU_FDD_MAX_DISKS] = {0xff, 0xff, 0xff, 0xff}; + static uint32_t trackOffset[MZ_EMU_FDD_MAX_DISKS] = {0, 0, 0, 0}; + static uint32_t trackLen[MZ_EMU_FDD_MAX_DISKS] = {0, 0, 0, 0}; + static uint8_t sectorCount[MZ_EMU_FDD_MAX_DISKS]; + uint8_t driveNo = ((ctrlReg & FDD_IOP_DISK_SELECT_NO) >> 5) & 0x03; + uint8_t noSides = FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].heads; + uint8_t side = ctrlReg & FDD_IOP_SIDE ? 1 : 0; + uint8_t sectorsPerTrack = FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].sectors; + uint8_t cmd = (ctrlReg & FDD_IOP_SERVICE_REQ) == 0 ? FDD_IOP_REQ_NOP : ctrlReg & FDD_IOP_REQ_MODE; + uint8_t sectorBuffer[1024]; + uint8_t idx; + // uint16_t sectorSize = FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].sectorSize; + uint32_t actualReadSize; + uint32_t sectorOffset; + uint32_t thisSectorSize = FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].sectorSize; + FRESULT result; +printf("Drive No:%d, %02x, %d\n", driveNo, ctrlReg & FDD_IOP_SERVICE_REQ, emuConfig.params[emuConfig.machineModel].fdd[driveNo].mounted); + // If this is a valid service request, process. + // + if(cmd != FDD_IOP_REQ_NOP) + { + // Is the disk mounted? Cannot process if no disk! + // + if(emuConfig.params[emuConfig.machineModel].fdd[driveNo].mounted) + { + // If the disk hasnt yet been opened, open to save time on next sector requests. + if(!((opened >> driveNo) & 0x01)) + { +printf("Opening disk:%s,%d\n", emuConfig.params[emuConfig.machineModel].fdd[driveNo].fileName, driveNo); + result = f_open(&fileDesc[driveNo], emuConfig.params[emuConfig.machineModel].fdd[driveNo].fileName, FA_OPEN_EXISTING | FA_READ); + if(result) + { + debugf("[open] File:%s, error: %d.\n", emuConfig.params[emuConfig.machineModel].fdd[driveNo].fileName, fileDesc[driveNo]); + return(FLPYERR_DISK_ERROR); + } + // Mark drive as being opened. + opened |= 1 << driveNo; + } + + // Locate sector according to image type. + if(emuConfig.params[emuConfig.machineModel].fdd[driveNo].imgType == IMAGETYPE_EDSK) + { + // If track or side has changed (or first call as lastTrack = 255), calculate the track offset position. + // This is necessary as the track length can vary from track to track, side to side. + if(trackNo != lastTrack[driveNo] || side != lastSide[driveNo]) + { + // Seek to the track information block, get initial sector count to calculate the track length, then back to the start to iterate tracks. + result = f_lseek(&fileDesc[driveNo], 0x34); + // if(!result) result = f_read(&fileDesc, §orBuffer, 2, &actualReadSize); + // if(!result) result = f_lseek(&fileDesc[driveNo], 0x34); + if(result) + { + debugf("Failed to seek to start of TIB:%d, sector:%d, drive:%d", trackNo, sectorNo, driveNo); + return(FLPYERR_TRACK_NOT_FOUND); + } + + // Go to track and retrieve the sector information. + trackLen[driveNo] = 0x0100; + trackOffset[driveNo] = 0x00000000; + for(idx = 0; idx == 0 || idx <= (trackNo * noSides)+side; idx++) + { + result = f_read(&fileDesc[driveNo], §orBuffer, 1, &actualReadSize); + if(actualReadSize != 1) + { + debugf("Failed to traverse track structure:%d", trackNo); + return(FLPYERR_TRACK_NOT_FOUND); + } + + // Bug on HD images, track reports 0x25 sectors but this is only applicable for the first track. + if(idx > 0 && sectorBuffer[0] == 0x25) sectorBuffer[0] = 0x11; + + // if(idx == 0) + // { + // // Disk Information Block size. + // trackOffset[driveNo] = 0x100; + // } else + // { + // trackOffset[driveNo] += sectorBuffer[0] * 0x100; + // } + trackOffset[driveNo] += trackLen[driveNo]; + trackLen[driveNo] = (uint32_t)sectorBuffer[0] * 0x100; +//printf("idx=%d,%08lx:%02x:%08lx,\n", idx,trackOffset[driveNo], sectorBuffer[0], trackLen[driveNo]); + } + // Get next track length for Side 1 offset addition. + // result = f_read(&fileDesc[driveNo], §orBuffer, 1, &actualReadSize); + // if(result) + // { + // debugf("Failed to seek to start of TIB:%d, sector:%d, drive:%d", trackNo, sectorNo, driveNo); + // return(FLPYERR_TRACK_NOT_FOUND); + // } + // trackLen[driveNo] = (uint32_t)sectorBuffer[0] * 0x100; + // Check to see if the track exists, some EDSK files have missing tracks and a request for one is an error. + // + if(sectorBuffer[0] == 0x00) + { + debugf("Track doesnt exist (%d,%d), bad image:%s", side, trackNo, emuConfig.params[emuConfig.machineModel].fdd[driveNo].fileName); + return(FLPYERR_TRACK_NOT_FOUND); + } + uint8_t sectorCountFromTIB = sectorBuffer[0]; + + // Read the sector count for this track, + result = f_lseek(&fileDesc[driveNo], trackOffset[driveNo] + 0x14); + if(!result) + result = f_read(&fileDesc[driveNo], §orBuffer, 2, &actualReadSize); + if(result) + { + debugf("Failed to seek to sector count in TIB:%d, sector:%d, trackOffset:%04x, drive:%d", trackNo, sectorNo, trackOffset[driveNo], driveNo); + return(FLPYERR_TRACK_NOT_FOUND); + } + // There are some wierd formats available, some report one set of sectors in the TIB which differes from the SIB and the TIB is right yet others + // report sectors in the TIB which differs from the SIB and the SIB is right! Work around, choose the maximum sector as the loop below will either find it or error out. + sectorCount[driveNo] = (uint8_t)sectorCountFromTIB > (uint8_t)sectorBuffer[1] ? (uint8_t)sectorCountFromTIB : (uint8_t)sectorBuffer[1]; +printf("trackLen=%08lx, trackOffset=%08lx, sectorCount=%d, %02x,%02x\n", trackLen[driveNo], trackOffset[driveNo], sectorCount[driveNo], sectorBuffer[0], sectorBuffer[1]); + thisSectorSize = sectorBuffer[0] == 0x00 ? 128 : sectorBuffer[0] == 0x01 ? 256 : sectorBuffer[0] == 0x02 ? 512 : 1024; +printf("%02x,%02x trackOffset:%08lx, side:%d, thisSectorSize:%d\n", sectorBuffer[0], sectorBuffer[1], trackOffset[driveNo], side, thisSectorSize); +printf("trackLen:%08lx\n", trackLen[driveNo]); + + // Store track and side so this section is skipped if we remain on a track and side. + lastTrack[driveNo] = trackNo; + lastSide[driveNo] = side; + } + + // // Check sector requested against sector available, error if request is greater. + // // + // if(sectorNo > sectorCount) + // { + // debugf("Requested sector (%d) greater than track max sector (%d), drive:%d", sectorNo, sectorCount, driveNo); + // return(FLPYERR_SECTOR_NOT_FOUND); + // } + + // Traverse the sector list to find the required sector. + sectorOffset = trackOffset[driveNo]; // + (side ? trackLen[driveNo] : 0); + uint32_t offsetLimit = sectorOffset + trackLen[driveNo]; + + // Seek to the Sector Information List for this track. + result = f_lseek(&fileDesc[driveNo], sectorOffset + 0x18); //trackOffset[driveNo] + (side ? trackLen[driveNo] : 0) + 0x18); + + // Loop through the SIL looking for a sector/head match. Constrain the search to a maximum track length of data. This check is in place + // because of the loose usage by host software of the 'sector' number and sector count which differs in the TIB/SIB. + //sectorOffset += 0x0100; + for(idx = 1; idx <= sectorCount[driveNo] && sectorOffset < offsetLimit; idx++) + { + if(!result) result = f_read(&fileDesc[driveNo], §orBuffer, 8, &actualReadSize); + if(result) + { + debugf("Failed to seek and read the Sector Information List for track:%d, sector:%d", trackNo, sectorNo); + return(FLPYERR_SECTOR_NOT_FOUND); + } +//printf("%d:%d:%d,%02x,%02x,%08lx\n", idx, sectorNo, side, sectorBuffer[2], sectorBuffer[1], sectorOffset); + + thisSectorSize = sectorBuffer[3] == 0x00 ? 128 : sectorBuffer[3] == 0x01 ? 256 : sectorBuffer[3] == 0x02 ? 512 : 1024; + if(sectorBuffer[2] == sectorNo) + { + // // Check the sector size for mismatch - if any found then this code needs to be updated to change the controller size parameter dynamically. + // if(thisSectorSize != sectorSize) + // { + // debugf("Sector size mismatch, Track:%d, Sector:%d, DefSize:%d, ReadSize:%d", trackNo, sectorNo, sectorSize, thisSectorSize); + // return(FLPYERR_SECTOR_NOT_FOUND); + // } + // sectorOffset += 0x100; //thisSectorSize; + break; + } + sectorOffset += thisSectorSize; + } + // Add in the offset to track data, which is 256 bytes from the Track Information Block. + sectorOffset += 0x100; +printf("Offset End:%08lx,idx=%d,sectorCount=%d\n", sectorOffset, idx, sectorCount[driveNo]+1); + // Not found? + if(idx == sectorCount[driveNo]+1 || sectorOffset >= offsetLimit) + { + debugf("Sector not found, Track:%d, Sector:%d", trackNo, sectorNo); + return(FLPYERR_SECTOR_NOT_FOUND); + } + } else + + if(emuConfig.params[emuConfig.machineModel].fdd[driveNo].imgType == IMAGETYPE_IMG) + { + // Calculate block based on disk configuration. + // + sectorOffset = (sectorsPerTrack * (sectorNo-1) + (trackNo * sectorsPerTrack) + (ctrlReg & FDD_IOP_SIDE ? sectorsPerTrack : 0)) * thisSectorSize; + printf("SectorsPerTrack=%d, Offset=%d\n", sectorsPerTrack, sectorOffset); + } else + { + debugf("Unrecognised disk image type:%d", emuConfig.params[emuConfig.machineModel].fdd[driveNo].imgType); + return(FLPYERR_DISK_ERROR); + } + + // Sector details determined, sectorOffset points to the exact disk location where the required sector is stored. Complete the command according to request, ie. read/write or info. + // + switch(cmd) + { + case FDD_IOP_REQ_READ: + // Get the sector. + result = f_lseek(&fileDesc[driveNo], sectorOffset); + if(!result) + result = f_read(&fileDesc[driveNo], §orBuffer, thisSectorSize, &actualReadSize); + if(result) + { + debugf("Failed to read the required sector, Track:%d, Sector:%d, offset:%04x", trackNo, sectorNo, sectorOffset); + return(FLPYERR_SECTOR_NOT_FOUND); + } +printf("sectorOffset=%08lx, thisSectorSize=%08lx, actualReadSize=%08lx\n", sectorOffset,thisSectorSize, actualReadSize); + // Write the sector to the sector cache memory. + writeZ80Array(MZ_EMU_FDD_CACHE_ADDR, sectorBuffer, thisSectorSize, FPGA); + break; + + case FDD_IOP_REQ_WRITE: + // Fetch the sector from FDC cache. + // + // Seek to the correct disk location. + // + // Write out the cache. + break; + + case FDD_IOP_REQ_INFO: + // Nothing else needs to be done, the sector has been located and the parameters obtained, these will now be returned to the FDC. + break; + + default: + debugf("Unrecognised service command:%d", cmd); + return(FLPYERR_DISK_ERROR); + break; + } + + + // Update the callers variable to indicate actual disk type parameters, ie. sector size - this is necessary as some disks have various sector sizes per track. The rotational speed is taken from + // the parameters and shouldnt change but kept here just in case! +printf("Check1:%08lx, %08lx, %08lx, %08lx, %d, %d\n", sectorSize, *sectorSize, rotationalSpeed, *rotationalSpeed, thisSectorSize, FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].rpm ); + if(sectorSize != NULL) + (*sectorSize) = thisSectorSize; + if(rotationalSpeed != NULL) + (*rotationalSpeed) = FLOPPY_DEFINITIONS[emuConfig.params[emuConfig.machineModel].fdd[driveNo].diskType].rpm; +printf("Check2:%08lx, %08lx, %08lx, %08lx\n", sectorSize, *sectorSize, rotationalSpeed, *rotationalSpeed); + } + } else + { + // Update the mounted status of all the disks. + // + for(uint8_t idx=0; idx < MZ_EMU_FDD_MAX_DISKS; idx++) + { + // If a disk is mounted, unmount (close) it. + if(opened >> idx) + { +printf("Closing disk:%d\n", idx); + f_close(&fileDesc[idx]); + opened &= ~(1 << idx); + dirty &= ~(1 << idx); + lastTrack[idx] = 0xff; + } + } + } + + return(FLPYERR_NOERROR); +} + + +// Method to invoke necessary services for the Sharp MZ Series emulations, ie. User OSD Menu, Tape Queuing, Floppy Disk loading etc. +// When interrupt = 1 then an interrupt from the FPGA has occurred, when 0 it is a scheduling call. +// +void EMZservice(uint8_t interrupt) +{ + // Locals. + static uint32_t entryScreenTimer = 0xFFFFFFFF; + uint8_t result; + uint8_t emuISRReason[MZ_EMU_INTR_MAX_REGISTERS]; + uint8_t emuInData[256]; + uint8_t emuOutData[256]; + uint8_t cmdType; + uint8_t cmdSvc; + const char * cmdStr; + unsigned long time = 0; + uint32_t timeElapsed; + + // Get elapsed time since last service poll. + time = *ms; + + // Has an interrupt been generated by the emulator support hardware? + if(interrupt) + { + // Take out a Lock on the Z80 bus - this is needed as a number of small operations are performed on the Z80/FPGA during an interrupt cycle and it adds overhead to wait for a Z80 request approval on each occasion. + // + if(lockZ80() == 0) + { + // Read the reason code register. + // + result=readZ80Array(MZ_EMU_REG_INTR_ADDR, emuISRReason, MZ_EMU_INTR_MAX_REGISTERS, FPGA); + printf("IntrReg:"); for(uint16_t idx=0; idx < MZ_EMU_INTR_MAX_REGISTERS; idx++) { printf("%02x ", emuISRReason[idx]); } printf("\n"); + if(!result) + { + // Keyboard interrupt? + if(emuISRReason[MZ_EMU_INTR_REG_ISR] & MZ_EMU_INTR_SRC_KEYB) + { + // Read the key pressed. + result=readZ80Array(MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_CTRL_REG, &emuInData[MZ_EMU_KEYB_CTRL_REG], MZ_EMU_KEYB_MAX_REGISTERS, FPGA); + printf("KeyReg:"); for(uint16_t idx=MZ_EMU_KEYB_CTRL_REG; idx < MZ_EMU_KEYB_CTRL_REG+MZ_EMU_KEYB_MAX_REGISTERS; idx++) { printf("%02x ", emuInData[idx]); } printf("\n"); + if(!result) + { + printf("Received key:%02x, %02x, %d, %d (%d,%d)\n", emuInData[MZ_EMU_KEYB_KEYD_REG], emuInData[MZ_EMU_KEYB_KEYC_REG], emuInData[MZ_EMU_KEYB_KEY_POS_REG], emuInData[MZ_EMU_KEYB_KEY_POS_LAST_REG], emuInData[MZ_EMU_KEYB_FIFO_WR_ADDR], emuInData[MZ_EMU_KEYB_FIFO_RD_ADDR]); + + // Not interested in up key events or control events except for CTRL+BREAK, discard. + if(emuInData[MZ_EMU_KEYB_KEYC_REG] & KEY_DOWN_BIT) + { + // First time the menu key has been pressed, pop up the menu and redirect all key input to the I/O processor. + if(emuControl.activeMenu.menu[0] == MENU_DISABLED && emuInData[MZ_EMU_KEYB_KEYD_REG] == 0xFE) + { + // Recheck OSD parameters - the running machine may change resolution which in turn changes the OSD settings. + OSDUpdateScreenSize(); + + // Setup Menu Font according to available X pixels. Original screen formats only have 320 pixel width. + if((uint16_t)OSDGet(ACTIVE_MAX_X) < 512) + { + EMZSetMenuFont(FONT_5X7); + } else + { + EMZSetMenuFont(FONT_7X8); + } + + // Disable keyboard scan being sent to emulation, enable interrupts on each key press. + emuOutData[MZ_EMU_KEYB_CTRL_REG] = MZ_EMU_KEYB_DISABLE_EMU | MZ_EMU_KEYB_ENABLE_INTR; + writeZ80Array(MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_CTRL_REG, &emuOutData[MZ_EMU_KEYB_CTRL_REG], 1, FPGA); + emuControl.activeMenu.menuIdx = 0; + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_MAIN; + + // Draw the initial main menu. + EMZMainMenu(); + OSDRefreshScreen(); + + // Enable the OSD. + emuOutData[0] = 0x40 | emuConfig.params[emuConfig.machineModel].displayOutput; + emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] = emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] | 0x40; + writeZ80Array(MZ_EMU_ADDR_REG_DISPLAY2, emuOutData, 1, FPGA); + + // Menu active and user has requested to return to emulation? + } + else if(emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] != MENU_DISABLED && emuInData[MZ_EMU_KEYB_KEYD_REG] == 0xFE) + { + // Enable keyboard scan being sent to emulation, disable interrupts on each key press. + emuOutData[MZ_EMU_KEYB_CTRL_REG] = 0; + writeZ80Array(MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_CTRL_REG, &emuOutData[MZ_EMU_KEYB_CTRL_REG], 1, FPGA); + emuControl.activeMenu.menuIdx = 0; + emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] = MENU_DISABLED; + + // Release any allocated menu or file list memory as next invocation restarts at the main menu. + EMZReleaseDirMemory(); + EMZReleaseMenuMemory(); + + // Disable the OSD cursor. + OSDClearCursorFlash(); + + // Disable the OSD, this is done by updating the local register copy as the final act is to upload the latest configuration, OSD mode included. + emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] = emuConfig.emuRegisters[MZ_EMU_REG_DISPLAY2] & 0xbf; + //emuOutData[0] = 0x0; + //writeZ80Array(MZ_EMU_ADDR_REG_DISPLAY3, emuOutData, 1, FPGA); + + // Switch to the requested machine if changed, update the configuration in the FPGA. + // + if(emuConfig.machineChanged) + { + EMZRun(); + } else + { + EMZSwitchToMachine(emuConfig.machineModel, 0); + } + + // Direct key intercept, process according to state. + } else + { + // Event driven key processing + switch(emuControl.activeDialog) + { + case DIALOG_FILELIST: + EMZProcessFileListKey(emuInData[MZ_EMU_KEYB_KEYD_REG], emuInData[MZ_EMU_KEYB_KEYC_REG]); + break; + + case DIALOG_KEYENTRY: + EMZKeyInjectionEdit(emuInData[MZ_EMU_KEYB_KEYD_REG], emuInData[MZ_EMU_KEYB_KEYC_REG]); + break; + + case DIALOG_MENU: + default: + EMZProcessMenuKey(emuInData[MZ_EMU_KEYB_KEYD_REG], emuInData[MZ_EMU_KEYB_KEYC_REG]); + //EMZProcessMenuKeyDebug(emuInData[MZ_EMU_KEYB_KEYD_REG], emuInData[MZ_EMU_KEYB_KEYC_REG]); + break; + } + } + } + } else + { + printf("Key retrieval error.\n"); + } + } + + // CMT generated an interrupt? + if(emuISRReason[MZ_EMU_INTR_REG_ISR] & MZ_EMU_INTR_SRC_CMT) + { + // Read the cmt status directly. This data can also be ready from the Master Control register of the emulator. + result=readZ80Array(MZ_EMU_CMT_REG_ADDR, emuInData, MZ_EMU_CMT_MAX_REGISTERS, FPGA); + + // Update the local copy. + emuConfig.emuRegisters[MZ_EMU_REG_CMT3] = emuInData[MZ_EMU_CMT_STATUS_INTR_REG]; + emuConfig.emuRegisters[MZ_EMU_REG_CMT2] = emuInData[MZ_EMU_CMT_STATUS2_INTR_REG]; + + // When debugging output register change as text message. + debugf("CMT/CMT2 (%02x,%02x,%s%s%s%s%s%s:%s%s%s%s%s).", + emuInData[MZ_EMU_CMT_STATUS_REG], emuInData[MZ_EMU_CMT_STATUS2_REG], + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_PLAY_READY ? "PLAY_READY," : "", + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_PLAYING ? "PLAYING," : "", + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_RECORD_READY ? "RECORD_READY,": "", + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_RECORDING ? "RECORDING," : "", + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_ACTIVE ? "ACTIVE," : "", + emuInData[MZ_EMU_CMT_STATUS_REG] & MZ_EMU_CMT_SENSE ? "SENSE," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_APSS ? "APSS," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_APSS ? emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_DIRECTION ? "FFWD," : "REW," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_EJECT ? "EJECT," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_PLAY ? "PLAY," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_STOP ? "STOP," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_AUTOREW ? "AUTOREW," : "", + emuInData[MZ_EMU_CMT_STATUS2_REG] & MZ_EMU_CMT2_AUTOPLAY ? "AUTOPLAY" : ""); + + debugf("CMT/CMT2i(%02x,%02x,%s%s%s%s%s%s:%s%s%s%s%s).", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG], emuInData[MZ_EMU_CMT_STATUS2_INTR_REG], + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_PLAY_READY ? "PLAY_READY," : "", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_PLAYING ? "PLAYING," : "", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_RECORD_READY ? "RECORD_READY,": "", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_RECORDING ? "RECORDING," : "", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_ACTIVE ? "ACTIVE," : "", + emuInData[MZ_EMU_CMT_STATUS_INTR_REG] & MZ_EMU_CMT_SENSE ? "SENSE," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_APSS ? "APSS," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_APSS ? emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_DIRECTION ? "FFWD," : "REW," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_EJECT ? "EJECT," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_PLAY ? "PLAY," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_STOP ? "STOP," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_AUTOREW ? "AUTOREW," : "", + emuInData[MZ_EMU_CMT_STATUS2_INTR_REG] & MZ_EMU_CMT2_AUTOPLAY ? "AUTOPLAY" : ""); + + // Process the tape queue according to signals received from the hardware. + EMZProcessTapeQueue(1); + } + + // Floppy Disk Drive unit interrupt? + if(emuISRReason[MZ_EMU_INTR_REG_ISR] & MZ_EMU_INTR_SRC_FDD) + { + + // Read the control data to allow a decision as to what is required. + result=readZ80Array(MZ_EMU_FDD_CTRL_ADDR, emuInData, MZ_EMU_FDD_MAX_REGISTERS, FPGA); + + // Debug information to evaluate interrupt. + result=readZ80Array(MZ_EMU_FDC_CTRL_ADDR, &emuInData[MZ_EMU_FDD_MAX_REGISTERS], 32, FPGA); //MZ_EMU_FDC_MAX_REGISTERS, FPGA); + debugf("FDD: (%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x)", + emuInData[MZ_EMU_FDD_CTRL_REG], + emuInData[MZ_EMU_FDD_SECTOR_REG], + emuInData[MZ_EMU_FDD_TRACK_REG], + emuInData[MZ_EMU_FDD_CST_REG], + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG], + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_TRACK_REG], + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_SECTOR_REG], + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_DATA_REG], + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_LCMD_REG]); + + debugf("FDD IOP: Drive No:%d, Head:%s, Request:%s, Command: %s, Sector:%d, Track:%d", + ((emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_DISK_SELECT_NO) >> 5) & 0x03, + emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_SIDE ? "1" : "0", + emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_SERVICE_REQ ? "YES " : "NO", + (emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_REQ_MODE) == 0 ? "NOP" : (emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_REQ_MODE) == 1 ? "READ" : (emuInData[MZ_EMU_FDD_CTRL_REG] & FDD_IOP_REQ_MODE) == 2 ? "WRITE" : "INFO", + emuInData[MZ_EMU_FDD_SECTOR_REG], + emuInData[MZ_EMU_FDD_TRACK_REG]); + debugf(" FDD Signals:(%s%s%s%s) Raw Drive Select:(%d)", + emuInData[MZ_EMU_FDD_CST_REG] & FDD_DISK_BUSY ? "BUSY," : "", + emuInData[MZ_EMU_FDD_CST_REG] & FDD_DISK_DRQ ? "DRQ," : "", + emuInData[MZ_EMU_FDD_CST_REG] & FDD_DISK_DDEN ? "" : "DDEN,", + emuInData[MZ_EMU_FDD_CST_REG] & FDD_DISK_MOTORON ? "" : "MOTOR", + emuInData[MZ_EMU_FDD_CST_REG] & FDD_DISK_SELECT_NO); + + cmdSvc = 0; + switch(emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_LCMD_REG] & 0xF0) + { + case FDC_CMD_RESTORE: + cmdStr = "RESTORE"; + cmdType = 1; + break; + case FDC_CMD_SEEK: + cmdStr = "SEEK"; + cmdType = 1; + break; + case FDC_CMD_STEP: + cmdStr = "STEP"; + cmdType = 1; + break; + case FDC_CMD_STEP_TU: + cmdStr = "STEP TU"; + cmdType = 1; + break; + case FDC_CMD_STEP_IN: + cmdStr = "STEPIN"; + cmdType = 1; + break; + case FDC_CMD_STEPIN_TU: + cmdStr = "STEPIN TU"; + cmdType = 1; + break; + case FDC_CMD_STEPOUT: + cmdStr = "STEPOUT"; + cmdType = 1; + break; + case FDC_CMD_STEPOUT_TU: + cmdStr = "STEPOUT TU"; + cmdType = 1; + break; + case FDC_CMD_READSEC: + cmdStr = "READSEC"; + cmdSvc = 1; + cmdType = 2; + break; + case FDC_CMD_READSEC_MULT: + cmdStr = "READSEC MULT"; + cmdSvc = 1; + cmdType = 2; + break; + case FDC_CMD_WRITESEC: + cmdStr = "WRITESEC"; + cmdSvc = 1; + cmdType = 2; + break; + case FDC_CMD_WRITESEC_MULT: + cmdStr = "WRITESEC MULT"; + cmdSvc = 1; + cmdType = 2; + break; + case FDC_CMD_READADDR: + cmdStr = "READADDR"; + cmdSvc = 1; + cmdType = 3; + break; + case FDC_CMD_READTRACK: + cmdStr = "READTRACK"; + cmdSvc = 1; + cmdType = 3; + break; + case FDC_CMD_WRITETRACK: + cmdStr = "WRITETRACK"; + cmdSvc = 1; + cmdType = 3; + break; + case FDC_CMD_FORCEINT: + cmdStr = "FORCEINT"; + cmdType = 4; + break; + } + debugf(" WD1793 Signals:(%s%s%s%s%s%s%s%s%s[%02x,%d])", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_NOTRDY ? "NOTRDY," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_PROTECTED ? "PROTECTED," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_HEADLOADED ? cmdType != 1 ? "RTYPE/WFAULT," : "HEADLOADED," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_SEEKERROR ? cmdType != 1 ? "RNF," : "SEEKERROR," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_CRCERROR ? "CRCERROR," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_TRACK0 ? cmdType != 1 ? "LOSTDATA," : "TRACK0," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_INDEX ? cmdType != 1 ? "DRQ," : "INDEX," : "", + emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_CTRL_REG] & FDC_STI_BUSY ? "BUSY," : "", + cmdStr, emuInData[MZ_EMU_FDD_MAX_REGISTERS+MZ_EMU_FDC_LCMD_REG], cmdType); + if(cmdType == 3) + debugf("READADDR:%02x,%02x,%02x,%02x,%02x,%02x", emuInData[MZ_EMU_FDD_MAX_REGISTERS+16], emuInData[MZ_EMU_FDD_MAX_REGISTERS+17],emuInData[MZ_EMU_FDD_MAX_REGISTERS+18],emuInData[MZ_EMU_FDD_MAX_REGISTERS+19],emuInData[MZ_EMU_FDD_MAX_REGISTERS+20],emuInData[MZ_EMU_FDD_MAX_REGISTERS+21]); + + for(uint8_t tst=0; tst < 32; tst ++) + { + printf("%02x,", emuInData[MZ_EMU_FDD_MAX_REGISTERS+tst]); + } + // Clear the READY flag. This also clears the interrupt, basically an acknowledgement. + emuControl.fdd.ctrlReg &= ((~FDD_CTRL_READY) & 0x1f); + printf("CTRLREG ENTER:%02x\n", emuControl.fdd.ctrlReg ); + writeZ80Array(MZ_EMU_FDD_CTRL_ADDR+MZ_EMU_FDD_CTRL_REG, &emuControl.fdd.ctrlReg, 1, FPGA); + + // Process the request if it requires servicing. + enum FLOPPYERRORCODES floppyError = FLPYERR_NOERROR; + uint16_t thisSectorSize = 0; + uint16_t thisRotationalSpeed = 0; + if(cmdSvc) floppyError = (uint8_t)EMZProcessFDDRequest(emuInData[MZ_EMU_FDD_CTRL_REG], emuInData[MZ_EMU_FDD_TRACK_REG], emuInData[MZ_EMU_FDD_SECTOR_REG], emuInData[MZ_EMU_FDD_CST_REG], &thisSectorSize, &thisRotationalSpeed); + printf("Error Code:%d, Sector Size:%d, Rotational Speed:%d\n", floppyError, thisSectorSize, thisRotationalSpeed); + + // Processing complete, set the READY flag along with current sector size, rotational speed and error code. 7:5 = error code, 4 = rotational speed, 3:1 = sector size code, 0 = Ready flag. + if(floppyError != FLPYERR_NOERROR) + { + emuControl.fdd.ctrlReg = (floppyError << 5 | FDD_CTRL_READY); + } else + { + emuControl.fdd.ctrlReg = thisRotationalSpeed == 360 ? 0x10 : 0x00 | ((uint8_t)((thisSectorSize&0xff00) >> 7) & 0x0E) | FDD_CTRL_READY; + } + printf("CTRLREG EXIT:%02x\n", emuControl.fdd.ctrlReg ); + writeZ80Array(MZ_EMU_FDD_CTRL_ADDR+MZ_EMU_FDD_CTRL_REG, &emuControl.fdd.ctrlReg, 1, FPGA); + } + } else + { + printf("Interrupt reason retrieval error.\n"); + } + + // Release the lock on the Z80 bus before exitting. + releaseLockZ80(); + + // Indicate processing time. + debugf("Int time:%ld", *ms - time); + } else + { + // Raise an error, couldnt service the interrupt because the Z80 bus couldnt be locked. + // + debugf("Failed to lock the Z80 bus, cannot service interrupt!"); + } + } + + // Scheduling block, called periodically by the zOS scheduling. + else + { + // On the first hot-key menu selection, draw the Argo logo and branding. + if(entryScreenTimer == 0xFFFFFFFF && emuControl.activeMenu.menu[emuControl.activeMenu.menuIdx] == MENU_DISABLED) + { + OSDClearScreen(BLACK); + OSDWriteBitmap(128,0,BITMAP_ARGO, RED, BLACK); + OSDWriteString(31, 6, 0, 10, 0, 0, FONT_9X16, NORMAL, "Sharp MZ Series", NULL, BLUE, BLACK); + OSDRefreshScreen(); + entryScreenTimer = 0x00FFFFF; + + // Enable the OSD. + emuOutData[0] = 0x40 | emuConfig.params[emuConfig.machineModel].displayOutput; + writeZ80Array(MZ_EMU_ADDR_REG_DISPLAY2, emuOutData, 1, FPGA); + } + else if(entryScreenTimer != 0xFFFFFFFF && entryScreenTimer > 0) + { + switch(--entryScreenTimer) + { + // Quick wording change... + case 0x40000: + OSDClearScreen(BLACK); + OSDWriteBitmap(128,0,BITMAP_ARGO, RED, BLACK); + OSDWriteString(31, 6, 0, 10, 0, 0, FONT_9X16, NORMAL, "Argo Inside", NULL, BLUE, BLACK); + OSDRefreshScreen(); + break; + + // Near expiry, clear the OSD and disable it. + case 0x00100: + // Set initial menu on-screen for user to interact with. + OSDClearScreen(BLACK); + + // Disable the OSD. + // + emuOutData[0] = 0x00 | emuConfig.params[emuConfig.machineModel].displayOutput; + writeZ80Array(MZ_EMU_ADDR_REG_DISPLAY2, emuOutData, 1, FPGA); + break; + + default: + break; + } + } + + // When the startup banner has been displayed, normal operations can commence as this module has been initialised. + else if(entryScreenTimer == 0x00000000) + { + // Call the OSD service scheduling routine to allow any OSD updates to take place. + OSDService(); + + // Process the tape queue periodically, this is generally only needed for uploading a new file from the queue, all other actions are serviced + // via interrupt. + EMZProcessTapeQueue(0); + } + } + + return; +} + + +// Initialise the EmuMZ subsystem. This method is called at startup to intialise control structures and hardware settings. +// +uint8_t EMZInit(enum MACHINE_HW_TYPES hostMachine, uint8_t machineModel) +{ + // Locals. + // + uint8_t result = 0; + + // On initialisation, copy the default configuration into the working data structures and if an SD configuration is found, overwrite as necessary. + memcpy(&emuControl, &emuControlDefault, sizeof(t_emuControl)); + memcpy(&emuConfig, hostMachine == HW_MZ2000 ? &emuConfigDefault_MZ2000 : hostMachine == HW_MZ80A ? &emuConfigDefault_MZ80A : &emuConfigDefault_MZ700, sizeof(t_emuConfig)); + for(uint16_t idx=0; idx < MAX_MZMACHINES; idx++) { for(uint16_t idx2=0; idx2 < MAX_KEY_INS_BUFFER; idx2++) { if(emuConfig.params[idx].loadApp.preKeyInsertion[idx2].i == 0) { emuConfig.params[idx].loadApp.preKeyInsertion[idx2].i = 0xffffffff; } } } + for(uint16_t idx=0; idx < MAX_MZMACHINES; idx++) { for(uint16_t idx2=0; idx2 < MAX_KEY_INS_BUFFER; idx2++) { if(emuConfig.params[idx].loadApp.postKeyInsertion[idx2].i == 0) { emuConfig.params[idx].loadApp.postKeyInsertion[idx2].i = 0xffffffff; } } } + + // Initialise the on screen display. + if(!(result=OSDInit(MENU))) + { + // Store the host machine under which the emulation is running, needed for features such as keyboard scan code mapping. + emuControl.hostMachine = hostMachine; + + // Allocate first file list top level directory buffer. + emuControl.activeDir.dirIdx = 0; + + if((emuControl.activeDir.dir[emuControl.activeDir.dirIdx] = (char *)malloc(strlen(TOPLEVEL_DIR)+1)) == NULL) + { + printf("Memory exhausted during init of directory cache, aborting!\n"); + } else + { + strcpy(emuControl.activeDir.dir[emuControl.activeDir.dirIdx], TOPLEVEL_DIR); + } + + // Initialise tape queue. + // + for(int i=0; i < MAX_TAPE_QUEUE; i++) + { + emuControl.tapeQueue.queue[i] = NULL; + } + emuControl.tapeQueue.tapePos = 0; + emuControl.tapeQueue.elements = 0; + emuControl.tapeQueue.fileName[0] = 0; + + // Read in the persisted configuration. + // + EMZLoadConfig(); + + // Setup the local copy of the emulator register contents. + if(readZ80Array(MZ_EMU_ADDR_REG_MODEL, emuConfig.emuRegisters, MZ_EMU_MAX_REGISTERS, FPGA)) + { + printf("Failed to read initial emulator register configuration.\n"); + } + + // Initialise Floppy control variables. + emuControl.fdd.ctrlReg = 0; + + // Finally set the initial machine and group. + emuConfig.machineModel = machineModel; + emuConfig.machineGroup = EMZGetMachineGroup(); + } + + return(result); +} + +// Once the tranZPUter has been engaged and configured, start the emulation with the required machine. +// +void EMZRun(void) +{ + // Locals. + uint8_t errCode; + uint8_t keyCnt; + + // Ensure the tape queue is cleared, dont persist between emulations. + EMZClearTapeQueue(); + + // Switch to the requested machine and upload the configuration to the FPGA. + // + EMZSwitchToMachine(emuConfig.machineModel, 1); + + // If startup application is enabled and pre key insertion keys given, send them to the emulation keyboard module. This functionality works without + // an actual application for load being specified and these keys can also be used to interact with the I/O processor menu, ie. to set a CPU speed. + if(emuConfig.params[emuConfig.machineModel].loadApp.appEnabled && emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[0].i != 0xffffffff) + { + // Count the number of keys for insertion. + for(keyCnt=0; keyCnt < MAX_KEY_INS_BUFFER; keyCnt++) { if(emuConfig.params[emuConfig.machineModel].loadApp.preKeyInsertion[keyCnt].i == 0xffffffff) break; } + } + + // If a startup application is specified, load it direct to RAM. + // + if(emuConfig.params[emuConfig.machineModel].loadApp.appEnabled && strlen(emuConfig.params[emuConfig.machineModel].loadApp.appFileName) > 0) + { + // Use the tape method for loading direct to memory. Currently startup applications limited to tape but this will change as the project progresses. + errCode = EMZLoadTapeToRAM(emuConfig.params[emuConfig.machineModel].loadApp.appFileName, 0); + if(errCode != 0) + { + debugf("Failed to load startup application:%s to memory.", emuConfig.params[emuConfig.machineModel].loadApp.appFileName); + } + } + + // If a startup application is enabled and there are post keys to be injected into the keyboard module, send them. + // + if(emuConfig.params[emuConfig.machineModel].loadApp.appEnabled && emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[0].i != 0xffffffff) + { + // Count the number of keys for insertion. + for(keyCnt=0; keyCnt < MAX_KEY_INS_BUFFER; keyCnt++) { if(emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion[keyCnt].i == 0xffffffff) break; } +printf("KeyCnt:%d, addr=%08lx, mem=%08lx\n", keyCnt, MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_FIFO_ADDR, (uint8_t *)&emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion); + writeZ80Array(MZ_EMU_REG_KEYB_ADDR+MZ_EMU_KEYB_FIFO_ADDR, (uint8_t *)&emuConfig.params[emuConfig.machineModel].loadApp.postKeyInsertion, keyCnt*4, FPGA); + } +} diff --git a/software/FusionX/src/z80drv/MZ80A/k64fcpu.c b/software/FusionX/src/z80drv/src/k64fcpu.c similarity index 99% rename from software/FusionX/src/z80drv/MZ80A/k64fcpu.c rename to software/FusionX/src/z80drv/src/k64fcpu.c index 65ca232f3..07eb94e87 100644 --- a/software/FusionX/src/z80drv/MZ80A/k64fcpu.c +++ b/software/FusionX/src/z80drv/src/k64fcpu.c @@ -183,16 +183,10 @@ int getch(uint8_t wait) return 0; } -void delay(int number_of_seconds) +// Millisecond delay routine. +void delay(int ms_delay) { - // 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); + usleep(1000 * ms_delay); } // 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); // Start the Z80 after initialisation of the memory. + delay(500); startZ80(TZMM_BOOT); return; } @@ -2925,6 +2920,18 @@ int main(int argc, char *argv[]) printf("Failed to open the Z80 Driver, exiting...\n"); 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. signal(SIGIO, z80ServiceRequest); diff --git a/software/FusionX/src/z80drv/MZ2000/optparse.h b/software/FusionX/src/z80drv/src/optparse.h similarity index 100% rename from software/FusionX/src/z80drv/MZ2000/optparse.h rename to software/FusionX/src/z80drv/src/optparse.h diff --git a/software/FusionX/src/z80drv/MZ80A/sharpbiter.c b/software/FusionX/src/z80drv/src/sharpbiter.c similarity index 84% rename from software/FusionX/src/z80drv/MZ80A/sharpbiter.c rename to software/FusionX/src/z80drv/src/sharpbiter.c index 40781575d..7236371fc 100644 --- a/software/FusionX/src/z80drv/MZ80A/sharpbiter.c +++ b/software/FusionX/src/z80drv/src/sharpbiter.c @@ -101,9 +101,15 @@ static t_Z80Ctrl *Z80Ctrl = NULL; static uint8_t *Z80RAM = NULL; static uint8_t *Z80ROM = NULL; +// Millisecond delay routine. +void delay(int ms_delay) +{ + usleep(1000 * ms_delay); +} + // Method to reset the Z80 CPU. // -void reqResetZ80(uint8_t memoryMode) +void reqResetZ80(void) { // Locals. // @@ -116,7 +122,7 @@ void reqResetZ80(uint8_t memoryMode) // Method to start the Z80 CPU. // -void startZ80(uint8_t memoryMode) +void startZ80(void) { // Locals. // @@ -129,7 +135,7 @@ void startZ80(uint8_t memoryMode) // Method to stop the Z80 CPU. // -void stopZ80(uint8_t memoryMode) +void stopZ80(void) { // Locals. // @@ -323,24 +329,42 @@ int main(int argc, char *argv[]) ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); // Stop the Z80. - ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; - ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); + stopZ80(); // Remove drivers. Remove all because an external event could have changed last configured driver. // 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; ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; 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. - 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); + delay(500); + reqResetZ80(); break; case HOTKEY_RFS80: @@ -349,12 +373,21 @@ int main(int argc, char *argv[]) ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); // Stop the Z80. - ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; - ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); + stopZ80(); // Remove drivers. Remove all because an external event could have changed last configured driver. // 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; ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; @@ -368,10 +401,8 @@ int main(int argc, char *argv[]) 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); + delay(500); + reqResetZ80(); break; case HOTKEY_TZFS: @@ -379,12 +410,21 @@ int main(int argc, char *argv[]) ioctl(arbCtrl.fdTTY, IOCTL_CMD_SUSPEND_IO, &result); // Stop the Z80. - ioctlCmd.cmd = IOCTL_CMD_Z80_STOP; - ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); + stopZ80(); // Remove drivers. Remove all because an external event could have changed last configured driver. // 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; ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; @@ -396,12 +436,6 @@ int main(int argc, char *argv[]) ioctlCmd.cmd = IOCTL_CMD_ADD_DEVICE; ioctlCmd.vdev.device = VIRTUAL_DEVICE_TZPU; 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; 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. // 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; ioctl(arbCtrl.fdZ80, IOCTL_CMD_SEND, &ioctlCmd); ioctlCmd.vdev.device = VIRTUAL_DEVICE_RFS40; diff --git a/software/FusionX/src/z80drv/MZ80A/sharpmz.c b/software/FusionX/src/z80drv/src/sharpmz.c similarity index 100% rename from software/FusionX/src/z80drv/MZ80A/sharpmz.c rename to software/FusionX/src/z80drv/src/sharpmz.c diff --git a/software/FusionX/src/z80drv/MZ80A/tzpu.h b/software/FusionX/src/z80drv/src/tzpu.h similarity index 99% rename from software/FusionX/src/z80drv/MZ80A/tzpu.h rename to software/FusionX/src/z80drv/src/tzpu.h index 1a11874b7..e0d9992f9 100755 --- a/software/FusionX/src/z80drv/MZ80A/tzpu.h +++ b/software/FusionX/src/z80drv/src/tzpu.h @@ -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 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 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. +#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. +#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 TZ_MAX_Z80_MEM 0x100000 // Maximum Z80 memory available on the tranZPUter board. diff --git a/software/FusionX/src/z80drv/MZ80A/z80ctrl.c b/software/FusionX/src/z80drv/src/z80ctrl.c similarity index 96% rename from software/FusionX/src/z80drv/MZ80A/z80ctrl.c rename to software/FusionX/src/z80drv/src/z80ctrl.c index 6dde0c153..648869834 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80ctrl.c +++ b/software/FusionX/src/z80drv/src/z80ctrl.c @@ -593,6 +593,23 @@ int ctrlCmd(int fdZ80, enum CTRL_COMMANDS cmd, long param1, long param2, long pa { 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) { 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; } + 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) { 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(" = LOADMEM --file --addr <24 bit addr> --type <0 - Host RAM, 1 = Virtual RAM, 2 = Virtual ROM> [--offset --len ]\n"); printf(" = SAVE --file --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"); #if(DEBUG_ENABLED != 0) printf(" = DEBUG --level # 0 = off, 1..15 debug level, 15 is very verbose.\n"); diff --git a/software/FusionX/src/z80drv/MZ80A/z80driver.c b/software/FusionX/src/z80drv/src/z80driver.c similarity index 71% rename from software/FusionX/src/z80drv/MZ80A/z80driver.c rename to software/FusionX/src/z80drv/src/z80driver.c index ade923132..42a3bc3b2 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80driver.c +++ b/software/FusionX/src/z80drv/src/z80driver.c @@ -29,11 +29,15 @@ // in host memory at full speed. // Feb 2023 - v1.2 Added MZ-80A Rom Filing System device driver. This allows the FusionX // hosted in an MZ-80A to run the original RFS Monitor and software. -// Feb 2023 - v11.3 Added tranZPUter SW device driver. This allows the FusionX hosted +// Feb 2023 - v1.3 Added tranZPUter SW device driver. This allows the FusionX hosted // in any supported host to run TZFS and the updated applications // such as CP/M, SA-5510 Basic, MS-Basic etc. Adding this device driver // prepares the ground to add the SOM GPU as the Video emulation of // the Sharp machines. +// Mar 2023 - v1.4 With the advent of the PCW-8XXX series, seperated distinct machine +// configurations into device driver modules, which are only built +// if same as the target machine. This then allows the z80 driver to be +// a vanilla Z80 or customisations for hosts pulled in. // // // Notes: See Makefile to enable/disable conditional components @@ -111,9 +115,23 @@ static DEFINE_MUTEX(Z80DRV_MUTEX); // Include the virtual hardware drivers. #if(TARGET_HOST_MZ80A == 1) + #include "z80vhw_mz80a.c" +#endif +#if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) #include "z80vhw_rfs.c" #endif -#include "z80vhw_tzpu.c" +#if(TARGET_HOST_MZ700 == 1) + #include "z80vhw_mz700.c" +#endif +#if(TARGET_HOST_MZ2000 == 1) + #include "z80vhw_mz2000.c" +#endif +#if(TARGET_HOST_PCW == 0) + #include "z80vhw_tzpu.c" +#endif +#if(TARGET_HOST_PCW == 1) + #include "z80vhw_pcw.c" +#endif //------------------------------------------------------------------------------------------------------------------------------- // @@ -128,269 +146,46 @@ static DEFINE_MUTEX(Z80DRV_MUTEX); static inline void decodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag) { // Locals. - uint32_t idx; // If the RFS module is enabled, it must set the map otherwise use the default handler. // #if(TARGET_HOST_MZ80A == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ80A) + { + mz80aDecodeMemoryMapSetup(address, data, ioFlag, readFlag); + } else + #endif + #if(TARGET_HOST_MZ700 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ700) + { + mz700DecodeMemoryMapSetup(address, data, ioFlag, readFlag); + } else + #endif + #if(TARGET_HOST_MZ2000 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ2000) + { + mz2000DecodeMemoryMapSetup(address, data, ioFlag, readFlag); + } else + #endif + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) { rfsDecodeMemoryMapSetup(address, data, ioFlag, readFlag); } else #endif + #if(TARGET_HOST_PCW == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_PCW) + { + pcwDecodeMemoryMapSetup(address, data, ioFlag, readFlag); + } else + #endif + #if(TARGET_HOST_PCW == 0) if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU) { - rfsDecodeMemoryMapSetup(address, data, ioFlag, readFlag); + tzpuDecodeMemoryMapSetup(address, data, ioFlag, readFlag); } else + #endif { - // 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) - { - #if(TARGET_HOST_MZ700 == 1) - #endif - #if(TARGET_HOST_MZ2000 == 1) - #endif - - #if(TARGET_HOST_MZ80A == 1) - // 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); - } - } - Z80Ctrl->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)); - } - } - Z80Ctrl->memSwitch = 0; - break; - #endif - - default: - break; - } - } else - { - // 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 to the state prior to the complimentary command being invoked. - #if(TARGET_HOST_MZ700 == 1) - // 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; - #endif - - #if(TARGET_HOST_MZ2000 == 1) - case IO_ADDR_E0: - break; - - case IO_ADDR_E1: - break; - - case IO_ADDR_E2: - break; - - case IO_ADDR_E3: - // Program control register. - if(value & 0x80) - { - } else - { - switch((value >> 1) & 0x07) - { - // NST toggle. - case 1: - // NST pages in all RAM and resets cpu. - if(value & 0x01) - { - Z80Ctrl->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; - - default: - break; - } - } - break; - - case IO_ADDR_E8: - // NEED FLAG TO SET THIS WHEN CALLED WITH NON MEMORY SWITCH BYTE - if(isPhysical(0xD000) && (value & 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, (Z80Ctrl->lowMemorySwap ? idx - 0x8000 : idx)); - } - } - } else - if(value & 0x80) - { - if(value & 0x40) - { - setMemoryType(0xD000/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, 0xD000); - } else - { - for(idx=0xC000; idx < 0x10000; idx+=MEMORY_BLOCK_GRANULARITY) - { - setMemoryType(idx/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_PHYSICAL_VRAM, idx); - } - } - } - break; - #endif - - #if(TARGET_HOST_MZ80A == 1) - #endif - - // Port is not a memory management port. - default: - break; - } - } } return; @@ -407,6 +202,31 @@ static inline zuint8 readVirtual(zuint16 address, uint8_t ioFlag) // Invoke the specific hardware emulation. // #if(TARGET_HOST_MZ80A == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ80A) + { + data = mz80aRead(address, ioFlag); + } else + #endif + #if(TARGET_HOST_MZ700 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ700) + { + data = mz700Read(address, ioFlag); + } else + #endif + #if(TARGET_HOST_MZ2000 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ2000) + { + data = mz2000Read(address, ioFlag); + } else + #endif + #if(TARGET_HOST_PCW == 1) + if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_PCW)) + { + data = pcwRead(address, ioFlag); + } else + #endif + + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) // RFS only has memory mapped registers. if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) && ioFlag == 0) { @@ -414,15 +234,14 @@ static inline zuint8 readVirtual(zuint16 address, uint8_t ioFlag) } else #endif + #if(TARGET_HOST_PCW == 0) if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)) { data = tzpuRead(address, ioFlag); - } + } else + #endif - else if(isVirtualMemory(address)) { - // Retrieve data from virtual memory. - data = isVirtualROM(address) ? readVirtualROM(address) : readVirtualRAM(address); } return(data); @@ -437,6 +256,31 @@ static inline void writeVirtual(zuint16 address, zuint8 data, uint8_t ioFlag) // Invoke the specific hardware emulation. // #if(TARGET_HOST_MZ80A == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ80A) + { + mz80aWrite(address, data, ioFlag); + } else + #endif + #if(TARGET_HOST_MZ700 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ700) + { + mz700Write(address, data, ioFlag); + } else + #endif + #if(TARGET_HOST_MZ2000 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ2000) + { + mz2000Write(address, data, ioFlag); + } else + #endif + #if(TARGET_HOST_PCW == 1) + if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_PCW)) + { + pcwWrite(address, data, ioFlag); + } else + #endif + + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) // RFS only has memory mapped registers. if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) && ioFlag == 0) { @@ -444,15 +288,14 @@ static inline void writeVirtual(zuint16 address, zuint8 data, uint8_t ioFlag) } else #endif + #if(TARGET_HOST_PCW == 0) if((Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)) { tzpuWrite(address, data, ioFlag); - } + } else + #endif - else if(isVirtualRAM(address)) { - // Update virtual memory. - writeVirtualRAM(address, data); } return; @@ -464,43 +307,133 @@ static inline void writeVirtual(zuint16 address, zuint8 data, uint8_t ioFlag) // time critical operations such as Floppy Disk read/write. // // This method attempts to decode the current opcode and if it is a hardware operation, make the request ahead of the Z80 emulator. -static inline void lookAhead(zuint16 address, zuint8 opcode, zuint8 opcode2) +static inline uint8_t lookAhead(zuint16 address, zuint8 opcode, zuint8 opcode2) { - // Locals. - // - - // IN r,(C) INI, INIR, IND, INDR - if( (opcode == 0xED && (( (opcode2 & 0x78) != 0) || ((opcode2 & 0xBA) != 0)) && ((opcode2 & 0x01) == 0x00)) ) + // IN A,(C) ED 78 + // IN B,(C) ED 40 + // IN C,(C) ED 48 + // IN D,(C) ED 50 + // IN E,(C) ED 58 + // IN H,(C) ED 60 + // IN L,(C) ED 68 + if( opcode == 0xED && (opcode2 == 0x78 || opcode2 == 0x40 || opcode2 == 0x48 || opcode2 == 0x50 || opcode2 == 0x58 || opcode2 == 0x60 || opcode2 == 0x68) ) { - SPI_SEND32( (Z80CPU.bc.uint16_value << 16) | CPLD_CMD_READIO_ADDR); - Z80Ctrl->ioReadAhead = 1; + if(isPhysicalIO(Z80CPU.bc.uint16_value)) + { + if((Z80CPU.bc.uint16_value&0x00ff) < 8) + { + SPI_SEND_8(CPLD_CMD_READIO_ADDR + (Z80CPU.bc.uint16_value&0x00ff)); + } else + { + //SPI_SEND_16((Z80CPU.bc.uint16_value&0x00ff) << 8 | CPLD_CMD_READIO_ADDR); + SPI_SEND_32(Z80CPU.bc.uint16_value, CPLD_CMD_READIO_ADDR); + } + Z80Ctrl->ioReadAhead = 1; + } } - // IN A, (N) + // IND ED AA + // INDR ED BA + // INI ED A2 + // INIR ED B2 + else if( opcode == 0xED && (opcode2 == 0xAA || opcode2 == 0xBA || opcode2 == 0xA2 || opcode2 == 0xB2) ) + { + if(isPhysicalIO(Z80CPU.bc.uint16_value)) + { + #if(TARGET_HOST_PCW == 0) + if((Z80CPU.bc.uint16_value&0x00ff) < 8) + { + SPI_SEND_8(CPLD_CMD_READIO_ADDR + (Z80CPU.bc.uint16_value&0x00ff)); + } else + { + SPI_SEND_32(Z80CPU.bc.uint16_value, CPLD_CMD_READIO_ADDR); + } + Z80Ctrl->ioReadAhead = 1; + #else + if((Z80CPU.bc.uint16_value&0x00ff) < 8) + { + SPI_SEND_8(CPLD_CMD_READIO_WRITE_ADDR + (Z80CPU.bc.uint16_value&0x00ff)); + } else + { + //SPI_SEND_32(Z80CPU.bc.uint16_value, CPLD_CMD_READIO_WRITE_ADDR); + SPI_SEND_16((Z80CPU.bc.uint16_value&0x00ff) << 8 | CPLD_CMD_READIO_WRITE_ADDR); + } + + // Send destination address. + SPI_SEND_P_16(Z80CPU.hl.uint16_value); + Z80Ctrl->ioReadAhead = 2; + #endif + } + } + // IN A,(N) DB XX else if(opcode == 0xDB) { - SPI_SEND32( ((Z80CPU.bc.uint16_value & 0xff00) | opcode2) << 16 | CPLD_CMD_READIO_ADDR); - Z80Ctrl->ioReadAhead = 1; + if(isPhysicalIO(opcode2)) + { + if(opcode2 < 8) + { + SPI_SEND_8(CPLD_CMD_READIO_ADDR + opcode2); + } else + { + SPI_SEND_32(((Z80CPU.bc.uint16_value & 0xff00) | opcode2), CPLD_CMD_READIO_ADDR); + // SPI_SEND_16(opcode2 << 8 | CPLD_CMD_READIO_ADDR); + } + //SPI_SEND_32(((Z80CPU.bc.uint16_value & 0xff00) | opcode2), CPLD_CMD_READIO_ADDR); + Z80Ctrl->ioReadAhead = 1; + } } - // OUT (C), r OTDR, OTIR, OUTD, OUTI - else if( (opcode == 0xED && (( (opcode2 & 0x79) != 0) || ((opcode2 & 0xBB) != 0)) && ((opcode2 & 0x01) == 0x01)) ) + + // OUT (C),A ED 79 + // OUT (C),B ED 41 + // OUT (C),C ED 49 + // OUT (C),D ED 51 + // OUT (C),E ED 59 + // OUT (C),H ED 61 + // OUT (C),L ED 69 + // OTDR ED BB + // OTIR ED B3 + // OUTD ED AB + // OUTI ED A3 + else if( opcode == 0xED && (opcode2 == 0x79 || opcode2 == 0x41 || opcode2 == 0x49 || opcode2 == 0x51 || opcode2 == 0x59 || opcode2 == 0x61 || opcode2 == 0x69 || opcode2 == 0xBB || opcode2 == 0xB3 || opcode2 == 0xAB || opcode2 == 0xA3) ) { - SPI_SEND32( (Z80CPU.bc.uint16_value << 16) | ((opcode2 == 0x79 ? Z80CPU.af.uint8_values.at_1 : - opcode2 == 0x41 ? Z80CPU.bc.uint8_values.at_1 : - opcode2 == 0x49 ? Z80CPU.bc.uint8_values.at_0 : - opcode2 == 0x51 ? Z80CPU.de.uint8_values.at_1 : - opcode2 == 0x59 ? Z80CPU.de.uint8_values.at_0 : - opcode2 == 0x61 ? Z80CPU.hl.uint8_values.at_1 : - opcode2 == 0x69 ? Z80CPU.hl.uint8_values.at_0 : - isVirtualROM((Z80CPU.hl.uint16_value)) ? readVirtualROM((Z80CPU.hl.uint16_value)) : readVirtualRAM((Z80CPU.hl.uint16_value))) << 8) - | CPLD_CMD_WRITEIO_ADDR); - Z80Ctrl->ioWriteAhead = 1; + uint16_t data = ((opcode2 == 0x79 ? Z80CPU.af.uint8_values.at_1 : + opcode2 == 0x41 ? Z80CPU.bc.uint8_values.at_1 : + opcode2 == 0x49 ? Z80CPU.bc.uint8_values.at_0 : + opcode2 == 0x51 ? Z80CPU.de.uint8_values.at_1 : + opcode2 == 0x59 ? Z80CPU.de.uint8_values.at_0 : + opcode2 == 0x61 ? Z80CPU.hl.uint8_values.at_1 : + opcode2 == 0x69 ? Z80CPU.hl.uint8_values.at_0 : + isVirtualROM((Z80CPU.hl.uint16_value)) ? readVirtualROM((Z80CPU.hl.uint16_value)) : readVirtualRAM((Z80CPU.hl.uint16_value))) << 8) | CPLD_CMD_WRITEIO_ADDR; + + if(isPhysicalIO(Z80CPU.bc.uint16_value)) + { + if((Z80CPU.bc.uint16_value&0x00ff) < 8) + { + SPI_SEND_16(data + (Z80CPU.bc.uint16_value&0x00ff)); + } else + { + SPI_SEND_32(Z80CPU.bc.uint16_value, data); + } + Z80Ctrl->ioWriteAhead = 1; + } } - // OUT (N), A + + // OUT (N),A D3 XX else if(opcode == 0xD3) { - SPI_SEND32( ((Z80CPU.bc.uint16_value & 0xff00) | opcode2) << 16 | (Z80CPU.af.uint8_values.at_1 << 8) | CPLD_CMD_WRITEIO_ADDR); - Z80Ctrl->ioWriteAhead = 1; + if(isPhysicalIO(((Z80CPU.bc.uint16_value & 0xff00) | opcode2))) + { + if(opcode2 < 8) + { + SPI_SEND_16((Z80CPU.af.uint8_values.at_1 << 8) | (CPLD_CMD_WRITEIO_ADDR + opcode2)); + } else + { + SPI_SEND_32(((Z80CPU.bc.uint16_value & 0xff00) | opcode2), (Z80CPU.af.uint8_values.at_1 << 8) | CPLD_CMD_WRITEIO_ADDR); + } + Z80Ctrl->ioWriteAhead = 1; + } } + + return(Z80Ctrl->ioReadAhead | Z80Ctrl->ioWriteAhead); } //------------------------------------------------------------------------------------------------------------------------------- @@ -522,10 +455,14 @@ static zuint8 z80_read(void *context, zuint16 address) Z_UNUSED(context) // Only read if the address is in physical RAM. + #if(TARGET_HOST_PCW == 0) if(isPhysical(address)) + #else + if(isPhysicalHW(address)) + #endif { // Commence cycle to retrieve the data from Real RAM. - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); + SPI_SEND_32(address, CPLD_CMD_READ_ADDR); // Decode address to action any host specific memory map changes. decodeMemoryMapSetup(address, 0, 0, true); @@ -543,6 +480,7 @@ static zuint8 z80_read(void *context, zuint16 address) data = readVirtual(address, 0); } + #if (TARGET_HOST_MZ80A == 1) // Keyport data? Store. if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 0) { @@ -554,8 +492,8 @@ static zuint8 z80_read(void *context, zuint16 address) if((Z80Ctrl->keyportStrobe & 0x0f) == 8 && (data & 0x1D) != 0x1D) { Z80Ctrl->keyportHotKey = (data & 0x01) == 0 ? HOTKEY_ORIGINAL : - (data & 0x04) == 0 ? HOTKEY_RFS80 : - (data & 0x08) == 0 ? HOTKEY_RFS40 : + (data & 0x04) == 0 ? HOTKEY_RFS40 : + (data & 0x08) == 0 ? HOTKEY_RFS80 : (data & 0x10) == 0 ? HOTKEY_LINUX : 0x00; Z80Ctrl->keyportTrigger = Z80Ctrl->keyportHotKey; } else @@ -568,6 +506,27 @@ static zuint8 z80_read(void *context, zuint16 address) Z80Ctrl->keyportTrigger = 0; } } + #elif (TARGET_HOST_MZ700 == 1) + // Keyport data? Store. + if(isHW(address) && address == 0xE001 && (Z80Ctrl->keyportStrobe & 0x0f) == 8) + { + Z80Ctrl->keyportShiftCtrl = (data & 0x40) == 0 ? 0x01 : 0x00; + } else + if(isHW(address) && address == 0xE001 && Z80Ctrl->keyportShiftCtrl == 1) + { + if((Z80Ctrl->keyportStrobe & 0x0f) == 5 && (data & 0xF0) != 0xF0) + { + Z80Ctrl->keyportHotKey = (data & 0x80) == 0 ? HOTKEY_ORIGINAL : + (data & 0x40) == 0 ? HOTKEY_RFS40 : + (data & 0x20) == 0 ? HOTKEY_TZFS : + (data & 0x10) == 0 ? HOTKEY_LINUX : 0x00; + Z80Ctrl->keyportTrigger = Z80Ctrl->keyportHotKey; + } else + { + Z80Ctrl->keyportTrigger = 0; + } + } + #endif #if(DEBUG_ENABLED & 1) if(Z80Ctrl->debug >= 3) @@ -586,17 +545,27 @@ static void z80_write(void *context, zuint16 address, zuint8 data) // Locals. Z_UNUSED(context) + #if (TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) // To detect Hotkey presses, we need to store the keyboard strobe data and on keydata read. if(isHW(address) && address == 0xE000) { Z80Ctrl->keyportStrobe = data; } + #endif // Write to physical host? - if(isPhysical(address)) + if(Z80Ctrl->ioReadAhead == 2) + { + // Write-thru to virtual memory if we update real memory. + if(isPhysicalRAM(address)) + writeVirtualRAM(address, data); + Z80Ctrl->ioReadAhead = 0; + } + else if(isPhysical(address)) { // Commence cycle to write the data to real RAM. - SPI_SEND32((uint32_t)address << 16 | data << 8 | CPLD_CMD_WRITE_ADDR); + SPI_SEND_32(address, data << 8 | CPLD_CMD_WRITE_ADDR); + //SPI_SEND32(address << 16 | data << 8 | CPLD_CMD_WRITE_ADDR); // Write-thru to virtual memory if we update real memory. if(isPhysicalRAM(address)) @@ -639,11 +608,17 @@ static void z80_write(void *context, zuint16 address, zuint8 data) static zuint8 z80_fetch_opcode(void *context, zuint16 address) { // Locals. - zuint8 opcode = 0x00; - volatile uint32_t idx; // Leave as volatile otherwise optimiser will optimise out the delay code. + zuint8 opcode = 0x00; + #if(TARGET_HOST_PCW == 1) + static zuint8 opcodeNxt = 0x00; + #endif + volatile uint32_t idx; // Leave as volatile otherwise optimiser will optimise out the delay code. Z_UNUSED(context) // Normally only opcode fetches occur in RAM but allow any physical address as it could be a Z80 programming trick. + + // PCW machines operate with write-thru < 128K and virtual >= 128K +#if(TARGET_HOST_PCW == 0) #if(TARGET_HOST_MZ80A == 1) // MZ-80A floppy disk controller uses address 0xF3FE/0xF7FE to direct program flow according to READY state of the // MB8866 controller. @@ -653,37 +628,97 @@ static zuint8 z80_fetch_opcode(void *context, zuint16 address) #endif { // Commence cycle to fetch the opcode from potentially Real RAM albeit it could be any physical hardware. - SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_FETCH_ADDR); + SPI_SEND_32(address, CPLD_CMD_FETCH_ADDR); + + // Set up to bypass governor if this is floppy access. + if(address == 0xF3FE) + Z80Ctrl->governorSkip = INSTRUCTION_GOVERNOR_IO_SKIP; + + // Wait for the data and retrieve. while(CPLD_READY() == 0); opcode = z80io_PRL_Read(); - - // Pause until the Last T-State is detected. - //while(CPLD_LAST_TSTATE() == 0); } else +#endif + + #if(TARGET_HOST_PCW == 1) // Virtual fetches only occur in memory as we are not emulating original hardware. if(isVirtualMemory(address)) { - // Delay loop is to govern execution speed to the same as the original host. Timing is primarily based on the main opcode - // fetch so long as the additional opcode parameter and read/write take less time than original host. + // Read the opcode and operand, operand needed for lookahead decisions. if(isVirtualROM(address)) { - opcode = readVirtualROM(address); - for(idx=0; idx < Z80Ctrl->cpuGovernorDelayROM; idx++); + opcode = readVirtualROM(address); + opcodeNxt = readVirtualROM((address+1)); } else { - opcode = readVirtualRAM(address); - for(idx=0; idx < Z80Ctrl->cpuGovernorDelayRAM; idx++); + opcode = readVirtualRAM(address); + opcodeNxt = readVirtualRAM((address+1)); + } + + // Multibyte opcodes, if triggered by an initial I/O read/write ahead operation, return immediately with the opcode. + if(Z80Ctrl->ioReadAhead == 0 && Z80Ctrl->ioWriteAhead == 0) + { + // Check if this operation is I/O or known memory I/O so we can look ahead to optimise sending request to CPLD. + if(!lookAhead(address, opcode, opcodeNxt) && !Z80Ctrl->governorSkip) + { + // Delay loop is to govern execution speed to the same as the original host. Timing is primarily based on the main opcode + // fetch so long as the additional opcode parameter and read/write take less time than original host. + if(isVirtualROM(address)) + { + // opcode = readVirtualROM(address); + for(idx=0; idx < Z80Ctrl->cpuGovernorDelayROM; idx++); + } else + { + // opcode = readVirtualRAM(address); + for(idx=0; idx < Z80Ctrl->cpuGovernorDelayRAM; idx++); + } + } else + { + // If skip is active, we bypass the delay governor in order to catch up on time wasted with the I/O overhead of the SSD202. + if(Z80Ctrl->governorSkip) + { + Z80Ctrl->governorSkip--; + } else + { + Z80Ctrl->governorSkip = INSTRUCTION_GOVERNOR_IO_SKIP; + } + } + } else + { + Z80Ctrl->ioReadAhead = 0; + Z80Ctrl->ioWriteAhead = 0; } } - - // Check if this operation is I/O or known memory I/O so we can look ahead to optimise sending request to CPLD. - lookAhead(address, opcode, isVirtualROM((address+1)) ? readVirtualROM((address+1)) : readVirtualRAM((address+1))); + #else + // Apply delay if required to match emulated CPU to host speed. + if(isVirtualROM(address)) + { + opcode = readVirtualROM(address); + if(Z80Ctrl->governorSkip) + Z80Ctrl->governorSkip--; + else + for(idx=0; idx < Z80Ctrl->cpuGovernorDelayROM; idx++); + } else + if(isVirtualRAM(address)) + { + opcode = readVirtualRAM(address); + if(Z80Ctrl->governorSkip) + Z80Ctrl->governorSkip--; + else + for(idx=0; idx < Z80Ctrl->cpuGovernorDelayRAM; idx++); + } + #endif #if(DEBUG_ENABLED & 1) if(Z80Ctrl->debug >= 3) { - if(address < 0xF036 || address > 0xF197) - pr_info("Fetch:%04x,%02x,%d\n", address, opcode, CPLD_Z80_INT()); + // if(address < 0xF036 || address > 0xF197) + if(address >= 0x0000) + { + pr_info("Fetch:%04x,%02x,%d,%d,%d\n", address, opcode, Z80Ctrl->ioReadAhead, Z80Ctrl->ioWriteAhead, CPLD_Z80_INT()); + udelay(2000); + udelay(1000); + } // If max level, add delay so that the kernel log doesnt overflow. if(Z80Ctrl->debug >= 15) @@ -711,15 +746,6 @@ static zuint8 z80_fetch(void *context, zuint16 address) // Given the limitation of the SigmaStar I/O, it isnt possible to fetch all code from ROM real time, it is therefore cached // and read from cache. data = isPhysicalROM(address) ? readVirtualROM(address) : readVirtualRAM(address); - - // Commence cycle to retrieve the data from Real RAM. - //SPI_SEND32((uint32_t)address << 16 | CPLD_CMD_READ_ADDR); - - //while(CPLD_READY() == 0); - //data = READ_CPLD_DATA_IN(); - - // Pause until the Last T-State is detected. - //while(CPLD_LAST_TSTATE() == 0); } else if(isVirtualMemory(address)) { @@ -739,6 +765,7 @@ static zuint8 z80_fetch(void *context, zuint16 address) { if(address < 0xF036 || address > 0xF197) pr_info("FetchB:%04x,%02x,%d\n", address, data, CPLD_Z80_INT()); + udelay(2000); } #endif return(data); @@ -756,15 +783,26 @@ static zuint8 z80_in(void *context, zuint16 port) // Physical port go direct to hardware to retrieve value. if(isPhysicalIO(port)) { + #if(TARGET_HOST_PCW == 1) if(Z80Ctrl->ioReadAhead == 0) { // Commence cycle to retrieve the value from the I/O port. Port contains the 16bit BC value. - SPI_SEND32((uint32_t)port << 16 | CPLD_CMD_READIO_ADDR); - - // Whilst waiting for the CPLD, we now determine if this is a memory management port and update the memory page if required. - decodeMemoryMapSetup(port, 0, 1, true); + SPI_SEND_32(port, CPLD_CMD_READIO_ADDR); } - Z80Ctrl->ioReadAhead = 0; + + // Whilst waiting for the CPLD, we now determine if this is a memory management port and update the memory page if required. + decodeMemoryMapSetup(port, 0, 1, true); + + if(Z80Ctrl->ioReadAhead != 2) + Z80Ctrl->ioReadAhead = 0; + #else + // Commence cycle to retrieve the value from the I/O port. Port contains the 16bit BC value. + SPI_SEND_32(port, CPLD_CMD_READIO_ADDR); + Z80Ctrl->governorSkip = INSTRUCTION_GOVERNOR_IO_SKIP; + + // Whilst waiting for the CPLD, we now determine if this is a memory management port and update the memory page if required. + decodeMemoryMapSetup(port, 0, 1, true); + #endif // Finally ensure the data from the port is ready and retrieve it. while(CPLD_READY() == 0); @@ -778,8 +816,9 @@ static zuint8 z80_in(void *context, zuint16 port) } #if(DEBUG_ENABLED & 1) - if(Z80Ctrl->debug >= 3) pr_info("z80_in:0x%x, 0x%x\n", port, value); + if(Z80Ctrl->debug >= 2) pr_info("z80_in:0x%x, 0x%x\n", port, value); #endif + // pr_info("z80_in:0x%x, 0x%x\n", port, value); return(value); } @@ -791,24 +830,30 @@ static zuint8 z80_in(void *context, zuint16 port) static void z80_out(void *context, zuint16 port, zuint8 value) { // Locals. - #if(TARGET_HOST_MZ2000 == 1) - uint32_t idx; - #endif Z_UNUSED(context) // Physical port go direct to hardware to retrieve value. if(isPhysicalIO(port)) { + #if(TARGET_HOST_PCW == 1) // If byte has already been written during the fetch phase, skip. if(Z80Ctrl->ioWriteAhead == 0) { // Commence cycle to write the value to the I/O port. Port contains the 16bit BC value. - SPI_SEND32((uint32_t)port << 16 | value << 8 | CPLD_CMD_WRITEIO_ADDR); + SPI_SEND_32(port, value << 8 | CPLD_CMD_WRITEIO_ADDR); } Z80Ctrl->ioWriteAhead = 0; // Decode address to action any host specific memory map changes. decodeMemoryMapSetup(port, value, 1, false); + #else + // Commence cycle to write the value to the I/O port. Port contains the 16bit BC value. + SPI_SEND_32(port, value << 8 | CPLD_CMD_WRITEIO_ADDR); + Z80Ctrl->governorSkip = INSTRUCTION_GOVERNOR_IO_SKIP; + + // Decode address to action any host specific memory map changes. + decodeMemoryMapSetup(port, value, 1, false); + #endif } else if(isVirtualIO(port)) { @@ -817,8 +862,9 @@ static void z80_out(void *context, zuint16 port, zuint8 value) } #if(DEBUG_ENABLED & 1) - if(Z80Ctrl->debug >= 3) pr_info("z80_out:0x%x, 0x%x\n", port, value); + if(Z80Ctrl->debug >= 2) pr_info("z80_out:0x%x, 0x%x\n", port, value); #endif + // pr_info("z80_out:0x%x, 0x%x\n", port, value); } // NOP - No Operation method. This instruction is used for timing, padding out an application or during @@ -834,7 +880,7 @@ static zuint8 z80_nop(void *context, zuint16 address) { // If autorefresh is not enabled, send a single refresh request. if(Z80Ctrl->refreshDRAM == 0) - SPI_SEND8(CPLD_CMD_REFRESH); + SPI_SEND_32(0x0000, CPLD_CMD_REFRESH); } return 0x00; } @@ -848,7 +894,7 @@ static void z80_halt(void *context, zboolean state) // Inform CPLD of halt state. pr_info("z80_halt\n"); - SPI_SEND8(CPLD_CMD_HALT); + SPI_SEND_32(0x0000, CPLD_CMD_HALT); Z80CPU.cycles = Z80_MAXIMUM_CYCLES; } @@ -856,36 +902,48 @@ static void z80_halt(void *context, zboolean state) static zuint8 z80_context(void *context, zuint16 address) { Z_UNUSED(context) - pr_info("z80_context\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_context\n"); + #endif return 0x00; } static zuint8 z80_nmia(void *context, zuint16 address) { Z_UNUSED(context) - pr_info("z80_nmia\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_nmia\n"); + #endif return 0x00; } static zuint8 z80_inta(void *context, zuint16 address) { Z_UNUSED(context) - //pr_info("z80_inta\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_inta\n"); + #endif return 0x00; } static zuint8 z80_intFetch(void *context, zuint16 address) { Z_UNUSED(context) - pr_info("z80_int_fetch\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_int_fetch\n"); + #endif return 0x00; } static void z80_ldia(void *context) { Z_UNUSED(context) - pr_info("z80_ldia\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_ldia\n"); + #endif } static void z80_ldra(void *context) { Z_UNUSED(context) - pr_info("z80_ldra\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 2) pr_info("z80_ldra\n"); + #endif } static void z80_reti(void *context) { @@ -907,12 +965,16 @@ static void z80_reti(void *context) static void z80_retn(void *context) { Z_UNUSED(context) - pr_info("z80_retn\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 3) pr_info("z80_retn\n"); + #endif } static zuint8 z80_illegal(void *context, zuint8 opcode) { Z_UNUSED(context) - pr_info("z80_illegal\n"); + #if(DEBUG_ENABLED & 1) + if(Z80Ctrl->debug >= 3) pr_info("z80_illegal\n"); + #endif return 0x00; } @@ -940,7 +1002,7 @@ int thread_z80(void * thread_nr) while(!kthread_should_stop()) { // Run the Z80 emulation if enabled. - if(canRun) z80_run(&Z80CPU, 100); + if(canRun) z80_run(&Z80CPU, 10); // Reset pressed? if(CPLD_RESET()) @@ -959,14 +1021,24 @@ int thread_z80(void * thread_nr) if(Z80RunMode == Z80_RUNNING) canRun=1; else canRun=0; mutex_unlock(&Z80RunModeMutex); + #if (TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) // Hotkey pressed? Bring up user menu. if(Z80Ctrl->keyportTrigger != 0x00 && Z80Ctrl->keyportTriggerLast == 0) { z80menu(); + + // Send signal to arbiter to change run mode. sendSignal(Z80Ctrl->arbTask, SIGUSR1); Z80Ctrl->keyportShiftCtrl = 0; + + // Suspend processing until arbiter sets up new environment. + mutex_lock(&Z80RunModeMutex); + Z80RunMode = Z80_STOPPED; + canRun = 0; + mutex_unlock(&Z80RunModeMutex); } Z80Ctrl->keyportTriggerLast = Z80Ctrl->keyportTrigger; + #endif } } @@ -1231,7 +1303,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint32_t dispaddr, uint8_t di { if(pnt+i < endAddr) { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); + SPI_SEND_32((uint16_t)(pnt+i), CPLD_CMD_READ_ADDR); while(CPLD_READY() == 0); data = z80io_PRL_Read(); pr_info(KERN_CONT "%02X", data); @@ -1249,7 +1321,7 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint32_t dispaddr, uint8_t di // print single ascii char for (i=0; i < displayWidth; i++) { - SPI_SEND32((uint16_t)(pnt+i) << 16 | CPLD_CMD_READ_ADDR); + SPI_SEND_32((uint16_t)(pnt+i), CPLD_CMD_READ_ADDR); while(CPLD_READY() == 0); c = (char)z80io_PRL_Read(); if ((pnt+i < endAddr) && (c >= ' ') && (c <= '~')) @@ -1280,7 +1352,6 @@ int memoryDump(uint32_t memaddr, uint32_t memsize, uint32_t dispaddr, uint8_t di void setupMemory(enum Z80_MEMORY_PROFILE mode) { // Locals. - uint32_t idx; // Check to see if the memory mode page has been allocated for current mode. if(Z80Ctrl->page[Z80Ctrl->memoryMode] == NULL) @@ -1294,169 +1365,45 @@ void setupMemory(enum Z80_MEMORY_PROFILE mode) } } - // Setup default mode according to run mode, ie. Physical run or Virtual run. - // - if(mode == USE_PHYSICAL_RAM) - { - #if(TARGET_HOST_MZ700 == 1) - #endif - #if(TARGET_HOST_MZ2000 == 1) - // 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); - // } - } - #endif - #if(TARGET_HOST_MZ80A == 1) - // 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); - } - } - #endif - 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) - { - #if(TARGET_HOST_MZ2000 == 1) - // 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, (Z80Ctrl->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 = 1; - #endif - #if(TARGET_HOST_MZ80A == 1) - // 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 = 0; - #endif - } - // Call driver specific methods to change default memory map per device requirements. #if(TARGET_HOST_MZ80A == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ80A) + mz80aSetupMemory(mode); + else + #endif + #if(TARGET_HOST_MZ700 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ700) + mz700SetupMemory(mode); + else + #endif + #if(TARGET_HOST_MZ2000 == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_MZ2000) + mz2000SetupMemory(mode); + else + #endif + // RFS board only works in an MZ-80A at present. + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_RFS) rfsSetupMemory(mode); else #endif + // tranZPUter operates in all supported Sharp machines. + #if(TARGET_HOST_PCW == 0) if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU) tzpuSetupMemory(mode); else + #endif + #if(TARGET_HOST_PCW == 1) + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_PCW) + pcwSetupMemory(mode); + else + #endif { - // 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(TARGET_HOST_MZ700 == 1) - if(idx >= 0x0000 && idx < 0x1000) - #endif - #if(TARGET_HOST_MZ80A == 1) - if((idx >= 0x0000 && idx < 0x1000) || (idx >= 0xF000 && idx < 0x10000)) - #endif - #if(TARGET_HOST_MZ2000 == 1) - if(idx >= 0x0000 && idx < 0x8000) - #endif - { - 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; - } - } } - // Enable autorefresh if refreshDRAM is set. - SPI_SEND8(Z80Ctrl->refreshDRAM == 1 ? CPLD_CMD_SET_AUTO_REFRESH : CPLD_CMD_CLEAR_AUTO_REFRESH); + // Enable autorefresh if refreshDRAM is set. 0 = disable, 1 = enable, > 1 = ignore and use CPLD default. + if(Z80Ctrl->refreshDRAM < 2) + SPI_SEND_32(0x0000, Z80Ctrl->refreshDRAM == 1 ? CPLD_CMD_SET_AUTO_REFRESH : CPLD_CMD_CLEAR_AUTO_REFRESH); // Inhibit mode disabled. Z80Ctrl->inhibitMode = 0; @@ -1507,8 +1454,8 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) // Command to power on and start the Z80 CPU. case IOCTL_CMD_Z80_START: mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_RUNNING; mutex_unlock(&Z80RunModeMutex); - z80_power(&Z80CPU, TRUE); + #if(DEBUG_ENABLED & 1) if(Z80Ctrl->debug >= 3) pr_info("Z80 started.\n"); #endif @@ -1539,7 +1486,7 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) resetZ80(); - mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); + mutex_lock(&Z80RunModeMutex); Z80RunMode = Z80_RUNNING; mutex_unlock(&Z80RunModeMutex); #if(DEBUG_ENABLED & 1) if(Z80Ctrl->debug >= 3) pr_info("Z80 Reset.\n"); #endif @@ -1590,7 +1537,7 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) // Copy virtual memory to host DRAM. for(idx=0x1000; idx < 0xD000; idx++) { - SPI_SEND32((uint32_t)idx << 16 | Z80Ctrl->ram[idx] << 8 | CPLD_CMD_WRITE_ADDR); + SPI_SEND_32(idx, Z80Ctrl->ram[idx] << 8 | CPLD_CMD_WRITE_ADDR); } mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); @@ -1686,17 +1633,19 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) { if(Z80Ctrl->virtualDevice[idx] == ioctlCmd.vdev.device) { - //pr_info("Virtual Device already installed.\n"); + pr_info("Virtual Device already installed.\n"); break; } } if(idx < Z80Ctrl->virtualDeviceCnt) break; - #if(TARGET_HOST_MZ700 == 1 || TARGET_HOST_MZ2000 == 1) + #if(TARGET_HOST_MZ80A == 0 && TARGET_HOST_MZ700 == 0) if(ioctlCmd.vdev.device & VIRTUAL_DEVICE_RFS) - pr_info("RFS Board currently supported on MZ-80A Host only.\n"); - break; + { + pr_info("RFS Board currently supported on MZ-80A/MZ-700 Hosts only.\n"); + break; + } #endif // Stop the CPU prior to adding virtual device. @@ -1709,6 +1658,14 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) switch(ioctlCmd.vdev.device) { #if(TARGET_HOST_MZ80A == 1) + case VIRTUAL_DEVICE_MZ80A: + Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = ioctlCmd.vdev.device; + Z80Ctrl->virtualDeviceBitMap |= ioctlCmd.vdev.device; + mz80aInit(0); + break; + #endif + + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) case VIRTUAL_DEVICE_RFS40: case VIRTUAL_DEVICE_RFS80: Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = ioctlCmd.vdev.device; @@ -1717,23 +1674,51 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) break; #endif + #if(TARGET_HOST_MZ700 == 1) + case VIRTUAL_DEVICE_MZ700: + Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = ioctlCmd.vdev.device; + Z80Ctrl->virtualDeviceBitMap |= ioctlCmd.vdev.device; + mz700Init(0); + break; + #endif + + #if(TARGET_HOST_MZ2000 == 1) + case VIRTUAL_DEVICE_MZ2000: + Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = ioctlCmd.vdev.device; + Z80Ctrl->virtualDeviceBitMap |= ioctlCmd.vdev.device; + mz2000Init(0); + break; + #endif + + #if(TARGET_HOST_PCW == 0) case VIRTUAL_DEVICE_TZPU: Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = VIRTUAL_DEVICE_TZPU; Z80Ctrl->virtualDeviceBitMap |= VIRTUAL_DEVICE_TZPU; tzpuInit(); break; + #endif + + #if(TARGET_HOST_PCW == 1) + case VIRTUAL_DEVICE_PCW: + Z80Ctrl->virtualDevice[Z80Ctrl->virtualDeviceCnt++] = VIRTUAL_DEVICE_PCW; + Z80Ctrl->virtualDeviceBitMap |= VIRTUAL_DEVICE_PCW; + + #ifdef TARGET_HOST_PCW8XXX + pcwInit(0); + #else + pcwInit(1); + #endif + break; + #endif case VIRTUAL_DEVICE_NONE: default: break; } - // Re-initialise the memory map to reflect device removal. - setupMemory(Z80Ctrl->defaultPageMode); - // Z80 can continue. mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); - //pr_info("Virtual device added.\n"); + pr_info("Virtual device added.\n"); break; // Command to remove a device from the Z80 configuration. @@ -1765,6 +1750,13 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) switch(ioctlCmd.vdev.device) { #if(TARGET_HOST_MZ80A == 1) + case VIRTUAL_DEVICE_MZ80A: + Z80Ctrl->virtualDeviceBitMap &= ~ioctlCmd.vdev.device; + mz80aRemove(); + break; + #endif + + #if(TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) case VIRTUAL_DEVICE_RFS40: case VIRTUAL_DEVICE_RFS80: Z80Ctrl->virtualDeviceBitMap &= ~ioctlCmd.vdev.device; @@ -1772,19 +1764,39 @@ static long int z80drv_ioctl(struct file *file, unsigned cmd, unsigned long arg) break; #endif + #if(TARGET_HOST_MZ700 == 1) + case VIRTUAL_DEVICE_MZ700: + Z80Ctrl->virtualDeviceBitMap &= ~ioctlCmd.vdev.device; + mz700Remove(); + break; + #endif + + #if(TARGET_HOST_MZ2000 == 1) + case VIRTUAL_DEVICE_MZ2000: + Z80Ctrl->virtualDeviceBitMap &= ~ioctlCmd.vdev.device; + mz2000Remove(); + break; + #endif + + #if(TARGET_HOST_PCW == 0) case VIRTUAL_DEVICE_TZPU: Z80Ctrl->virtualDeviceBitMap &= ~VIRTUAL_DEVICE_TZPU; tzpuRemove(); break; + #endif + + #if(TARGET_HOST_PCW == 1) + case VIRTUAL_DEVICE_PCW: + Z80Ctrl->virtualDeviceBitMap &= ~VIRTUAL_DEVICE_PCW; + pcwRemove(); + break; + #endif case VIRTUAL_DEVICE_NONE: default: break; } - // Re-initialise the memory map to reflect device removal. - setupMemory(Z80Ctrl->defaultPageMode); - // Z80 can continue. mutex_lock(&Z80RunModeMutex); Z80RunMode = currentRunMode; mutex_unlock(&Z80RunModeMutex); //pr_info("Device removed\n"); @@ -1983,78 +1995,7 @@ static int __init ModuleInit(void) // Initialise the hardware to host interface. z80io_init(); - - // 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(TARGET_HOST_MZ700 == 1) - 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; - } - #endif - #if(TARGET_HOST_MZ2000 == 1) - 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 - { - if(idx >= 0x0000 && idx < 0x8000) - Z80Ctrl->ram[idx+0x8000] = 0x00; - else - Z80Ctrl->ram[idx] = 0x00; - } - #endif - #if(TARGET_HOST_MZ80A == 1) - 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; - } - #endif - } - - // 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 - - #if(TARGET_HOST_MZ2000 == 1) - Z80Ctrl->lowMemorySwap = 1; - #endif - - #if(TARGET_HOST_MZ80A == 1) - Z80Ctrl->memSwitch = 0; - #endif + SPI_SET_FRAME_SIZE(); // Initialise the virtual device array. for(idx=0; idx < MAX_VIRTUAL_DEVICES; idx++) @@ -2071,6 +2012,7 @@ static int __init ModuleInit(void) // Setup the governor delay, it is the delay per opcode fetch to restrict the Z80 CPU to a given speed. Z80Ctrl->cpuGovernorDelayROM = ROM_DELAY_NORMAL; Z80Ctrl->cpuGovernorDelayRAM = RAM_DELAY_NORMAL; + Z80Ctrl->governorSkip = 0; // Setup the default Page Mode. This is needed if an event such as a reset occurs which needs to return the page and iotable back to default. Z80Ctrl->defaultPageMode = USE_VIRTUAL_RAM; @@ -2090,16 +2032,18 @@ static int __init ModuleInit(void) Z80Ctrl->ioReadAhead = 0; Z80Ctrl->ioWriteAhead = 0; + #if (TARGET_HOST_MZ80A == 1 || TARGET_HOST_MZ700 == 1) // Initialse hotkey detection variables. Z80Ctrl->keyportStrobe = 0x00; Z80Ctrl->keyportShiftCtrl = 0x00; Z80Ctrl->keyportHotKey = 0x00; Z80Ctrl->keyportTrigger = 0x00; Z80Ctrl->keyportTriggerLast = 0x00; + #endif - // PC to start and power on the CPU + // PC to start. Z80_PC(Z80CPU) = 0; - z80_power(&Z80CPU, TRUE); + //z80_power(&Z80CPU, TRUE); // Initialise Debug logic if compile time enabled. #if(DEBUG_ENABLED != 0) diff --git a/software/FusionX/src/z80drv/MZ80A/z80driver.h b/software/FusionX/src/z80drv/src/z80driver.h similarity index 78% rename from software/FusionX/src/z80drv/MZ80A/z80driver.h rename to software/FusionX/src/z80drv/src/z80driver.h index c7fe7062b..2c2f3a0cc 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80driver.h +++ b/software/FusionX/src/z80drv/src/z80driver.h @@ -35,16 +35,41 @@ #ifndef 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. #define DRIVER_LICENSE "GPL" #define DRIVER_AUTHOR "Philip D Smart" #define DRIVER_DESCRIPTION "Z80 CPU Emulator and Hardware Interface Driver" -#define DRIVER_VERSION "v1.3" -#define DRIVER_VERSION_DATE "Feb 2023" +#define DRIVER_VERSION "v1.4" +#define DRIVER_VERSION_DATE "Apr 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_RAM_SIZE (65536 * 32) // Sized to maximum Kernel contiguous allocation size, 2M. #define Z80_MEMORY_PAGE_SIZE 16 @@ -69,13 +94,14 @@ #define MEMORY_TYPE_VIRTUAL_ROM 0x04000000 #define MEMORY_TYPE_VIRTUAL_RAM_RO 0x02000000 #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_VIRTUAL_HW 0x40000000 // Hotkeys handled. #define HOTKEY_ORIGINAL 0xE8 -#define HOTKEY_RFS80 0xE9 -#define HOTKEY_RFS40 0xEA +#define HOTKEY_RFS40 0xE9 +#define HOTKEY_RFS80 0xEA #define HOTKEY_TZFS 0xEB #define HOTKEY_LINUX 0xEC @@ -100,22 +126,42 @@ // 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 +#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 +#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_7MHZ 7000000 #define INSTRUCTION_EQUIV_FREQ_14MHZ 14000000 @@ -124,6 +170,7 @@ #define INSTRUCTION_EQUIV_FREQ_112MHZ 112000000 #define INSTRUCTION_EQUIV_FREQ_224MHZ 224000000 #define INSTRUCTION_EQUIV_FREQ_448MHZ 448000000 +#define INSTRUCTION_GOVERNOR_IO_SKIP 10 enum Z80_INSTRUCTION_DELAY { 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_X32 = INSTRUCTION_DELAY_RAM_112MHZ, 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_X2 = INSTRUCTION_EQUIV_FREQ_7MHZ, CPU_FREQUENCY_X4 = INSTRUCTION_EQUIV_FREQ_14MHZ, @@ -155,22 +202,43 @@ enum Z80_INSTRUCTION_DELAY { // MZ-2000 #if(TARGET_HOST_MZ2000 == 1) -#define INSTRUCTION_DELAY_ROM_4MHZ 243 -#define INSTRUCTION_DELAY_ROM_8MHZ 122 -#define INSTRUCTION_DELAY_ROM_16MHZ 61 -#define INSTRUCTION_DELAY_ROM_32MHZ 30 -#define INSTRUCTION_DELAY_ROM_64MHZ 15 -#define INSTRUCTION_DELAY_ROM_128MHZ 7 -#define INSTRUCTION_DELAY_ROM_256MHZ 3 -#define INSTRUCTION_DELAY_ROM_512MHZ 1 -#define INSTRUCTION_DELAY_RAM_4MHZ 218 -#define INSTRUCTION_DELAY_RAM_8MHZ 112 -#define INSTRUCTION_DELAY_RAM_16MHZ 56 -#define INSTRUCTION_DELAY_RAM_32MHZ 28 -#define INSTRUCTION_DELAY_RAM_64MHZ 14 -#define INSTRUCTION_DELAY_RAM_128MHZ 7 -#define INSTRUCTION_DELAY_RAM_256MHZ 3 -#define INSTRUCTION_DELAY_RAM_512MHZ 1 + +#if(DEBUG_ENABLED > 0) + #define INSTRUCTION_DELAY_ROM_4MHZ 213 + #define INSTRUCTION_DELAY_ROM_8MHZ 109 + #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_256MHZ 3 + #define INSTRUCTION_DELAY_ROM_512MHZ 1 + #define INSTRUCTION_DELAY_RAM_4MHZ 212 + #define INSTRUCTION_DELAY_RAM_8MHZ 106 + #define INSTRUCTION_DELAY_RAM_16MHZ 53 + #define INSTRUCTION_DELAY_RAM_32MHZ 26 + #define INSTRUCTION_DELAY_RAM_64MHZ 13 + #define INSTRUCTION_DELAY_RAM_128MHZ 7 + #define INSTRUCTION_DELAY_RAM_256MHZ 3 + #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_8MHZ 8000000 #define INSTRUCTION_EQUIV_FREQ_16MHZ 16000000 @@ -179,6 +247,7 @@ enum Z80_INSTRUCTION_DELAY { #define INSTRUCTION_EQUIV_FREQ_128MHZ 128000000 #define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 #define INSTRUCTION_EQUIV_FREQ_512MHZ 512000000 +#define INSTRUCTION_GOVERNOR_IO_SKIP 4 enum Z80_INSTRUCTION_DELAY { 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_128MHZ 128000000 #define INSTRUCTION_EQUIV_FREQ_256MHZ 256000000 +#define INSTRUCTION_GOVERNOR_IO_SKIP 2 // Table of governor delays to be used to control run frequency, enum Z80_INSTRUCTION_DELAY { @@ -288,6 +358,82 @@ enum Z80_INSTRUCTION_DELAY { }; #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. #define IOCTL_CMD_Z80_STOP 's' #define IOCTL_CMD_Z80_START 'S' @@ -426,9 +572,12 @@ enum Z80_INSTRUCTION_DELAY { }\ } #define resetZ80() {\ - if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\ - sendSignal(Z80Ctrl->ioTask, SIGUSR1); \ setupMemory(Z80Ctrl->defaultPageMode);\ + if(Z80Ctrl->virtualDeviceBitMap & VIRTUAL_DEVICE_TZPU)\ + {\ + sendSignal(Z80Ctrl->ioTask, SIGUSR1); \ + udelay(2000);\ + }\ z80_instant_reset(&Z80CPU);\ } @@ -460,10 +609,14 @@ enum Z80_MEMORY_PROFILE { }; enum VIRTUAL_DEVICE { 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_RFS80 = 0x02000000, VIRTUAL_DEVICE_RFS = 0x03000000, - VIRTUAL_DEVICE_TZPU = 0x04000000 + VIRTUAL_DEVICE_TZPU = 0x04000000, }; typedef struct { @@ -524,14 +677,6 @@ typedef struct { uint8_t ioReadAhead; 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. uint8_t keyportStrobe; 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. uint32_t cpuGovernorDelayROM; uint32_t cpuGovernorDelayRAM; + uint8_t governorSkip; // An I/O processor, running as a User Space daemon, can register to receive signals and events. struct task_struct *ioTask; diff --git a/software/FusionX/src/z80drv/MZ80A/z80io.c b/software/FusionX/src/z80drv/src/z80io.c similarity index 100% rename from software/FusionX/src/z80drv/MZ80A/z80io.c rename to software/FusionX/src/z80drv/src/z80io.c diff --git a/software/FusionX/src/z80drv/MZ80A/z80io.h b/software/FusionX/src/z80drv/src/z80io.h similarity index 79% rename from software/FusionX/src/z80drv/MZ80A/z80io.h rename to software/FusionX/src/z80drv/src/z80io.h index 641cea5ed..5639da632 100755 --- a/software/FusionX/src/z80drv/MZ80A/z80io.h +++ b/software/FusionX/src/z80drv/src/z80io.h @@ -82,6 +82,14 @@ #define CPLD_CMD_READIO_ADDR_P5 0x35 #define CPLD_CMD_READIO_ADDR_P6 0x36 #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_REFRESH 0x51 #define CPLD_CMD_SET_SIGROUP1 0xF0 @@ -130,7 +138,7 @@ #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_READY_ADDR 0x103C18 // GPIO12 #define PAD_Z80IO_LTSTATE_ADDR 0x103C30 // GPIO47 #define PAD_Z80IO_BUSRQ_ADDR 0x103C1A #define PAD_Z80IO_BUSACK_ADDR 0x103C1C @@ -386,8 +394,8 @@ #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_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4) -#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \ - MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ +#define SPI_SEND_8(_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); \ @@ -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_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; \ - MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \ 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);\ 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); \ @@ -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_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); \ +#define SPI_SEND_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \ + MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ + MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \ 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); \ @@ -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_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ } -#define SPI_SEND32i(_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); \ - pr_info("Stage 0");\ - while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\ - pr_info("Stage 1");\ +#define SPI_SEND_P_16(_d1_) { uint32_t timeout = MAX_CHECK_CNT; \ + MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \ + MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)_d1_); \ + 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); \ - pr_info("Stage 2");\ - timeout = MAX_CHECK_CNT; \ - while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \ - pr_info("Stage 3");\ + 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_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_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \ } - // while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; }; // read 2 byte #define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2)) // write 2 byte diff --git a/software/FusionX/src/z80drv/MZ80A/z80io_test.c b/software/FusionX/src/z80drv/src/z80io_test.c similarity index 98% rename from software/FusionX/src/z80drv/MZ80A/z80io_test.c rename to software/FusionX/src/z80drv/src/z80io_test.c index ac7346b57..afaeed03b 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80io_test.c +++ b/software/FusionX/src/z80drv/src/z80io_test.c @@ -54,7 +54,7 @@ uint8_t z80io_Z80_TestMemory(void) spinlock_t spinLock; 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); udelay(100); @@ -185,7 +185,7 @@ uint8_t z80io_Z80_TestMemory(void) // Read back the same byte. cmd = 0x10; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); while(CPLD_READY() == 0); result = READ_CPLD_DATA_IN(); @@ -223,7 +223,7 @@ uint8_t z80io_Z80_TestMemory(void) // Read back the same byte. cmd = 0x20; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); while(CPLD_READY() == 0); result = READ_CPLD_DATA_IN(); @@ -254,7 +254,7 @@ uint8_t z80io_Z80_TestMemory(void) } else { cmd = 0x11; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); } while(CPLD_READY() == 0); result = READ_CPLD_DATA_IN(); @@ -280,7 +280,7 @@ uint8_t z80io_Z80_TestMemory(void) } else { cmd = 0x21; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); } while(CPLD_READY() == 0); result = READ_CPLD_DATA_IN(); @@ -306,7 +306,7 @@ uint8_t z80io_Z80_TestMemory(void) } else { cmd = 0x19; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); } } for(idx=0; idx < iterations; idx++) @@ -322,7 +322,7 @@ uint8_t z80io_Z80_TestMemory(void) } else { cmd = 0x19; - SPI_SEND8(cmd); + SPI_SEND_8(cmd); } } } diff --git a/software/FusionX/src/z80drv/MZ2000/z80menu.c b/software/FusionX/src/z80drv/src/z80menu.c similarity index 100% rename from software/FusionX/src/z80drv/MZ2000/z80menu.c rename to software/FusionX/src/z80drv/src/z80menu.c diff --git a/software/FusionX/src/z80drv/MZ2000/z80menu.h b/software/FusionX/src/z80drv/src/z80menu.h similarity index 100% rename from software/FusionX/src/z80drv/MZ2000/z80menu.h rename to software/FusionX/src/z80drv/src/z80menu.h diff --git a/software/FusionX/src/z80drv/src/z80vhw_mz2000.c b/software/FusionX/src/z80drv/src/z80vhw_mz2000.c new file mode 100644 index 000000000..fb0c51c0d --- /dev/null +++ b/software/FusionX/src/z80drv/src/z80vhw_mz2000.c @@ -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 +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80io.h" + +#include +#include +#include +#include + +// 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; +} diff --git a/software/FusionX/src/z80drv/src/z80vhw_mz700.c b/software/FusionX/src/z80drv/src/z80vhw_mz700.c new file mode 100644 index 000000000..de229b19f --- /dev/null +++ b/software/FusionX/src/z80drv/src/z80vhw_mz700.c @@ -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 +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80io.h" + +#include +#include +#include +#include + +// 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 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; +} diff --git a/software/FusionX/src/z80drv/src/z80vhw_mz80a.c b/software/FusionX/src/z80drv/src/z80vhw_mz80a.c new file mode 100644 index 000000000..e9b80ab68 --- /dev/null +++ b/software/FusionX/src/z80drv/src/z80vhw_mz80a.c @@ -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 +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80io.h" + +#include +#include +#include +#include + +// 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; +} diff --git a/software/FusionX/src/z80drv/src/z80vhw_pcw.c b/software/FusionX/src/z80drv/src/z80vhw_pcw.c new file mode 100644 index 000000000..d10117d4b --- /dev/null +++ b/software/FusionX/src/z80drv/src/z80vhw_pcw.c @@ -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 +// +// 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 . +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z80io.h" + +#include +#include +#include +#include + +// 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; +} diff --git a/software/FusionX/src/z80drv/MZ80A/z80vhw_rfs.c b/software/FusionX/src/z80drv/src/z80vhw_rfs.c similarity index 94% rename from software/FusionX/src/z80drv/MZ80A/z80vhw_rfs.c rename to software/FusionX/src/z80drv/src/z80vhw_rfs.c index 83022e662..00d93fdbf 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80vhw_rfs.c +++ b/software/FusionX/src/z80drv/src/z80vhw_rfs.c @@ -67,6 +67,10 @@ #define BNKSELUSER 0xEFFE // Select RFS Bank2 (User ROM) #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. // @@ -87,7 +91,13 @@ #define BNKCTRLDEF BBMOSI+SDCS+BBCLK // Default on startup for the Bank Control register. // RFS Board ROM rom filename definitions. -#define ROM_DIR "/apps/FusionX/host/MZ-80A/RFS/" +#if(TARGET_HOST_MZ80A == 1) + #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_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" @@ -167,6 +177,7 @@ typedef struct { uint8_t upCntr; // Register enable up counter. uint32_t mromAddr; // Actual address in MROM 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_RFSCtrl; @@ -194,7 +205,7 @@ void rfsSetupMemory(enum Z80_MEMORY_PROFILE mode) RFSCtrl.regCtrl = 0x00; RFSCtrl.mromAddr = MROM_ADDR; RFSCtrl.uromAddr = USER_ROM_I_ADDR; - Z80Ctrl->memSwitch = 0; + RFSCtrl.memSwitch = 0; RFSCtrl.sd.trainingCnt = 0; RFSCtrl.sd.initialised = 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)); } - 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. 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))); } } + 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. 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. // -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. uint8_t result = 0; @@ -264,10 +299,10 @@ void rfsInit(uint8_t mode80c) uint32_t idx; // 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); - loadROM(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); - 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_MROM_40C_FILENAME : ROM_MROM_80C_FILENAME, ROM_MROM_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); + rfsLoadROM(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_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. for(idx=0xF000; idx < 0x10000; idx++) @@ -276,7 +311,13 @@ void rfsInit(uint8_t mode80c) while(CPLD_READY() == 0); 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; } @@ -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+0xC000)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx)); } - Z80Ctrl->memSwitch = 0x01; + RFSCtrl.memSwitch = 0x01; } // 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)); } } - 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. for(idx=0x0000; idx < 0x1000; idx+=MEMORY_BLOCK_GRANULARITY) { - if(Z80Ctrl->memSwitch) + if(RFSCtrl.memSwitch) { // Monitor ROM is located at 0xC000. setMemoryType((0xC000+idx)/MEMORY_BLOCK_GRANULARITY, MEMORY_TYPE_VIRTUAL_ROM, (RFSCtrl.mromAddr+idx)); diff --git a/software/FusionX/src/z80drv/MZ80A/z80vhw_tzpu.c b/software/FusionX/src/z80drv/src/z80vhw_tzpu.c similarity index 84% rename from software/FusionX/src/z80drv/MZ80A/z80vhw_tzpu.c rename to software/FusionX/src/z80drv/src/z80vhw_tzpu.c index ad1633b22..2b4a69111 100644 --- a/software/FusionX/src/z80drv/MZ80A/z80vhw_tzpu.c +++ b/software/FusionX/src/z80drv/src/z80vhw_tzpu.c @@ -17,6 +17,7 @@ // Copyright: (c) 2019-2023 Philip Smart // // 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 // @@ -72,6 +73,9 @@ typedef struct { // TZPU Board control. 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. // 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. 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"); } // Perform any de-initialisation when the driver is removed. 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"); return; } @@ -133,27 +267,154 @@ void tzpuRemove(void) static inline void tzpuDecodeMemoryMapSetup(zuint16 address, zuint8 data, uint8_t ioFlag, uint8_t readFlag) { // Locals. + uint32_t idx; // I/O or Memory? if(ioFlag == 0) { - // Memory map switch. - if(readFlag == 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) { - - } else - { - + default: + break; } } else // I/O Decoding. { - // Only lower 8 bits recognised in the tzpu. - switch(address & 0xFF) + // #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) { - 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 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; } } } @@ -252,7 +513,9 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag) switch(address & 0x00FF) { 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. Z80Ctrl->memoryMode = (data & (MEMORY_MODES - 1)); @@ -264,13 +527,13 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag) (Z80Ctrl->page[Z80Ctrl->memoryMode]) = (uint32_t *)kmalloc((MEMORY_BLOCK_SLOTS*sizeof(uint32_t)), GFP_KERNEL); if ((Z80Ctrl->page[Z80Ctrl->memoryMode]) == NULL) { - pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode); + pr_info("z80drv: failed to allocate memory mapping page:%d memory!", Z80Ctrl->memoryMode); Z80Ctrl->page[Z80Ctrl->memoryMode] = Z80Ctrl->page[0]; } // 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) { @@ -744,71 +1007,3 @@ static inline void tzpuWrite(zuint16 address, zuint8 data, uint8_t ioFlag) 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"); -}