Browse Source

Initial commit.

Sergio L. Pascual 9 years ago
commit
8da567e4b6
19 changed files with 6865 additions and 0 deletions
  1. 395 0
      vhdl/DRAM.vhd
  2. 319 0
      vhdl/MMU.vhd
  3. 56 0
      vhdl/MonZ80_template.vhd
  4. 506 0
      vhdl/SDRAM_Controller.vhd
  5. 88 0
      vhdl/SSRAM.vhd
  6. 1094 0
      vhdl/T80.vhd
  7. 371 0
      vhdl/T80_ALU.vhd
  8. 2028 0
      vhdl/T80_MCode.vhd
  9. 220 0
      vhdl/T80_Pack.vhd
  10. 114 0
      vhdl/T80_Reg.vhd
  11. 192 0
      vhdl/T80se.vhd
  12. 96 0
      vhdl/Z80cpu.vhd
  13. 74 0
      vhdl/clkscale.vhd
  14. 93 0
      vhdl/fifo.vhd
  15. 64 0
      vhdl/gpio.vhd
  16. 183 0
      vhdl/timer.vhd
  17. 563 0
      vhdl/top_level.vhd
  18. 193 0
      vhdl/uart.vhd
  19. 216 0
      vhdl/uart_interface.vhd

+ 395 - 0
vhdl/DRAM.vhd

@@ -0,0 +1,395 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| DRAM interface: Connect the CPU to the SDRAM on the Papilio Pro board.  |--
+--| The SDRAM takes about 10 cycles to respond with data after making a     |--
+--| request, so this module includes a direct-mapped cache to store         |--
+--| recently read or written data in order to hide this latency.            |--
+--+-------------------------------------------------------------------------+--
+--
+-- The Papilio Pro board has an 8MB SDRAM chip on the board. The socz80 MMU
+-- provides a 64MB (26-bit) phyiscal address space. The low 32MB of address space
+-- is allocated to the DRAM (the top 32MB being used for other memory devices).
+-- 
+-- The low 32MB is divided into two 16MB blocks. Accesses to the first block
+-- (starting at 0MB) go through the cache, while accesses to the second
+-- block (starting at 16MB) bypass the cache. There is only 8MB SDRAM on the
+-- Papilio Pro so it is aliased twice in each block, ie it appears at 0MB,
+-- 8MB, 16MB and 24MB.
+--
+-- The cache is direct mapped, ie the low bits of the address dictate which cache
+-- line to use and which byte within that line. When a cache line is written to
+-- the top bits of the address are stored in "cache tag" memory. When a cache line
+-- is read the top bits of the address are compared to the stored tag to determine
+-- if the cached data relates to the same address.
+--
+-- Each cache line consists of a 45 bits:
+--   32 bits of cached data
+--    4 validity bits to indicate if the cached data is valid or not
+--    9 bits of address tag to indicate the top address bits of the
+--
+-- bit number (read these two        22222211111111110000000000
+-- lines top to bottom)              54321098765432109876543210
+--
+-- CPU address is 16 bits wide:                PPPPOOOOOOOOOOOO (4 bit page,  12 bit offset)
+-- physical address is 26 bits wide: FFFFFFFFFFFFFFOOOOOOOOOOOO (14 bit frame, 12 bit offset)
+-- DRAM address is     25 bits wide:  CIFFFFFFFFFFFOOOOOOOOOOOO (1 bit cache flag, 1 bit ignored, 11 bit frame, 12 bit offset)
+-- cached address is   23 bits wide:    TTTTTTTTTLLLLLLLLLLLLBB (9 bit cache line tag, 12 bit cache line, 2 bit byte offset)
+--
+-- cache lines use 4096 x 36 bit BRAM
+-- cache tags use  4096 x  9 bit BRAM
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity DRAM is
+	generic(
+        sdram_address_width : natural;
+        sdram_column_bits   : natural;
+        sdram_startup_cycles: natural;
+        cycles_per_refresh  : natural
+	);
+	port(
+        -- interface to the system
+        clk         : in    std_logic;
+        reset       : in    std_logic;
+        cs          : in    std_logic;
+        req_read    : in    std_logic;
+        req_write   : in    std_logic;
+        mem_address : in    std_logic_vector(24 downto 0);
+        data_in     : in    std_logic_vector(7 downto 0);
+        data_out    : out   std_logic_vector(7 downto 0) := (others => '0');
+        mem_wait    : out   std_logic;
+        coldboot    : out   std_logic; -- this signals 1 until the SDRAM has been initialised
+
+        -- interface to hardware SDRAM chip
+        SDRAM_CLK   : out   std_logic;
+        SDRAM_CKE   : out   std_logic;
+        SDRAM_CS    : out   std_logic;
+        SDRAM_nRAS  : out   std_logic;
+        SDRAM_nCAS  : out   std_logic;
+        SDRAM_nWE   : out   std_logic;
+        SDRAM_DQM   : out   std_logic_vector( 1 downto 0);
+        SDRAM_ADDR  : out   std_logic_vector (12 downto 0);
+        SDRAM_BA    : out   std_logic_vector( 1 downto 0);
+        SDRAM_DQ    : inout std_logic_vector (15 downto 0)
+	);
+end DRAM;
+
+architecture behaviour of DRAM is
+    -- sdram controller interface
+    signal cmd_address           : std_logic_vector(sdram_address_width-2 downto 0) := (others => '0');
+    signal cmd_wr                : std_logic := '1';
+    signal cmd_enable            : std_logic;
+    signal cmd_byte_enable       : std_logic_vector(3 downto 0);
+    signal cmd_data_in           : std_logic_vector(31 downto 0);
+    signal cmd_ready             : std_logic;
+    signal sdram_data_out        : std_logic_vector(31 downto 0);
+    signal sdram_data_out_ready  : std_logic;
+    signal seen_ready            : std_logic := '0';
+    signal last_address_word     : std_logic_vector(20 downto 0);
+
+    -- internal signals
+    signal current_word          : std_logic_vector(31 downto 0); -- value of current cache line
+    signal current_byte_valid    : std_logic_vector(3 downto 0);  -- validity bits for current cache line
+    signal word_changed          : std_logic;                     -- did the address bus value change?
+    signal cache_hit             : std_logic;
+    signal address_hit           : std_logic;
+    signal byte_valid_hit        : std_logic;                      
+    signal write_back            : std_logic;
+
+    -- state machine
+    type controller_state is ( st_idle,         -- waiting for command
+                               st_read,         -- cache miss: issued read command to controller, waiting for data to arrive
+                               st_read_done,    -- cache hit/completed miss: data arrived from controller, waiting for CPU to de-assert
+                               st_write);       -- write: issued write command, waiting for CPU to de-assert
+    signal current_state : controller_state;
+    signal next_state    : controller_state;
+
+    -- here's the cache memory signals
+    signal cache_line_memory_write_enable : std_logic;
+    signal cache_line_memory_data_in      : std_logic_vector(35 downto 0); -- 35 downto 32: validity bits; 31 downto 0: four data bytes
+    signal cache_line_memory_data_out     : std_logic_vector(35 downto 0);
+    signal cache_tag_memory_write_enable  : std_logic;
+    signal cache_tag_memory_data_in       : std_logic_vector(8 downto 0);
+    signal cache_tag_memory_data_out      : std_logic_vector(8 downto 0);
+
+    -- break up the incoming physical address
+    alias address_byte       : std_logic_vector(1 downto 0)  is mem_address(1 downto 0);
+    alias address_line       : std_logic_vector(11 downto 0) is mem_address(13 downto 2);
+    alias address_tag        : std_logic_vector(8 downto 0)  is mem_address(22 downto 14);
+    alias address_word       : std_logic_vector(20 downto 0) is mem_address(22 downto 2);
+                                                             -- mem_address(23) is unused in this design
+    alias cache_inhibit      : std_logic                     is mem_address(24);
+
+begin
+
+    -- this should be based on the generic, really
+    cmd_address <= mem_address(22 downto 2); -- address_tag & address_line
+    cmd_data_in <= data_in & data_in & data_in & data_in; -- write the same data four times
+    cmd_wr <= req_write;
+    cache_tag_memory_data_in <= address_tag;
+    coldboot <= not seen_ready;
+
+    compute_next_state: process(req_read, req_write, current_state, cache_hit, cmd_ready, cs, sdram_data_out_ready, word_changed)
+    begin
+        cmd_enable <= '0';
+        mem_wait <= '0';
+        write_back <= '0';
+        case current_state is
+            when st_idle =>
+                if cs = '1' and cmd_ready = '1' then 
+                    if req_read = '1' then 
+                        -- we can't process a read immediately if the address input just changed; delay them for one cycle.
+                        if word_changed = '1' then
+                            mem_wait <= '1';
+                            next_state <= st_idle;
+                            -- come back next cycle!
+                        else
+                            if cache_hit = '1' then
+                                mem_wait <= '0';
+                                next_state <= st_read_done;
+                            else
+                                cmd_enable <= '1';
+                                mem_wait <= '1';
+                                next_state <= st_read;
+                            end if;
+                        end if;
+                    elsif req_write = '1' then
+                        if word_changed = '1' then
+                            mem_wait <= '1';
+                            next_state <= st_idle;
+                            -- come back next cycle!
+                        else
+                            next_state <= st_write;
+                            cmd_enable <= '1';
+                            mem_wait <= '0'; -- no need to wait, the SDRAM controller will latch all the inputs
+                            write_back <= '1';
+                        end if;
+                    else
+                        next_state <= st_idle;
+                        mem_wait <= '0'; -- we know cmd_ready='1'
+                    end if;
+                else
+                    next_state <= st_idle;
+                    mem_wait <= (not cmd_ready);
+                end if;
+            when st_read =>
+                if cs = '1' and req_read = '1' then
+                    if sdram_data_out_ready = '1' then
+                        next_state <= st_read_done;
+                    else
+                        next_state <= st_read;
+                    end if;
+                else
+                    -- this kind of implies that they gave up on us?
+                    next_state <= st_idle;
+                end if;
+                mem_wait <= (not sdram_data_out_ready) and (not cache_hit);
+            when st_read_done =>
+                if cs = '1' and req_read = '1' then
+                    next_state <= st_read_done;
+                else
+                    next_state <= st_idle;
+                end if;
+                mem_wait <= (not sdram_data_out_ready) and (not cache_hit);
+            when st_write =>
+                if cs = '1' and req_write = '1' then
+                    next_state <= st_write;
+                else
+                    next_state <= st_idle;
+                end if;
+                mem_wait <= (not cmd_ready); -- no need to wait once the write has been committed
+        end case;
+    end process;
+
+    word_changed_check: process(last_address_word, address_word)
+    begin
+        if address_word = last_address_word then
+            word_changed <= '0';
+        else
+            word_changed <= '1';
+        end if;
+    end process;
+
+    cache_address_check: process(cache_tag_memory_data_out, cache_line_memory_data_out, address_tag)
+    begin
+        if cache_tag_memory_data_out = address_tag then
+            address_hit <= '1';
+            current_byte_valid <= cache_line_memory_data_out(35 downto 32);
+        else
+            address_hit <= '0';
+            current_byte_valid <= "0000";
+        end if;
+    end process;
+
+    cache_byte_valid_check: process(address_byte, current_byte_valid)
+    begin
+        case address_byte is
+            when "00"   => byte_valid_hit <= current_byte_valid(0);
+            when "01"   => byte_valid_hit <= current_byte_valid(1);
+            when "10"   => byte_valid_hit <= current_byte_valid(2);
+            when "11"   => byte_valid_hit <= current_byte_valid(3);
+            when others => byte_valid_hit <= '0';
+        end case;
+    end process;
+
+    cache_hit_check: process(byte_valid_hit, address_hit, cache_inhibit)
+    begin
+        if address_hit = '1' and byte_valid_hit = '1' and cache_inhibit = '0' then
+            cache_hit <= '1';
+        else
+            cache_hit <= '0';
+        end if;
+    end process;
+
+    byte_enable_decode: process(address_byte)
+    begin
+        case address_byte is
+            when "00"   => cmd_byte_enable <= "0001";
+            when "01"   => cmd_byte_enable <= "0010";
+            when "10"   => cmd_byte_enable <= "0100";
+            when "11"   => cmd_byte_enable <= "1000";
+            when others => cmd_byte_enable <= "1000";
+        end case;
+    end process;
+
+    data_out_demux: process(address_byte, sdram_data_out_ready, sdram_data_out, cache_line_memory_data_out, current_word)
+    begin
+        -- when the SDRAM is presenting data, feed it direct to the CPU.
+        -- otherwise feed data from our cache memory.
+        if sdram_data_out_ready = '1' then
+            current_word <= sdram_data_out;
+        else
+            current_word <= cache_line_memory_data_out(31 downto 0);
+        end if;
+
+        case address_byte is
+            when "00"   => data_out <= current_word( 7 downto  0);
+            when "01"   => data_out <= current_word(15 downto  8);
+            when "10"   => data_out <= current_word(23 downto 16);
+            when "11"   => data_out <= current_word(31 downto 24);
+            when others => data_out <= current_word(31 downto 24);
+        end case;
+    end process;
+
+    cache_write: process(current_state, data_in, next_state, write_back, cache_line_memory_data_out, sdram_data_out, sdram_data_out_ready, address_byte, current_byte_valid)
+    begin
+        if (next_state = st_read) or (write_back = '1') then
+            cache_tag_memory_write_enable <= '1';
+        else
+            cache_tag_memory_write_enable <= '0';
+        end if;
+
+        cache_line_memory_write_enable <= '0';
+        cache_line_memory_data_in <= cache_line_memory_data_out;
+
+        if next_state = st_read then
+            cache_line_memory_data_in <= (others => '0'); -- set word and all valid flags to 1
+            cache_line_memory_write_enable <= '1';
+        end if;
+
+        -- has our read completed?
+        if current_state = st_read then
+            if sdram_data_out_ready = '1' then
+                cache_line_memory_data_in <= "1111" & sdram_data_out;
+                cache_line_memory_write_enable <= '1';
+            end if;
+        elsif write_back = '1' then 
+            case address_byte is
+                when "00"   =>
+                    cache_line_memory_data_in <= current_byte_valid(3 downto 1) & "1" & 
+                                                 cache_line_memory_data_out(31 downto  8) & data_in;
+                when "01"   =>
+                    cache_line_memory_data_in <= 
+                                                current_byte_valid(3 downto 2) & "1" & current_byte_valid(0) &
+                                                cache_line_memory_data_out(31 downto 16) & data_in & cache_line_memory_data_out(7 downto 0);
+                when "10"   =>
+                    cache_line_memory_data_in <= 
+                                                current_byte_valid(3) & "1" & current_byte_valid(1 downto 0) &
+                                                cache_line_memory_data_out(31 downto 24) & data_in & cache_line_memory_data_out(15 downto 0);
+                when "11"   =>
+                    cache_line_memory_data_in <= 
+                                                "1" & current_byte_valid(2 downto 0) &
+                                                data_in & cache_line_memory_data_out(23 downto 0);
+                when others => -- shut up, compiler!
+            end case;
+            cache_line_memory_write_enable <= '1';
+        end if;
+    end process;
+
+    sdram_registers: process(clk)
+    begin
+        if rising_edge(clk) then
+            -- state register
+            current_state <= next_state;
+            -- coldboot detection
+            seen_ready <= seen_ready or cmd_ready;
+            -- track memory address
+            last_address_word <= address_word;
+        end if;
+    end process;
+
+    -- underlying SDRAM controller (thanks, Hamsterworks!)
+    sdram_ctrl: entity work.SDRAM_Controller 
+    GENERIC MAP(
+                sdram_address_width => sdram_address_width,
+                sdram_column_bits   => sdram_column_bits,
+                sdram_startup_cycles=> sdram_startup_cycles,
+                cycles_per_refresh  => cycles_per_refresh
+               ) 
+    PORT MAP(
+                clk             => clk,
+                reset           => reset,
+
+                cmd_address     => cmd_address,
+                cmd_wr          => cmd_wr,
+                cmd_enable      => cmd_enable,
+                cmd_ready       => cmd_ready,
+                cmd_byte_enable => cmd_byte_enable,
+                cmd_data_in     => cmd_data_in,
+
+                data_out        => sdram_data_out,
+                data_out_ready  => sdram_data_out_ready,
+
+                SDRAM_CLK       => SDRAM_CLK,
+                SDRAM_CKE       => SDRAM_CKE,
+                SDRAM_CS        => SDRAM_CS,
+                SDRAM_RAS       => SDRAM_nRAS,
+                SDRAM_CAS       => SDRAM_nCAS,
+                SDRAM_WE        => SDRAM_nWE,
+                SDRAM_DQM       => SDRAM_DQM,
+                SDRAM_BA        => SDRAM_BA,
+                SDRAM_ADDR      => SDRAM_ADDR,
+                SDRAM_DATA      => SDRAM_DQ
+            );
+
+    -- block RAM used to store cache line data and byte validity (packs nicely into 36 bits)
+    cacheline_memory_sram: entity work.RAM4K36
+    port map (
+        clk      => clk,
+        reset    => reset,
+        write    => cache_line_memory_write_enable,
+        address  => address_line,
+        data_in  => cache_line_memory_data_in,
+        data_out => cache_line_memory_data_out
+        
+    );
+
+    -- block RAM used to store cache line tag memory (packs nicely into 9 bits)
+    cachetag_memory_sram: entity work.RAM4K9
+    port map (
+        clk      => clk,
+        reset    => reset,
+        write    => cache_tag_memory_write_enable,
+        address  => address_line,
+        data_in  => cache_tag_memory_data_in,
+        data_out => cache_tag_memory_data_out
+    );
+end;

+ 319 - 0
vhdl/MMU.vhd

@@ -0,0 +1,319 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| 4K paged Memory Management Unit: Translates 16-bit virtual addresses    |--
+--| from the CPU into 26-bit physical addresses to allow more memory to be  |--
+--| addressed. Also has a hack to allow unmapped physical memory to be      |--
+--| accessed through an IO port which synthesises memory operations.        |--
+--+-------------------------------------------------------------------------+--
+--
+-- The MMU takes a 16-bit virtual address from the CPU and divides it into a
+-- 4-bit frame number and a 12-bit offset. The frame number is used as an index
+-- into an array of sixteen registers which contain the hardware page numbers
+-- (the translation table). The physical address is then formed from the
+-- hardware page number concatenated with the 12-bit offset. My hardware page
+-- numbers are 14-bits long because I wanted a 64MB physical address space but
+-- you could use any length you wished.
+-- 
+-- So if the virtual address 0xABCD is accessed, we'd divide that into frame
+-- number 0xA (decimal 10), offset 0xBCD. If the 10th MMU translation register
+-- contains 0x1234 then the translated physical address would be 0x1234BCD.
+-- 
+-- My MMU is programmed using 8 I/O ports in the range 0xF8 through 0xFF. The
+-- chip select line is asserted for any access in that range and the MMU 
+-- decodes the low three bits to determine which register is being accessed.
+-- 
+-- The port at 0xF8 is effectively a mux which selects the function of ports
+-- 0xFB through 0xFF.
+-- 
+-- Writing 0x00 through 0x0F to the function register at 0xF8 allows you to
+-- read/write one of the 16 MMU translation registers. With these selected;
+--   - 0xFC contains the high byte of the physical address
+--   - 0xFD contains the low byte of the physical address
+--   - 0xFB contains permission bits (read/write/execute, currently ignored)
+-- 
+-- So updating a mapping generally requires just three I/O writes: One to 0xF8
+-- to select which frame to modify, and one each to 0xFC and 0xFD to write out
+-- the new translation. The permission bits are programmable but currently
+-- ignored (I had planned to add some level of memory protection to UZI one day)
+-- 
+-- The "17th page" is a bit of a hack bolted on. The lazy programmer in me finds
+-- it much more convenient to sometimes access memory without remapping a frame;
+-- in particular you don't have to select which frame to remap such as to avoid
+-- remapping the memory pointed to by PC, SP or your source/target pointer.
+-- 
+-- Writing 0xFF to port 0xF8 selects the "17th page pointer". This is a 26-bit
+-- register but again could be wider/narrower as required. With this selected
+-- ports 0xFC, 0xFD, 0xFE, 0xFF are the register contents (with the high byte in
+-- 0xFC, low byte in 0xFF).
+-- 
+-- When the CPU reads or writes the I/O port 0xFA the MMU translates the I/O
+-- operation into a memory operation. The physical memory address accessed is
+-- the address contained in the 17th page pointer register. After the memory
+-- operation completes the 17th page pointer register is incremented so that
+-- repeated accesses to 0xFA walk forward through memory.
+--
+--
+-- MMU registers:
+--
+-- base = 0xF8 in standard socz80 system
+--
+-- base+0  mux frame select (write 00..0F to select frame, FF to select "17th page" pointer)
+-- base+1  (unused)
+-- base+2  17th page (read/write will address pointed memory and post-increment the pointer)
+-- base+3  page permissions
+-- base+4  page address (high byte) / ptr address (high byte)
+-- base+5  page address (low byte)  / ptr address
+-- base+6                             ptr address
+-- base+7                             ptr address (low byte)
+--
+-- Basic operation of the MMU (older documentation, concise but still correct)
+-- For memory requests:
+--    The top 4 bits of CPU logical address are replaced with 14 bits taken 
+--    from the MMU translation table entry indexed by those top four bits.
+-- For IO requests:
+--    Reads or writes to I/O port at (base+2) are converted into memory
+--    requests to the address pointed to by "the 17th page", a pointer which
+--    can be accessed by writing 0xFF to the frame select register (base+0)
+--    and then programming a 26-bit address into I/O ports FC FD FE FF. Each
+--    I/O request to port F/A results in the pointer being post-incremented
+--    by 1, which means that INIR our OUTIR instructions can read/write blocks
+--    of memory outside of the CPU logical address space through this port.
+--
+--    Note that the MMU has to insert a forced CPU wait state to give the
+--    addressed memory device time to read the new address off the bus.
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity MMU is
+    port(
+        clk             : in  std_logic;
+        reset           : in  std_logic;
+        address_in      : in  std_logic_vector(15 downto 0);
+        address_out     : out std_logic_vector(25 downto 0);
+        cpu_data_in     : in  std_logic_vector(7 downto 0);
+        cpu_data_out    : out std_logic_vector(7 downto 0);
+        cpu_wait        : out std_logic;
+        req_mem_in      : in  std_logic;
+        req_mem_out     : out std_logic;
+        req_io_in       : in  std_logic;
+        req_io_out      : out std_logic;
+        io_cs           : in  std_logic;
+        req_read        : in  std_logic;
+        req_write       : in  std_logic;
+        access_violated : out std_logic
+    );
+end MMU;
+
+architecture behaviour of MMU is
+    -- each MMU entry looks like this
+    type mmu_entry_type is
+        record
+            frame:     std_logic_vector(13 downto 0);
+            can_read:  std_logic;
+            can_write: std_logic;
+        end record;
+
+    -- the whole MMU state looks like this
+    type mmu_entry_array is array(natural range <>) of mmu_entry_type;
+
+    -- and here's our instance
+    signal mmu_entry : mmu_entry_array(0 to 15);
+
+    -- 17th page pointer (frame FF)
+    signal mmu_frame_ff_pointer : std_logic_vector(25 downto 0);
+
+    -- IO interface
+    signal cpu_entry_select : std_logic_vector(7 downto 0);
+
+    -- break up the incoming virtual address
+    alias frame_number : std_logic_vector( 3 downto 0) is address_in(15 downto 12);
+    alias page_offset : std_logic_vector(11 downto 0) is address_in(11 downto  0);
+
+    signal map_io_to_ff_ptr : std_logic;
+    signal was_map_io_to_ff_ptr : std_logic := '0';
+begin
+
+    map_io_to_mem_proc: process(address_in, req_mem_in, req_io_in)
+    begin
+        if req_mem_in = '0' and req_io_in = '1' and address_in(7 downto 0) = "11111010" then
+            map_io_to_ff_ptr <= '1';
+        else
+            map_io_to_ff_ptr <= '0';
+        end if;
+    end process;
+
+    with map_io_to_ff_ptr select
+        address_out <= 
+                      mmu_entry(to_integer(unsigned(frame_number))).frame & page_offset when '0',
+                      mmu_frame_ff_pointer when '1';
+
+    with map_io_to_ff_ptr select
+        req_mem_out <= 
+                      req_mem_in when '0',
+                      '1' when '1';
+
+    with map_io_to_ff_ptr select
+        req_io_out <= 
+                      req_io_in when '0',
+                      '0' when '1';
+
+    with map_io_to_ff_ptr select
+        access_violated <= 
+                          req_mem_in and ((req_read  and not mmu_entry(to_integer(unsigned(frame_number))).can_read) or
+                          (req_write and not mmu_entry(to_integer(unsigned(frame_number))).can_write)) when '0',
+                          '0' when '1';
+
+    -- force CPU to wait one cycle when we map IO to memory access; this
+    -- is in order to give our synchronous memories a cycle to read the
+    -- address, look up the data in synchronous memory, and provide a result.
+    cpu_wait <= map_io_to_ff_ptr and (not was_map_io_to_ff_ptr);
+
+    data_out: process(address_in, cpu_entry_select, mmu_entry, mmu_frame_ff_pointer)
+    begin
+        if cpu_entry_select = "11111111" then
+            -- pointer (FF)
+            case address_in(2 downto 0) is
+                when "000" => 
+                    cpu_data_out <= cpu_entry_select;
+                when "011" => 
+                    cpu_data_out <= "00000011";
+                when "100" => 
+                    cpu_data_out <= "000000" & mmu_frame_ff_pointer(25 downto 24);
+                when "101" => 
+                    cpu_data_out <= mmu_frame_ff_pointer(23 downto 16);
+                when "110" => 
+                    cpu_data_out <= mmu_frame_ff_pointer(15 downto 8);
+                when "111" => 
+                    cpu_data_out <= mmu_frame_ff_pointer(7 downto 0);
+                when others =>
+                    cpu_data_out <= "00000000";
+            end case;
+        else
+            -- 16 frames (00 .. 0F)
+            case address_in(2 downto 0) is
+                when "000" => 
+                    cpu_data_out <= cpu_entry_select;
+                when "011" => 
+                    cpu_data_out <= "000000" & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_write & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_read;
+                when "100" => 
+                    cpu_data_out <= "00" & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 8);
+                when "101" => 
+                    cpu_data_out <= mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(7 downto 0);
+                when others =>
+                    cpu_data_out <= "00000000";
+            end case;
+        end if;
+    end process;
+
+    mmu_registers: process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then
+                mmu_entry( 0).frame <= "10000000000000"; -- first page of SRAM (monitor ROM)
+                mmu_entry( 1).frame <= "00000000000001"; -- DRAM page 1
+                mmu_entry( 2).frame <= "00000000000010"; -- DRAM page 2
+                mmu_entry( 3).frame <= "00000000000011"; -- DRAM page 3
+                mmu_entry( 4).frame <= "00000000000100"; -- DRAM page 4
+                mmu_entry( 5).frame <= "00000000000101"; -- DRAM page 5
+                mmu_entry( 6).frame <= "00000000000110"; -- DRAM page 6
+                mmu_entry( 7).frame <= "00000000000111"; -- DRAM page 7
+                mmu_entry( 8).frame <= "00000000001000"; -- DRAM page 8
+                mmu_entry( 9).frame <= "00000000001001"; -- DRAM page 9
+                mmu_entry(10).frame <= "00000000001010"; -- DRAM page 10
+                mmu_entry(11).frame <= "00000000001011"; -- DRAM page 11
+                mmu_entry(12).frame <= "00000000001100"; -- DRAM page 12
+                mmu_entry(13).frame <= "00000000001101"; -- DRAM page 13
+                mmu_entry(14).frame <= "00000000001110"; -- DRAM page 14
+                mmu_entry(15).frame <= "10000000000001"; -- second page of SRAM
+                mmu_entry( 0).can_read  <= '1';
+                mmu_entry( 1).can_read  <= '1';
+                mmu_entry( 2).can_read  <= '1';
+                mmu_entry( 3).can_read  <= '1';
+                mmu_entry( 4).can_read  <= '1';
+                mmu_entry( 5).can_read  <= '1';
+                mmu_entry( 6).can_read  <= '1';
+                mmu_entry( 7).can_read  <= '1';
+                mmu_entry( 8).can_read  <= '1';
+                mmu_entry( 9).can_read  <= '1';
+                mmu_entry(10).can_read  <= '1';
+                mmu_entry(11).can_read  <= '1';
+                mmu_entry(12).can_read  <= '1';
+                mmu_entry(13).can_read  <= '1';
+                mmu_entry(14).can_read  <= '1';
+                mmu_entry(15).can_read  <= '1';
+                mmu_entry( 0).can_write <= '0';
+                mmu_entry( 1).can_write <= '1';
+                mmu_entry( 2).can_write <= '1';
+                mmu_entry( 3).can_write <= '1';
+                mmu_entry( 4).can_write <= '1';
+                mmu_entry( 5).can_write <= '1';
+                mmu_entry( 6).can_write <= '1';
+                mmu_entry( 7).can_write <= '1';
+                mmu_entry( 8).can_write <= '1';
+                mmu_entry( 9).can_write <= '1';
+                mmu_entry(10).can_write <= '1';
+                mmu_entry(11).can_write <= '1';
+                mmu_entry(12).can_write <= '1';
+                mmu_entry(13).can_write <= '1';
+                mmu_entry(14).can_write <= '1';
+                mmu_entry(15).can_write <= '1';
+                mmu_frame_ff_pointer <= "10000000000000000000000000"; -- map first byte of ROM to pointer on reset
+                was_map_io_to_ff_ptr <= '0';
+            else
+                was_map_io_to_ff_ptr <= map_io_to_ff_ptr;
+
+                if io_cs = '1' and req_write = '1' then
+                    case address_in(2 downto 0) is
+                        when "000" => 
+                            cpu_entry_select <= cpu_data_in;
+                        when "011" => 
+                            if cpu_entry_select(7 downto 4) = "0000" then
+                                mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_read  <= cpu_data_in(0);
+                                mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).can_write <= cpu_data_in(1);
+                            end if;
+                        when "100" => 
+                            if cpu_entry_select(7 downto 4) = "0000" then
+                                mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 0) <= 
+                                    cpu_data_in(5 downto 0) & mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(7 downto 0);
+                            elsif cpu_entry_select = "11111111" then
+                                mmu_frame_ff_pointer <=
+                                    cpu_data_in(1 downto 0) & mmu_frame_ff_pointer(23 downto 0);
+                            end if;
+                        when "101" => 
+                            if cpu_entry_select(7 downto 4) = "0000" then
+                                mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 0) <= 
+                                    mmu_entry(to_integer(unsigned(cpu_entry_select(3 downto 0)))).frame(13 downto 8) & cpu_data_in(7 downto 0);
+                            elsif cpu_entry_select = "11111111" then
+                                mmu_frame_ff_pointer <=
+                                    mmu_frame_ff_pointer(25 downto 24) & cpu_data_in(7 downto 0) & mmu_frame_ff_pointer(15 downto 0);
+                            end if;
+                        when "110" => 
+                            if cpu_entry_select = "11111111" then
+                                mmu_frame_ff_pointer <=
+                                    mmu_frame_ff_pointer(25 downto 16) & cpu_data_in(7 downto 0) & mmu_frame_ff_pointer(7 downto 0);
+                            end if;
+                        when "111" =>
+                            if cpu_entry_select = "11111111" then
+                                mmu_frame_ff_pointer <=
+                                    mmu_frame_ff_pointer(25 downto 8) & cpu_data_in(7 downto 0);
+                            end if;
+                        when others =>
+                            -- nothing
+                    end case;
+                elsif map_io_to_ff_ptr = '0' and was_map_io_to_ff_ptr = '1' then
+                    -- post-increment our pointer (this is what makes "the 17th page" efficient!)
+                    mmu_frame_ff_pointer <= std_logic_vector(unsigned(mmu_frame_ff_pointer) + 1);
+                end if;
+            end if;
+        end if;
+    end process;
+end;

+ 56 - 0
vhdl/MonZ80_template.vhd

@@ -0,0 +1,56 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| An inferrable 4KB ROM to contain the monitor program                    |--
+--+-------------------------------------------------------------------------+--
+--
+-- MonZ80_template.vhd contains the template VHDL for the ROM but no actual
+-- data. The "ROMHERE" string is replaced by byte data by the "make_vhdl_rom"
+-- tool in software/tools which is invoked to generate "MonZ80.vhd" after
+-- the monitor program has been assembled.
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity MonZ80 is
+   port(
+      clk           : in  std_logic;
+      a             : in  std_logic_vector(11 downto 0);
+      d             : out std_logic_vector(7 downto 0)
+   );
+end MonZ80;
+
+architecture arch of MonZ80 is
+   constant byte_rom_WIDTH: integer := 8;
+   type byte_rom_type is array (0 to 4095) of std_logic_vector(byte_rom_WIDTH-1 downto 0);
+   signal address_latch : std_logic_vector(11 downto 0) := (others => '0');
+
+   -- actually memory cells
+   signal byte_rom : byte_rom_type := (
+   -- ROM contents follows
+
+
+%ROMHERE%
+
+
+     );
+
+begin
+
+  ram_process: process(clk, byte_rom)
+  begin
+      if rising_edge(clk) then
+          -- latch the address, in order to infer a synchronous memory
+          address_latch <= a;
+      end if;
+  end process;
+
+  d <= byte_rom(to_integer(unsigned(address_latch)));
+
+end arch;

+ 506 - 0
vhdl/SDRAM_Controller.vhd

@@ -0,0 +1,506 @@
+----------------------------------------------------------------------------------
+-- Engineer: Mike Field <hamster@snap.net.nz>
+-- 
+-- Create Date:    14:09:12 09/15/2013 
+-- Module Name:    SDRAM_Controller - Behavioral 
+-- Description:    Simple SDRAM controller for a Micron 48LC16M16A2-7E
+--                 or Micron 48LC4M16A2-7E @ 100MHz      
+-- Revision: 
+-- Revision 0.1 - Initial version
+-- Revision 0.2 - Removed second clock signal that isn't needed.
+-- Revision 0.3 - Added back-to-back reads and writes.
+-- Revision 0.4 - Allow refeshes to be delayed till next PRECHARGE is issued,
+--                Unless they get really, really delayed. If a delay occurs multiple
+--                refreshes might get pushed out, but it will have avioded about 
+--                50% of the refresh overhead
+-- Revision 0.5 - Add more paramaters to the design, allowing it to work for both the 
+--                Papilio Pro and Logi-Pi
+--
+-- Worst case performance (single accesses to different rows or banks) is: 
+-- Writes 16 cycles = 6,250,000 writes/sec = 25.0MB/s (excluding refresh overhead)
+-- Reads  17 cycles = 5,882,352 reads/sec  = 23.5MB/s (excluding refresh overhead)
+--
+-- For 1:1 mixed reads and writes into the same row it is around 88MB/s 
+-- For reads or wries to the same it is can be as high as 184MB/s 
+----------------------------------------------------------------------------------
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+library UNISIM;
+use UNISIM.VComponents.all;
+use IEEE.NUMERIC_STD.ALL;
+
+
+entity SDRAM_Controller is
+    generic (
+      sdram_address_width : natural;
+      sdram_column_bits   : natural;
+      sdram_startup_cycles: natural;
+      cycles_per_refresh  : natural
+    );
+    Port ( clk           : in  STD_LOGIC;
+           reset         : in  STD_LOGIC;
+           
+           -- Interface to issue reads or write data
+           cmd_ready         : out STD_LOGIC;                     -- '1' when a new command will be acted on
+           cmd_enable        : in  STD_LOGIC;                     -- Set to '1' to issue new command (only acted on when cmd_read = '1')
+           cmd_wr            : in  STD_LOGIC;                     -- Is this a write?
+           cmd_address       : in  STD_LOGIC_VECTOR(sdram_address_width-2 downto 0); -- address to read/write
+           cmd_byte_enable   : in  STD_LOGIC_VECTOR(3 downto 0);  -- byte masks for the write command
+           cmd_data_in       : in  STD_LOGIC_VECTOR(31 downto 0); -- data for the write command
+           
+           data_out          : out STD_LOGIC_VECTOR(31 downto 0); -- word read from SDRAM
+           data_out_ready    : out STD_LOGIC;                     -- is new data ready?
+           
+           -- SDRAM signals
+           SDRAM_CLK     : out   STD_LOGIC;
+           SDRAM_CKE     : out   STD_LOGIC;
+           SDRAM_CS      : out   STD_LOGIC;
+           SDRAM_RAS     : out   STD_LOGIC;
+           SDRAM_CAS     : out   STD_LOGIC;
+           SDRAM_WE      : out   STD_LOGIC;
+           SDRAM_DQM     : out   STD_LOGIC_VECTOR( 1 downto 0);
+           SDRAM_ADDR    : out   STD_LOGIC_VECTOR(12 downto 0);
+           SDRAM_BA      : out   STD_LOGIC_VECTOR( 1 downto 0);
+           SDRAM_DATA    : inout STD_LOGIC_VECTOR(15 downto 0));
+end SDRAM_Controller;
+
+architecture Behavioral of SDRAM_Controller is
+   -- From page 37 of MT48LC16M16A2 datasheet
+   -- Name (Function)       CS# RAS# CAS# WE# DQM  Addr    Data
+   -- COMMAND INHIBIT (NOP)  H   X    X    X   X     X       X
+   -- NO OPERATION (NOP)     L   H    H    H   X     X       X
+   -- ACTIVE                 L   L    H    H   X  Bank/row   X
+   -- READ                   L   H    L    H  L/H Bank/col   X
+   -- WRITE                  L   H    L    L  L/H Bank/col Valid
+   -- BURST TERMINATE        L   H    H    L   X     X     Active
+   -- PRECHARGE              L   L    H    L   X   Code      X
+   -- AUTO REFRESH           L   L    L    H   X     X       X 
+   -- LOAD MODE REGISTER     L   L    L    L   X  Op-code    X 
+   -- Write enable           X   X    X    X   L     X     Active
+   -- Write inhibit          X   X    X    X   H     X     High-Z
+
+   -- Here are the commands mapped to constants   
+   constant CMD_UNSELECTED    : std_logic_vector(3 downto 0) := "1000";
+   constant CMD_NOP           : std_logic_vector(3 downto 0) := "0111";
+   constant CMD_ACTIVE        : std_logic_vector(3 downto 0) := "0011";
+   constant CMD_READ          : std_logic_vector(3 downto 0) := "0101";
+   constant CMD_WRITE         : std_logic_vector(3 downto 0) := "0100";
+   constant CMD_TERMINATE     : std_logic_vector(3 downto 0) := "0110";
+   constant CMD_PRECHARGE     : std_logic_vector(3 downto 0) := "0010";
+   constant CMD_REFRESH       : std_logic_vector(3 downto 0) := "0001";
+   constant CMD_LOAD_MODE_REG : std_logic_vector(3 downto 0) := "0000";
+
+   constant MODE_REG          : std_logic_vector(12 downto 0) := 
+    -- Reserved, wr bust, OpMode, CAS Latency (2), Burst Type, Burst Length (2)
+         "000" &   "0"  &  "00"  &    "010"      &     "0"    &   "001";
+
+   signal iob_command     : std_logic_vector( 3 downto 0) := CMD_NOP;
+   signal iob_address     : std_logic_vector(12 downto 0) := (others => '0');
+   signal iob_data        : std_logic_vector(15 downto 0) := (others => '0');
+   signal iob_dqm         : std_logic_vector( 1 downto 0) := (others => '0');
+   signal iob_cke         : std_logic := '0';
+   signal iob_bank        : std_logic_vector( 1 downto 0) := (others => '0');
+   
+   attribute IOB: string;
+   attribute IOB of iob_command: signal is "true";
+   attribute IOB of iob_address: signal is "true";
+   attribute IOB of iob_dqm    : signal is "true";
+   attribute IOB of iob_cke    : signal is "true";
+   attribute IOB of iob_bank   : signal is "true";
+   attribute IOB of iob_data   : signal is "true";
+   
+   signal iob_data_next      : std_logic_vector(15 downto 0) := (others => '0');
+   signal captured_data      : std_logic_vector(15 downto 0) := (others => '0');
+   signal captured_data_last : std_logic_vector(15 downto 0) := (others => '0');
+   signal sdram_din          : std_logic_vector(15 downto 0);
+   attribute IOB of captured_data : signal is "true";
+   
+   type fsm_state is (s_startup,
+                      s_idle_in_6, s_idle_in_5, s_idle_in_4,   s_idle_in_3, s_idle_in_2, s_idle_in_1,
+                      s_idle,
+                      s_open_in_2, s_open_in_1,
+                      s_write_1, s_write_2, s_write_3,
+                      s_read_1,  s_read_2,  s_read_3,  s_read_4,  
+                      s_precharge
+                      );
+
+   signal state              : fsm_state := s_startup;
+   attribute FSM_ENCODING : string;
+   attribute FSM_ENCODING of state : signal is "ONE-HOT";
+   
+   -- dual purpose counter, it counts up during the startup phase, then is used to trigger refreshes.
+   constant startup_refresh_max   : unsigned(13 downto 0) := (others => '1');  
+   signal   startup_refresh_count : unsigned(13 downto 0) := startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+
+   -- logic to decide when to refresh
+   signal pending_refresh : std_logic := '0';
+   signal forcing_refresh : std_logic := '0';
+
+   -- The incoming address is split into these three values
+   signal addr_row         : std_logic_vector(12 downto 0) := (others => '0');
+   signal addr_col         : std_logic_vector(12 downto 0) := (others => '0');
+   signal addr_bank        : std_logic_vector( 1 downto 0) := (others => '0');
+   
+   signal dqm_sr           : std_logic_vector( 3 downto 0) := (others => '1'); -- an extra two bits in case CAS=3
+   
+   -- signals to hold the requested transaction before it is completed
+   signal save_wr          : std_logic := '0';
+   signal save_row         : std_logic_vector(12 downto 0);
+   signal save_bank        : std_logic_vector( 1 downto 0);
+   signal save_col         : std_logic_vector(12 downto 0);
+   signal save_data_in     : std_logic_vector(31 downto 0);
+   signal save_byte_enable : std_logic_vector( 3 downto 0);
+   
+   -- control when new transactions are accepted
+   signal ready_for_new    : std_logic := '0';
+   signal got_transaction  : std_logic := '0';
+   
+   signal can_back_to_back : std_logic := '0';
+
+   -- signal to control the Hi-Z state of the DQ bus
+   signal iob_dq_hiz       : std_logic := '1';
+
+   -- signals for when to read the data off of the bus
+   signal data_ready_delay : std_logic_vector( 4 downto 0);   
+   
+   -- bit indexes used when splitting the address into row/colum/bank.
+   constant start_of_col  : natural := 0;
+   constant end_of_col    : natural := sdram_column_bits-2;
+   constant start_of_bank : natural := sdram_column_bits-1;
+   constant end_of_bank   : natural := sdram_column_bits;
+   constant start_of_row  : natural := sdram_column_bits+1;
+   constant end_of_row    : natural := sdram_address_width-2;
+   constant prefresh_cmd  : natural := 10;
+begin
+   -- Indicate the need to refresh when the counter is 2048,
+   -- Force a refresh when the counter is 4096 - (if a refresh is forced, 
+   -- multiple refresshes will be forced until the counter is below 2048
+   pending_refresh <= startup_refresh_count(11);
+   forcing_refresh <= startup_refresh_count(12);
+
+   -- tell the outside world when we can accept a new transaction;
+   cmd_ready <= ready_for_new;
+   ----------------------------------------------------------------------------
+   -- Seperate the address into row / bank / address
+   ----------------------------------------------------------------------------
+   addr_row(end_of_row-start_of_row downto 0) <= cmd_address(end_of_row  downto start_of_row);       -- 12:0 <=  22:10
+   addr_bank                                  <= cmd_address(end_of_bank downto start_of_bank);      -- 1:0  <=  9:8
+   addr_col(sdram_column_bits-1 downto 0)     <= cmd_address(end_of_col  downto start_of_col) & '0'; -- 8:0  <=  7:0 & '0'
+   --addr_row(12 downto 0) <= cmd_address(22 downto 10);       -- 12:0 <=  22:10
+   --addr_bank             <= cmd_address( 9 downto  8);      -- 1:0  <=  9:8
+   --addr_col(8 downto 0)  <= cmd_address( 7 downto  0) & '0'; -- 8:0  <=  7:0 & '0'
+
+   -----------------------------------------------------------
+   -- Forward the SDRAM clock to the SDRAM chip - 180 degress 
+   -- out of phase with the control signals (ensuring setup and holdup 
+  -----------------------------------------------------------
+ sdram_clk_forward : ODDR2
+   generic map(DDR_ALIGNMENT => "NONE", INIT => '0', SRTYPE => "SYNC")
+   port map (Q => sdram_clk, C0 => clk, C1 => not clk, CE => '1', R => '0', S => '0', D0 => '0', D1 => '1');
+
+   -----------------------------------------------
+   --!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   --!! Ensure that all outputs are registered. !!
+   --!! Check the pinout report to be sure      !!
+   --!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   -----------------------------------------------
+   sdram_cke  <= iob_cke;
+   sdram_CS   <= iob_command(3);
+   sdram_RAS  <= iob_command(2);
+   sdram_CAS  <= iob_command(1);
+   sdram_WE   <= iob_command(0);
+   sdram_dqm  <= iob_dqm;
+   sdram_ba   <= iob_bank;
+   sdram_addr <= iob_address;
+   
+   ---------------------------------------------------------------
+   -- Explicitly set up the tristate I/O buffers on the DQ signals
+   ---------------------------------------------------------------
+iob_dq_g: for i in 0 to 15 generate
+   begin
+iob_dq_iob: IOBUF
+   generic map (DRIVE => 12, IOSTANDARD => "LVTTL", SLEW => "FAST")
+   port map ( O  => sdram_din(i), IO => sdram_data(i), I  => iob_data(i), T  => iob_dq_hiz);
+end generate;
+                                     
+capture_proc: process(clk) 
+   begin
+     if rising_edge(clk) then
+         captured_data      <= sdram_din;
+      end if;
+   end process;
+
+main_proc: process(clk) 
+   begin
+      if rising_edge(clk) then
+         captured_data_last <= captured_data;
+      
+         ------------------------------------------------
+         -- Default state is to do nothing
+         ------------------------------------------------
+         iob_command     <= CMD_NOP;
+         iob_address     <= (others => '0');
+         iob_bank        <= (others => '0');
+
+         ------------------------------------------------
+         -- countdown for initialisation & refresh
+         ------------------------------------------------
+         startup_refresh_count <= startup_refresh_count+1;
+                  
+         -------------------------------------------------------------------
+         -- It we are ready for a new tranasction and one is being presented
+         -- then accept it. Also remember what we are reading or writing,
+         -- and if it can be back-to-backed with the last transaction
+         -------------------------------------------------------------------
+         if ready_for_new = '1' and cmd_enable = '1' then
+            if save_bank = addr_bank and save_row = addr_row then
+               can_back_to_back <= '1';
+            else
+               can_back_to_back <= '0';
+            end if;
+            save_row         <= addr_row;
+            save_bank        <= addr_bank;
+            save_col         <= addr_col;
+            save_wr          <= cmd_wr; 
+            save_data_in     <= cmd_data_in;
+            save_byte_enable <= cmd_byte_enable;
+            got_transaction  <= '1';
+            ready_for_new    <= '0';
+         end if;
+
+         ------------------------------------------------
+         -- Handle the data coming back from the 
+         -- SDRAM for the Read transaction
+         ------------------------------------------------
+         data_out_ready <= '0';
+         if data_ready_delay(0) = '1' then
+            data_out       <= captured_data & captured_data_last;
+            data_out_ready <= '1';
+         end if;
+         
+         ----------------------------------------------------------------------------
+         -- update shift registers used to choose when to present data to/from memory
+         ----------------------------------------------------------------------------
+         data_ready_delay <= '0' & data_ready_delay(data_ready_delay'high downto 1);
+         iob_dqm          <= dqm_sr(1 downto 0);
+         dqm_sr           <= "11" & dqm_sr(dqm_sr'high downto 2);
+         
+         case state is 
+            when s_startup =>
+               ------------------------------------------------------------------------
+               -- This is the initial startup state, where we wait for at least 100us
+               -- before starting the start sequence
+               -- 
+               -- The initialisation is sequence is 
+               --  * de-assert SDRAM_CKE
+               --  * 100us wait, 
+               --  * assert SDRAM_CKE
+               --  * wait at least one cycle, 
+               --  * PRECHARGE
+               --  * wait 2 cycles
+               --  * REFRESH, 
+               --  * tREF wait
+               --  * REFRESH, 
+               --  * tREF wait 
+               --  * LOAD_MODE_REG 
+               --  * 2 cycles wait
+               ------------------------------------------------------------------------
+               iob_CKE <= '1';
+               
+               -- All the commands during the startup are NOPS, except these
+               if startup_refresh_count = startup_refresh_max-31 then      
+                  -- ensure all rows are closed
+                  iob_command     <= CMD_PRECHARGE;
+                  iob_address(prefresh_cmd) <= '1';  -- all banks
+                  iob_bank        <= (others => '0');
+               elsif startup_refresh_count = startup_refresh_max-23 then   
+                  -- these refreshes need to be at least tREF (66ns) apart
+                  iob_command     <= CMD_REFRESH;
+               elsif startup_refresh_count = startup_refresh_max-15 then
+                  iob_command     <= CMD_REFRESH;
+               elsif startup_refresh_count = startup_refresh_max-7 then    
+                  -- Now load the mode register
+                  iob_command     <= CMD_LOAD_MODE_REG;
+                  iob_address     <= MODE_REG;
+               end if;
+
+               ------------------------------------------------------
+               -- if startup is coomplete then go into idle mode,
+               -- get prepared to accept a new command, and schedule
+               -- the first refresh cycle
+               ------------------------------------------------------
+               if startup_refresh_count = 0 then
+                  state           <= s_idle;
+                  ready_for_new   <= '1';
+                  got_transaction <= '0';
+                  startup_refresh_count <= to_unsigned(2048 - cycles_per_refresh+1,14);
+               end if;
+               
+            when s_idle_in_6 => state <= s_idle_in_5;
+            when s_idle_in_5 => state <= s_idle_in_4;
+            when s_idle_in_4 => state <= s_idle_in_3;
+            when s_idle_in_3 => state <= s_idle_in_2;
+            when s_idle_in_2 => state <= s_idle_in_1;
+            when s_idle_in_1 => state <= s_idle;
+
+            when s_idle =>
+               -- Priority is to issue a refresh if one is outstanding
+               if pending_refresh = '1' or forcing_refresh = '1' then
+                 ------------------------------------------------------------------------
+                  -- Start the refresh cycle. 
+                  -- This tasks tRFC (66ns), so 6 idle cycles are needed @ 100MHz
+                  ------------------------------------------------------------------------
+                  state       <= s_idle_in_6;
+                  iob_command <= CMD_REFRESH;
+                  startup_refresh_count <= startup_refresh_count - cycles_per_refresh+1;
+               elsif got_transaction = '1' then
+                  --------------------------------
+                  -- Start the read or write cycle. 
+                  -- First task is to open the row
+                  --------------------------------
+                  state       <= s_open_in_2;
+                  iob_command <= CMD_ACTIVE;
+                  iob_address <= save_row;
+                  iob_bank    <= save_bank;
+               end if;               
+               
+            --------------------------------------------
+            -- Opening the row ready for reads or writes
+            --------------------------------------------
+            when s_open_in_2 => state <= s_open_in_1;
+
+            when s_open_in_1 =>
+               -- still waiting for row to open
+               if save_wr = '1' then
+                  state       <= s_write_1;
+                  iob_dq_hiz  <= '0';
+                  iob_data    <= save_data_in(15 downto 0); -- get the DQ bus out of HiZ early
+               else
+                  iob_dq_hiz  <= '1';
+                  state       <= s_read_1;
+               end if;
+               -- we will be ready for a new transaction next cycle!
+               ready_for_new   <= '1'; 
+               got_transaction <= '0';                  
+
+            ----------------------------------
+            -- Processing the read transaction
+            ----------------------------------
+            when s_read_1 =>
+               state           <= s_read_2;
+               iob_command     <= CMD_READ;
+               iob_address     <= save_col; 
+               iob_bank        <= save_bank;
+               iob_address(prefresh_cmd) <= '0'; -- A10 actually matters - it selects auto precharge
+               
+               -- Schedule reading the data values off the bus
+               data_ready_delay(data_ready_delay'high)   <= '1';
+               
+               -- Set the data masks to read all bytes
+               iob_dqm            <= (others => '0');
+               dqm_sr(1 downto 0) <= (others => '0');
+               
+            when s_read_2 =>
+               state <= s_read_3;
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  end if;
+               end if;
+               
+            when s_read_3 => 
+               state <= s_read_4;
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  end if;
+               end if;
+
+            when s_read_4 => 
+               state <= s_precharge;
+               -- can we do back-to-back read?
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '0' then
+                     state         <= s_read_1;
+                     ready_for_new <= '1'; -- we will be ready for a new transaction next cycle!
+                  else
+                     state <= s_open_in_2; -- we have to wait for the read data to come back before we swutch the bus into HiZ
+                  end if;
+               end if;
+
+            ------------------------------------------------------------------
+            -- Processing the write transaction
+            -------------------------------------------------------------------
+            when s_write_1 =>
+               state              <= s_write_2;
+               iob_command        <= CMD_WRITE;
+               iob_address        <= save_col; 
+               iob_address(prefresh_cmd)    <= '0'; -- A10 actually matters - it selects auto precharge
+               iob_bank           <= save_bank;
+               iob_dqm            <= NOT save_byte_enable(1 downto 0);    
+               dqm_sr(1 downto 0) <= NOT save_byte_enable(3 downto 2);    
+               iob_data           <= save_data_in(15 downto 0);
+               iob_data_next      <= save_data_in(31 downto 16);
+               
+            when s_write_2 =>
+               state           <= s_write_3;
+               iob_data        <= iob_data_next;
+               -- can we do a back-to-back write?
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '1' then
+                     -- back-to-back write?
+                     state           <= s_write_1;
+                     ready_for_new   <= '1';
+                     got_transaction <= '0';
+                  end if;
+                  -- Although it looks right in simulation you can't go write-to-read 
+                  -- here due to bus contention, as iob_dq_hiz takes a few ns.
+               end if;
+         
+            when s_write_3 =>  -- must wait tRDL, hence the extra idle state
+               -- back to back transaction?
+               if forcing_refresh = '0' and got_transaction = '1' and can_back_to_back = '1' then
+                  if save_wr = '1' then
+                     -- back-to-back write?
+                     state           <= s_write_1;
+                     ready_for_new   <= '1';
+                     got_transaction <= '0';
+                  else
+                     -- write-to-read switch?
+                     state           <= s_read_1;
+                     iob_dq_hiz      <= '1';
+                     ready_for_new   <= '1'; -- we will be ready for a new transaction next cycle!
+                     got_transaction <= '0';                  
+                  end if;
+               else
+                  iob_dq_hiz         <= '1';
+                  state              <= s_precharge;
+               end if;
+
+            -------------------------------------------------------------------
+            -- Closing the row off (this closes all banks)
+            -------------------------------------------------------------------
+            when s_precharge =>
+               state           <= s_idle_in_3;
+               iob_command     <= CMD_PRECHARGE;
+               iob_address(prefresh_cmd) <= '1'; -- A10 actually matters - it selects all banks or just one
+
+            -------------------------------------------------------------------
+            -- We should never get here, but if we do then reset the memory
+            -------------------------------------------------------------------
+            when others => 
+               state                 <= s_startup;
+               ready_for_new         <= '0';
+               startup_refresh_count <= startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+         end case;
+
+         if reset = '1' then  -- Sync reset
+            state                 <= s_startup;
+            ready_for_new         <= '0';
+            startup_refresh_count <= startup_refresh_max-to_unsigned(sdram_startup_cycles,14);
+         end if;
+      end if;      
+   end process;
+end Behavioral;

+ 88 - 0
vhdl/SSRAM.vhd

@@ -0,0 +1,88 @@
+--
+-- Inferrable Synchronous SRAM for XST synthesis
+--
+-- Version : 0220
+--
+-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--	http://www.opencores.org/cvsweb.shtml/t51/
+--
+-- Limitations :
+--
+-- File history :
+--	0208 : Initial release
+--	0218 : Fixed data out at write
+--	0220 : Added support for XST
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity SSRAM is
+	generic(
+		AddrWidth	: integer := 11;
+		DataWidth	: integer := 8
+	);
+	port(
+		clk			: in std_logic;
+		ce  		: in std_logic;
+		we  		: in std_logic;
+		A			: in std_logic_vector(AddrWidth - 1 downto 0);
+		DIn			: in std_logic_vector(DataWidth - 1 downto 0);
+		DOut		: out std_logic_vector(DataWidth - 1 downto 0)
+	);
+end SSRAM;
+
+architecture behaviour of SSRAM is
+
+	type Memory_Image is array (natural range <>) of std_logic_vector(DataWidth - 1 downto 0);
+	signal	RAM		: Memory_Image(0 to 2 ** AddrWidth - 1);
+	signal	A_r		: std_logic_vector(AddrWidth - 1 downto 0);
+
+begin
+
+	process (Clk)
+	begin
+        if rising_edge(clk) then
+            if (ce = '1' and we = '1') then
+				RAM(to_integer(unsigned(A))) <= DIn;
+			end if;
+			A_r <= A;
+		end if;
+	end process;
+
+	DOut <= RAM(to_integer(unsigned(A_r)));
+end;

+ 1094 - 0
vhdl/T80.vhd

@@ -0,0 +1,1094 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010
+-- Ver 302 fixed IO cycle timing, tested thanks to Alessandro.
+-- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle
+-- Ver 300 started tidyup. Rmoved some auto_wait bits from 0247 which caused problems
+--
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- Z80 compatible microprocessor core
+--
+-- Version : 0247
+--
+-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t80/
+--
+-- Limitations :
+--
+-- File history :
+--
+--      0208 : First complete release
+--
+--      0210 : Fixed wait and halt
+--
+--      0211 : Fixed Refresh addition and IM 1
+--
+--      0214 : Fixed mostly flags, only the block instructions now fail the zex regression test
+--
+--      0232 : Removed refresh address output for Mode > 1 and added DJNZ M1_n fix by Mike Johnson
+--
+--      0235 : Added clock enable and IM 2 fix by Mike Johnson
+--
+--      0237 : Changed 8080 I/O address output, added IntE output
+--
+--      0238 : Fixed (IX/IY+d) timing and 16 bit ADC and SBC zero flag
+--
+--      0240 : Added interrupt ack fix by Mike Johnson, changed (IX/IY+d) timing and changed flags in GB mode
+--
+--      0242 : Added I/O wait, fixed refresh address, moved some registers to RAM
+--
+--      0247 : Fixed bus req/ack cycle
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+use work.T80_Pack.all;
+
+entity T80 is
+	generic(
+		Mode   : integer := 0;  -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
+		IOWait : integer := 0;  -- 1 => Single cycle I/O, 1 => Std I/O cycle
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		RESET_n         : in  std_logic;
+		CLK_n           : in  std_logic;
+		CEN             : in  std_logic;
+		WAIT_n          : in  std_logic;
+		INT_n           : in  std_logic;
+		NMI_n           : in  std_logic;
+		BUSRQ_n         : in  std_logic;
+		M1_n            : out std_logic;
+		IORQ            : out std_logic;
+		NoRead          : out std_logic;
+		Write           : out std_logic;
+		RFSH_n          : out std_logic;
+		HALT_n          : out std_logic;
+		BUSAK_n         : out std_logic;
+		A               : out std_logic_vector(15 downto 0);
+		DInst           : in  std_logic_vector(7 downto 0);
+		DI              : in  std_logic_vector(7 downto 0);
+		DO              : out std_logic_vector(7 downto 0);
+		MC              : out std_logic_vector(2 downto 0);
+		TS              : out std_logic_vector(2 downto 0);
+		IntCycle_n      : out std_logic;
+		IntE            : out std_logic;
+		Stop            : out std_logic
+	);
+end T80;
+
+architecture rtl of T80 is
+
+	constant aNone              : std_logic_vector(2 downto 0) := "111";
+	constant aBC                : std_logic_vector(2 downto 0) := "000";
+	constant aDE                : std_logic_vector(2 downto 0) := "001";
+	constant aXY                : std_logic_vector(2 downto 0) := "010";
+	constant aIOA               : std_logic_vector(2 downto 0) := "100";
+	constant aSP                : std_logic_vector(2 downto 0) := "101";
+	constant aZI                : std_logic_vector(2 downto 0) := "110";
+
+	-- Registers
+	signal ACC, F               : std_logic_vector(7 downto 0);
+	signal Ap, Fp               : std_logic_vector(7 downto 0);
+	signal I                    : std_logic_vector(7 downto 0);
+	signal R                    : unsigned(7 downto 0);
+	signal SP, PC               : unsigned(15 downto 0);
+
+	signal RegDIH               : std_logic_vector(7 downto 0);
+	signal RegDIL               : std_logic_vector(7 downto 0);
+	signal RegBusA              : std_logic_vector(15 downto 0);
+	signal RegBusB              : std_logic_vector(15 downto 0);
+	signal RegBusC              : std_logic_vector(15 downto 0);
+	signal RegAddrA_r           : std_logic_vector(2 downto 0);
+	signal RegAddrA             : std_logic_vector(2 downto 0);
+	signal RegAddrB_r           : std_logic_vector(2 downto 0);
+	signal RegAddrB             : std_logic_vector(2 downto 0);
+	signal RegAddrC             : std_logic_vector(2 downto 0);
+	signal RegWEH               : std_logic;
+	signal RegWEL               : std_logic;
+	signal Alternate            : std_logic;
+
+	-- Help Registers
+	signal TmpAddr              : std_logic_vector(15 downto 0);        -- Temporary address register
+	signal IR                   : std_logic_vector(7 downto 0);         -- Instruction register
+	signal ISet                 : std_logic_vector(1 downto 0);         -- Instruction set selector
+	signal RegBusA_r            : std_logic_vector(15 downto 0);
+
+	signal ID16                 : signed(15 downto 0);
+	signal Save_Mux             : std_logic_vector(7 downto 0);
+
+	signal TState               : unsigned(2 downto 0);
+	signal MCycle               : std_logic_vector(2 downto 0);
+	signal IntE_FF1             : std_logic;
+	signal IntE_FF2             : std_logic;
+	signal Halt_FF              : std_logic;
+	signal BusReq_s             : std_logic;
+	signal BusAck               : std_logic;
+	signal ClkEn                : std_logic;
+	signal NMI_s                : std_logic;
+	signal INT_s                : std_logic;
+	signal IStatus              : std_logic_vector(1 downto 0);
+
+	signal DI_Reg               : std_logic_vector(7 downto 0);
+	signal T_Res                : std_logic;
+	signal XY_State             : std_logic_vector(1 downto 0);
+	signal Pre_XY_F_M           : std_logic_vector(2 downto 0);
+	signal NextIs_XY_Fetch      : std_logic;
+	signal XY_Ind               : std_logic;
+	signal No_BTR               : std_logic;
+	signal BTR_r                : std_logic;
+	signal Auto_Wait            : std_logic;
+	signal Auto_Wait_t1         : std_logic;
+	signal Auto_Wait_t2         : std_logic;
+	signal IncDecZ              : std_logic;
+
+	-- ALU signals
+	signal BusB                 : std_logic_vector(7 downto 0);
+	signal BusA                 : std_logic_vector(7 downto 0);
+	signal ALU_Q                : std_logic_vector(7 downto 0);
+	signal F_Out                : std_logic_vector(7 downto 0);
+
+	-- Registered micro code outputs
+	signal Read_To_Reg_r        : std_logic_vector(4 downto 0);
+	signal Arith16_r            : std_logic;
+	signal Z16_r                : std_logic;
+	signal ALU_Op_r             : std_logic_vector(3 downto 0);
+	signal Save_ALU_r           : std_logic;
+	signal PreserveC_r          : std_logic;
+	signal MCycles              : std_logic_vector(2 downto 0);
+
+	-- Micro code outputs
+	signal MCycles_d            : std_logic_vector(2 downto 0);
+	signal TStates              : std_logic_vector(2 downto 0);
+	signal IntCycle             : std_logic;
+	signal NMICycle             : std_logic;
+	signal Inc_PC               : std_logic;
+	signal Inc_WZ               : std_logic;
+	signal IncDec_16            : std_logic_vector(3 downto 0);
+	signal Prefix               : std_logic_vector(1 downto 0);
+	signal Read_To_Acc          : std_logic;
+	signal Read_To_Reg          : std_logic;
+	signal Set_BusB_To          : std_logic_vector(3 downto 0);
+	signal Set_BusA_To          : std_logic_vector(3 downto 0);
+	signal ALU_Op               : std_logic_vector(3 downto 0);
+	signal Save_ALU             : std_logic;
+	signal PreserveC            : std_logic;
+	signal Arith16              : std_logic;
+	signal Set_Addr_To          : std_logic_vector(2 downto 0);
+	signal Jump                 : std_logic;
+	signal JumpE                : std_logic;
+	signal JumpXY               : std_logic;
+	signal Call                 : std_logic;
+	signal RstP                 : std_logic;
+	signal LDZ                  : std_logic;
+	signal LDW                  : std_logic;
+	signal LDSPHL               : std_logic;
+	signal IORQ_i               : std_logic;
+	signal Special_LD           : std_logic_vector(2 downto 0);
+	signal ExchangeDH           : std_logic;
+	signal ExchangeRp           : std_logic;
+	signal ExchangeAF           : std_logic;
+	signal ExchangeRS           : std_logic;
+	signal I_DJNZ               : std_logic;
+	signal I_CPL                : std_logic;
+	signal I_CCF                : std_logic;
+	signal I_SCF                : std_logic;
+	signal I_RETN               : std_logic;
+	signal I_BT                 : std_logic;
+	signal I_BC                 : std_logic;
+	signal I_BTR                : std_logic;
+	signal I_RLD                : std_logic;
+	signal I_RRD                : std_logic;
+	signal I_INRC               : std_logic;
+	signal SetDI                : std_logic;
+	signal SetEI                : std_logic;
+	signal IMode                : std_logic_vector(1 downto 0);
+	signal Halt                 : std_logic;
+	signal XYbit_undoc          : std_logic;
+
+
+begin
+
+	mcode : T80_MCode
+		generic map(
+			Mode   => Mode,
+			Flag_C => Flag_C,
+			Flag_N => Flag_N,
+			Flag_P => Flag_P,
+			Flag_X => Flag_X,
+			Flag_H => Flag_H,
+			Flag_Y => Flag_Y,
+			Flag_Z => Flag_Z,
+			Flag_S => Flag_S)
+		port map(
+			IR          => IR,
+			ISet        => ISet,
+			MCycle      => MCycle,
+			F           => F,
+			NMICycle    => NMICycle,
+			IntCycle    => IntCycle,
+			XY_State    => XY_State,
+			MCycles     => MCycles_d,
+			TStates     => TStates,
+			Prefix      => Prefix,
+			Inc_PC      => Inc_PC,
+			Inc_WZ      => Inc_WZ,
+			IncDec_16   => IncDec_16,
+			Read_To_Acc => Read_To_Acc,
+			Read_To_Reg => Read_To_Reg,
+			Set_BusB_To => Set_BusB_To,
+			Set_BusA_To => Set_BusA_To,
+			ALU_Op      => ALU_Op,
+			Save_ALU    => Save_ALU,
+			PreserveC   => PreserveC,
+			Arith16     => Arith16,
+			Set_Addr_To => Set_Addr_To,
+			IORQ        => IORQ_i,
+			Jump        => Jump,
+			JumpE       => JumpE,
+			JumpXY      => JumpXY,
+			Call        => Call,
+			RstP        => RstP,
+			LDZ         => LDZ,
+			LDW         => LDW,
+			LDSPHL      => LDSPHL,
+			Special_LD  => Special_LD,
+			ExchangeDH  => ExchangeDH,
+			ExchangeRp  => ExchangeRp,
+			ExchangeAF  => ExchangeAF,
+			ExchangeRS  => ExchangeRS,
+			I_DJNZ      => I_DJNZ,
+			I_CPL       => I_CPL,
+			I_CCF       => I_CCF,
+			I_SCF       => I_SCF,
+			I_RETN      => I_RETN,
+			I_BT        => I_BT,
+			I_BC        => I_BC,
+			I_BTR       => I_BTR,
+			I_RLD       => I_RLD,
+			I_RRD       => I_RRD,
+			I_INRC      => I_INRC,
+			SetDI       => SetDI,
+			SetEI       => SetEI,
+			IMode       => IMode,
+			Halt        => Halt,
+			NoRead      => NoRead,
+			Write       => Write,
+			XYbit_undoc => XYbit_undoc);
+
+	alu : T80_ALU
+		generic map(
+			Mode   => Mode,
+			Flag_C => Flag_C,
+			Flag_N => Flag_N,
+			Flag_P => Flag_P,
+			Flag_X => Flag_X,
+			Flag_H => Flag_H,
+			Flag_Y => Flag_Y,
+			Flag_Z => Flag_Z,
+			Flag_S => Flag_S)
+		port map(
+			Arith16 => Arith16_r,
+			Z16     => Z16_r,
+			ALU_Op  => ALU_Op_r,
+			IR      => IR(5 downto 0),
+			ISet    => ISet,
+			BusA    => BusA,
+			BusB    => BusB,
+			F_In    => F,
+			Q       => ALU_Q,
+			F_Out   => F_Out);
+
+	ClkEn <= CEN and not BusAck;
+
+	T_Res <= '1' when TState = unsigned(TStates) else '0';
+
+	NextIs_XY_Fetch <= '1' when XY_State /= "00" and XY_Ind = '0' and
+							((Set_Addr_To = aXY) or
+							(MCycle = "001" and IR = "11001011") or
+							(MCycle = "001" and IR = "00110110")) else '0';
+
+	Save_Mux <= BusB when ExchangeRp = '1' else
+		DI_Reg when Save_ALU_r = '0' else
+		ALU_Q;
+
+	process (RESET_n, CLK_n)
+	begin
+		if RESET_n = '0' then
+			PC <= (others => '0');  -- Program Counter
+			A <= (others => '0');
+			TmpAddr <= (others => '0');
+			IR <= "00000000";
+			ISet <= "00";
+			XY_State <= "00";
+			IStatus <= "00";
+			MCycles <= "000";
+			DO <= "00000000";
+
+			ACC <= (others => '1');
+			F <= (others => '1');
+			Ap <= (others => '1');
+			Fp <= (others => '1');
+			I <= (others => '0');
+			R <= (others => '0');
+			SP <= (others => '1');
+			Alternate <= '0';
+
+			Read_To_Reg_r <= "00000";
+			F <= (others => '1');
+			Arith16_r <= '0';
+			BTR_r <= '0';
+			Z16_r <= '0';
+			ALU_Op_r <= "0000";
+			Save_ALU_r <= '0';
+			PreserveC_r <= '0';
+			XY_Ind <= '0';
+
+		elsif CLK_n'event and CLK_n = '1' then
+
+			if ClkEn = '1' then
+
+			ALU_Op_r <= "0000";
+			Save_ALU_r <= '0';
+			Read_To_Reg_r <= "00000";
+
+			MCycles <= MCycles_d;
+
+			if IMode /= "11" then
+				IStatus <= IMode;
+			end if;
+
+			Arith16_r <= Arith16;
+			PreserveC_r <= PreserveC;
+			if ISet = "10" and ALU_OP(2) = '0' and ALU_OP(0) = '1' and MCycle = "011" then
+				Z16_r <= '1';
+			else
+				Z16_r <= '0';
+			end if;
+
+			if MCycle  = "001" and TState(2) = '0' then
+			-- MCycle = 1 and TState = 1, 2, or 3
+
+				if TState = 2 and Wait_n = '1' then
+					if Mode < 2 then
+						A(7 downto 0) <= std_logic_vector(R);
+						A(15 downto 8) <= I;
+						R(6 downto 0) <= R(6 downto 0) + 1;
+					end if;
+
+					if Jump = '0' and Call = '0' and NMICycle = '0' and IntCycle = '0' and not (Halt_FF = '1' or Halt = '1') then
+						PC <= PC + 1;
+					end if;
+
+					if IntCycle = '1' and IStatus = "01" then
+						IR <= "11111111";
+					elsif Halt_FF = '1' or (IntCycle = '1' and IStatus = "10") or NMICycle = '1' then
+						IR <= "00000000";
+					else
+						IR <= DInst;
+					end if;
+
+					ISet <= "00";
+					if Prefix /= "00" then
+						if Prefix = "11" then
+							if IR(5) = '1' then
+								XY_State <= "10";
+							else
+								XY_State <= "01";
+							end if;
+						else
+							if Prefix = "10" then
+								XY_State <= "00";
+								XY_Ind <= '0';
+							end if;
+							ISet <= Prefix;
+						end if;
+					else
+						XY_State <= "00";
+						XY_Ind <= '0';
+					end if;
+				end if;
+
+			else
+			-- either (MCycle > 1) OR (MCycle = 1 AND TState > 3)
+
+				if MCycle = "110" then
+					XY_Ind <= '1';
+					if Prefix = "01" then
+						ISet <= "01";
+					end if;
+				end if;
+
+				if T_Res = '1' then
+					BTR_r <= (I_BT or I_BC or I_BTR) and not No_BTR;
+					if Jump = '1' then
+						A(15 downto 8) <= DI_Reg;
+						A(7 downto 0) <= TmpAddr(7 downto 0);
+						PC(15 downto 8) <= unsigned(DI_Reg);
+						PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
+					elsif JumpXY = '1' then
+						A <= RegBusC;
+						PC <= unsigned(RegBusC);
+					elsif Call = '1' or RstP = '1' then
+						A <= TmpAddr;
+						PC <= unsigned(TmpAddr);
+					elsif MCycle = MCycles and NMICycle = '1' then
+						A <= "0000000001100110";
+						PC <= "0000000001100110";
+					elsif MCycle = "011" and IntCycle = '1' and IStatus = "10" then
+						A(15 downto 8) <= I;
+						A(7 downto 0) <= TmpAddr(7 downto 0);
+						PC(15 downto 8) <= unsigned(I);
+						PC(7 downto 0) <= unsigned(TmpAddr(7 downto 0));
+					else
+						case Set_Addr_To is
+						when aXY =>
+							if XY_State = "00" then
+								A <= RegBusC;
+							else
+								if NextIs_XY_Fetch = '1' then
+									A <= std_logic_vector(PC);
+								else
+									A <= TmpAddr;
+								end if;
+							end if;
+						when aIOA =>
+							if Mode = 3 then
+								-- Memory map I/O on GBZ80
+								A(15 downto 8) <= (others => '1');
+							elsif Mode = 2 then
+								-- Duplicate I/O address on 8080
+								A(15 downto 8) <= DI_Reg;
+							else
+								A(15 downto 8) <= ACC;
+							end if;
+							A(7 downto 0) <= DI_Reg;
+						when aSP =>
+							A <= std_logic_vector(SP);
+						when aBC =>
+							if Mode = 3 and IORQ_i = '1' then
+								-- Memory map I/O on GBZ80
+								A(15 downto 8) <= (others => '1');
+								A(7 downto 0) <= RegBusC(7 downto 0);
+							else
+								A <= RegBusC;
+							end if;
+						when aDE =>
+							A <= RegBusC;
+						when aZI =>
+							if Inc_WZ = '1' then
+								A <= std_logic_vector(unsigned(TmpAddr) + 1);
+							else
+								A(15 downto 8) <= DI_Reg;
+								A(7 downto 0) <= TmpAddr(7 downto 0);
+							end if;
+						when others =>
+							A <= std_logic_vector(PC);
+						end case;
+					end if;
+
+					Save_ALU_r <= Save_ALU;
+					ALU_Op_r <= ALU_Op;
+
+					if I_CPL = '1' then
+						-- CPL
+						ACC <= not ACC;
+						F(Flag_Y) <= not ACC(5);
+						F(Flag_H) <= '1';
+						F(Flag_X) <= not ACC(3);
+						F(Flag_N) <= '1';
+					end if;
+					if I_CCF = '1' then
+						-- CCF
+						F(Flag_C) <= not F(Flag_C);
+						F(Flag_Y) <= ACC(5);
+						F(Flag_H) <= F(Flag_C);
+						F(Flag_X) <= ACC(3);
+						F(Flag_N) <= '0';
+					end if;
+					if I_SCF = '1' then
+						-- SCF
+						F(Flag_C) <= '1';
+						F(Flag_Y) <= ACC(5);
+						F(Flag_H) <= '0';
+						F(Flag_X) <= ACC(3);
+						F(Flag_N) <= '0';
+					end if;
+				end if;
+
+				if TState = 2 and Wait_n = '1' then
+					if ISet = "01" and MCycle = "111" then
+						IR <= DInst;
+					end if;
+					if JumpE = '1' then
+						PC <= unsigned(signed(PC) + signed(DI_Reg));
+					elsif Inc_PC = '1' then
+						PC <= PC + 1;
+					end if;
+					if BTR_r = '1' then
+						PC <= PC - 2;
+					end if;
+					if RstP = '1' then
+						TmpAddr <= (others =>'0');
+						TmpAddr(5 downto 3) <= IR(5 downto 3);
+					end if;
+				end if;
+				if TState = 3 and MCycle = "110" then
+					TmpAddr <= std_logic_vector(signed(RegBusC) + signed(DI_Reg));
+				end if;
+
+				if (TState = 2 and Wait_n = '1') or (TState = 4 and MCycle = "001") then
+					if IncDec_16(2 downto 0) = "111" then
+						if IncDec_16(3) = '1' then
+							SP <= SP - 1;
+						else
+							SP <= SP + 1;
+						end if;
+					end if;
+				end if;
+
+				if LDSPHL = '1' then
+					SP <= unsigned(RegBusC);
+				end if;
+				if ExchangeAF = '1' then
+					Ap <= ACC;
+					ACC <= Ap;
+					Fp <= F;
+					F <= Fp;
+				end if;
+				if ExchangeRS = '1' then
+					Alternate <= not Alternate;
+				end if;
+			end if;
+
+			if TState = 3 then
+				if LDZ = '1' then
+					TmpAddr(7 downto 0) <= DI_Reg;
+				end if;
+				if LDW = '1' then
+					TmpAddr(15 downto 8) <= DI_Reg;
+				end if;
+
+				if Special_LD(2) = '1' then
+					case Special_LD(1 downto 0) is
+					when "00" =>
+						ACC <= I;
+						F(Flag_P) <= IntE_FF2;
+					when "01" =>
+						ACC <= std_logic_vector(R);
+						F(Flag_P) <= IntE_FF2;
+					when "10" =>
+						I <= ACC;
+					when others =>
+						R <= unsigned(ACC);
+					end case;
+				end if;
+			end if;
+
+			if (I_DJNZ = '0' and Save_ALU_r = '1') or ALU_Op_r = "1001" then
+				if Mode = 3 then
+					F(6) <= F_Out(6);
+					F(5) <= F_Out(5);
+					F(7) <= F_Out(7);
+					if PreserveC_r = '0' then
+						F(4) <= F_Out(4);
+					end if;
+				else
+					F(7 downto 1) <= F_Out(7 downto 1);
+					if PreserveC_r = '0' then
+						F(Flag_C) <= F_Out(0);
+					end if;
+				end if;
+			end if;
+			if T_Res = '1' and I_INRC = '1' then
+				F(Flag_H) <= '0';
+				F(Flag_N) <= '0';
+				if DI_Reg(7 downto 0) = "00000000" then
+					F(Flag_Z) <= '1';
+				else
+					F(Flag_Z) <= '0';
+				end if;
+				F(Flag_S) <= DI_Reg(7);
+				F(Flag_P) <= not (DI_Reg(0) xor DI_Reg(1) xor DI_Reg(2) xor DI_Reg(3) xor
+					DI_Reg(4) xor DI_Reg(5) xor DI_Reg(6) xor DI_Reg(7));
+			end if;
+
+			if TState = 1 then
+				DO <= BusB;
+				if I_RLD = '1' then
+					DO(3 downto 0) <= BusA(3 downto 0);
+					DO(7 downto 4) <= BusB(3 downto 0);
+				end if;
+				if I_RRD = '1' then
+					DO(3 downto 0) <= BusB(7 downto 4);
+					DO(7 downto 4) <= BusA(3 downto 0);
+				end if;
+			end if;
+
+			if T_Res = '1' then
+				Read_To_Reg_r(3 downto 0) <= Set_BusA_To;
+				Read_To_Reg_r(4) <= Read_To_Reg;
+				if Read_To_Acc = '1' then
+					Read_To_Reg_r(3 downto 0) <= "0111";
+					Read_To_Reg_r(4) <= '1';
+				end if;
+			end if;
+
+			if TState = 1 and I_BT = '1' then
+				F(Flag_X) <= ALU_Q(3);
+				F(Flag_Y) <= ALU_Q(1);
+				F(Flag_H) <= '0';
+				F(Flag_N) <= '0';
+			end if;
+			if I_BC = '1' or I_BT = '1' then
+				F(Flag_P) <= IncDecZ;
+			end if;
+
+			if (TState = 1 and Save_ALU_r = '0') or
+				(Save_ALU_r = '1' and ALU_OP_r /= "0111") then
+				case Read_To_Reg_r is
+				when "10111" =>
+					ACC <= Save_Mux;
+				when "10110" =>
+					DO <= Save_Mux;
+				when "11000" =>
+					SP(7 downto 0) <= unsigned(Save_Mux);
+				when "11001" =>
+					SP(15 downto 8) <= unsigned(Save_Mux);
+				when "11011" =>
+					F <= Save_Mux;
+				when others =>
+				end case;
+				if XYbit_undoc='1' then
+					DO <= ALU_Q;
+				end if;
+			end if;
+
+		end if;
+
+		end if;
+
+	end process;
+
+---------------------------------------------------------------------------
+--
+-- BC('), DE('), HL('), IX and IY
+--
+---------------------------------------------------------------------------
+	process (CLK_n)
+	begin
+		if CLK_n'event and CLK_n = '1' then
+			if ClkEn = '1' then
+				-- Bus A / Write
+				RegAddrA_r <= Alternate & Set_BusA_To(2 downto 1);
+				if XY_Ind = '0' and XY_State /= "00" and Set_BusA_To(2 downto 1) = "10" then
+					RegAddrA_r <= XY_State(1) & "11";
+				end if;
+
+				-- Bus B
+				RegAddrB_r <= Alternate & Set_BusB_To(2 downto 1);
+				if XY_Ind = '0' and XY_State /= "00" and Set_BusB_To(2 downto 1) = "10" then
+					RegAddrB_r <= XY_State(1) & "11";
+				end if;
+
+				-- Address from register
+				RegAddrC <= Alternate & Set_Addr_To(1 downto 0);
+				-- Jump (HL), LD SP,HL
+				if (JumpXY = '1' or LDSPHL = '1') then
+					RegAddrC <= Alternate & "10";
+				end if;
+				if ((JumpXY = '1' or LDSPHL = '1') and XY_State /= "00") or (MCycle = "110") then
+					RegAddrC <= XY_State(1) & "11";
+				end if;
+
+				if I_DJNZ = '1' and Save_ALU_r = '1' and Mode < 2 then
+					IncDecZ <= F_Out(Flag_Z);
+				end if;
+				if (TState = 2 or (TState = 3 and MCycle = "001")) and IncDec_16(2 downto 0) = "100" then
+					if ID16 = 0 then
+						IncDecZ <= '0';
+					else
+						IncDecZ <= '1';
+					end if;
+				end if;
+
+				RegBusA_r <= RegBusA;
+			end if;
+		end if;
+	end process;
+
+	RegAddrA <=
+			-- 16 bit increment/decrement
+			Alternate & IncDec_16(1 downto 0) when (TState = 2 or
+				(TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and XY_State = "00" else
+			XY_State(1) & "11" when (TState = 2 or
+				(TState = 3 and MCycle = "001" and IncDec_16(2) = '1')) and IncDec_16(1 downto 0) = "10" else
+			-- EX HL,DL
+			Alternate & "10" when ExchangeDH = '1' and TState = 3 else
+			Alternate & "01" when ExchangeDH = '1' and TState = 4 else
+			-- Bus A / Write
+			RegAddrA_r;
+
+	RegAddrB <=
+			-- EX HL,DL
+			Alternate & "01" when ExchangeDH = '1' and TState = 3 else
+			-- Bus B
+			RegAddrB_r;
+
+	ID16 <= signed(RegBusA) - 1 when IncDec_16(3) = '1' else
+			signed(RegBusA) + 1;
+
+	process (Save_ALU_r, Auto_Wait_t1, ALU_OP_r, Read_To_Reg_r,
+			ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
+	begin
+		RegWEH <= '0';
+		RegWEL <= '0';
+		if (TState = 1 and Save_ALU_r = '0') or
+			(Save_ALU_r = '1' and ALU_OP_r /= "0111") then
+			case Read_To_Reg_r is
+			when "10000" | "10001" | "10010" | "10011" | "10100" | "10101" =>
+				RegWEH <= not Read_To_Reg_r(0);
+				RegWEL <= Read_To_Reg_r(0);
+			when others =>
+			end case;
+		end if;
+
+		if ExchangeDH = '1' and (TState = 3 or TState = 4) then
+			RegWEH <= '1';
+			RegWEL <= '1';
+		end if;
+
+		if IncDec_16(2) = '1' and ((TState = 2 and Wait_n = '1' and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
+			case IncDec_16(1 downto 0) is
+			when "00" | "01" | "10" =>
+				RegWEH <= '1';
+				RegWEL <= '1';
+			when others =>
+			end case;
+		end if;
+	end process;
+
+	process (Save_Mux, RegBusB, RegBusA_r, ID16,
+			ExchangeDH, IncDec_16, MCycle, TState, Wait_n)
+	begin
+		RegDIH <= Save_Mux;
+		RegDIL <= Save_Mux;
+
+		if ExchangeDH = '1' and TState = 3 then
+			RegDIH <= RegBusB(15 downto 8);
+			RegDIL <= RegBusB(7 downto 0);
+		end if;
+		if ExchangeDH = '1' and TState = 4 then
+			RegDIH <= RegBusA_r(15 downto 8);
+			RegDIL <= RegBusA_r(7 downto 0);
+		end if;
+
+		if IncDec_16(2) = '1' and ((TState = 2 and MCycle /= "001") or (TState = 3 and MCycle = "001")) then
+			RegDIH <= std_logic_vector(ID16(15 downto 8));
+			RegDIL <= std_logic_vector(ID16(7 downto 0));
+		end if;
+	end process;
+
+	Regs : T80_Reg
+		port map(
+			Clk => CLK_n,
+			CEN => ClkEn,
+			WEH => RegWEH,
+			WEL => RegWEL,
+			AddrA => RegAddrA,
+			AddrB => RegAddrB,
+			AddrC => RegAddrC,
+			DIH => RegDIH,
+			DIL => RegDIL,
+			DOAH => RegBusA(15 downto 8),
+			DOAL => RegBusA(7 downto 0),
+			DOBH => RegBusB(15 downto 8),
+			DOBL => RegBusB(7 downto 0),
+			DOCH => RegBusC(15 downto 8),
+			DOCL => RegBusC(7 downto 0));
+
+---------------------------------------------------------------------------
+--
+-- Buses
+--
+---------------------------------------------------------------------------
+	process (CLK_n)
+	begin
+		if CLK_n'event and CLK_n = '1' then
+			if ClkEn = '1' then
+			case Set_BusB_To is
+			when "0111" =>
+				BusB <= ACC;
+			when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
+				if Set_BusB_To(0) = '1' then
+					BusB <= RegBusB(7 downto 0);
+				else
+					BusB <= RegBusB(15 downto 8);
+				end if;
+			when "0110" =>
+				BusB <= DI_Reg;
+			when "1000" =>
+				BusB <= std_logic_vector(SP(7 downto 0));
+			when "1001" =>
+				BusB <= std_logic_vector(SP(15 downto 8));
+			when "1010" =>
+				BusB <= "00000001";
+			when "1011" =>
+				BusB <= F;
+			when "1100" =>
+				BusB <= std_logic_vector(PC(7 downto 0));
+			when "1101" =>
+				BusB <= std_logic_vector(PC(15 downto 8));
+			when "1110" =>
+				BusB <= "00000000";
+			when others =>
+				BusB <= "--------";
+			end case;
+
+			case Set_BusA_To is
+			when "0111" =>
+				BusA <= ACC;
+			when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" =>
+				if Set_BusA_To(0) = '1' then
+					BusA <= RegBusA(7 downto 0);
+				else
+					BusA <= RegBusA(15 downto 8);
+				end if;
+			when "0110" =>
+				BusA <= DI_Reg;
+			when "1000" =>
+				BusA <= std_logic_vector(SP(7 downto 0));
+			when "1001" =>
+				BusA <= std_logic_vector(SP(15 downto 8));
+			when "1010" =>
+				BusA <= "00000000";
+			when others =>
+				BusB <= "--------";
+			end case;
+			if XYbit_undoc='1' then
+				BusA <= DI_Reg;
+				BusB <= DI_Reg;
+			end if;
+			end if;
+		end if;
+	end process;
+
+---------------------------------------------------------------------------
+--
+-- Generate external control signals
+--
+---------------------------------------------------------------------------
+	process (RESET_n,CLK_n)
+	begin
+		if RESET_n = '0' then
+			RFSH_n <= '1';
+		elsif CLK_n'event and CLK_n = '1' then
+			if CEN = '1' then
+			if MCycle = "001" and ((TState = 2  and Wait_n = '1') or TState = 3) then
+				RFSH_n <= '0';
+			else
+				RFSH_n <= '1';
+			end if;
+			end if;
+		end if;
+	end process;
+
+	MC <= std_logic_vector(MCycle);
+	TS <= std_logic_vector(TState);
+	DI_Reg <= DI;
+	HALT_n <= not Halt_FF;
+	BUSAK_n <= not BusAck;
+	IntCycle_n <= not IntCycle;
+	IntE <= IntE_FF1;
+	IORQ <= IORQ_i;
+	Stop <= I_DJNZ;
+
+-------------------------------------------------------------------------
+--
+-- Syncronise inputs
+--
+-------------------------------------------------------------------------
+	process (RESET_n, CLK_n)
+		variable OldNMI_n : std_logic;
+	begin
+		if RESET_n = '0' then
+			BusReq_s <= '0';
+			INT_s <= '0';
+			NMI_s <= '0';
+			OldNMI_n := '0';
+		elsif CLK_n'event and CLK_n = '1' then
+			if CEN = '1' then
+			BusReq_s <= not BUSRQ_n;
+			INT_s <= not INT_n;
+			if NMICycle = '1' then
+				NMI_s <= '0';
+			elsif NMI_n = '0' and OldNMI_n = '1' then
+				NMI_s <= '1';
+			end if;
+			OldNMI_n := NMI_n;
+			end if;
+		end if;
+	end process;
+
+-------------------------------------------------------------------------
+--
+-- Main state machine
+--
+-------------------------------------------------------------------------
+	process (RESET_n, CLK_n)
+	begin
+		if RESET_n = '0' then
+			MCycle <= "001";
+			TState <= "000";
+			Pre_XY_F_M <= "000";
+			Halt_FF <= '0';
+			BusAck <= '0';
+			NMICycle <= '0';
+			IntCycle <= '0';
+			IntE_FF1 <= '0';
+			IntE_FF2 <= '0';
+			No_BTR <= '0';
+			Auto_Wait_t1 <= '0';
+			Auto_Wait_t2 <= '0';
+			M1_n <= '1';
+		elsif CLK_n'event and CLK_n = '1' then
+			if CEN = '1' then
+			Auto_Wait_t1 <= Auto_Wait;
+			Auto_Wait_t2 <= Auto_Wait_t1;
+			No_BTR <= (I_BT and (not IR(4) or not F(Flag_P))) or
+					(I_BC and (not IR(4) or F(Flag_Z) or not F(Flag_P))) or
+					(I_BTR and (not IR(4) or F(Flag_Z)));
+			if TState = 2 then
+				if SetEI = '1' then
+					IntE_FF1 <= '1';
+					IntE_FF2 <= '1';
+				end if;
+				if I_RETN = '1' then
+					IntE_FF1 <= IntE_FF2;
+				end if;
+			end if;
+			if TState = 3 then
+				if SetDI = '1' then
+					IntE_FF1 <= '0';
+					IntE_FF2 <= '0';
+				end if;
+			end if;
+			if IntCycle = '1' or NMICycle = '1' then
+				Halt_FF <= '0';
+			end if;
+			if MCycle = "001" and TState = 2 and Wait_n = '1' then
+				M1_n <= '1';
+			end if;
+			if BusReq_s = '1' and BusAck = '1' then
+			else
+				BusAck <= '0';
+				if TState = 2 and Wait_n = '0' then
+				elsif T_Res = '1' then
+					if Halt = '1' then
+						Halt_FF <= '1';
+					end if;
+					if BusReq_s = '1' then
+						BusAck <= '1';
+					else
+						TState <= "001";
+						if NextIs_XY_Fetch = '1' then
+							MCycle <= "110";
+							Pre_XY_F_M <= MCycle;
+							if IR = "00110110" and Mode = 0 then
+								Pre_XY_F_M <= "010";
+							end if;
+						elsif (MCycle = "111") or
+							(MCycle = "110" and Mode = 1 and ISet /= "01") then
+							MCycle <= std_logic_vector(unsigned(Pre_XY_F_M) + 1);
+						elsif (MCycle = MCycles) or
+							No_BTR = '1' or
+							(MCycle = "010" and I_DJNZ = '1' and IncDecZ = '1') then
+							M1_n <= '0';
+							MCycle <= "001";
+							IntCycle <= '0';
+							NMICycle <= '0';
+							if NMI_s = '1' and Prefix = "00" then
+								NMICycle <= '1';
+								IntE_FF1 <= '0';
+							elsif (IntE_FF1 = '1' and INT_s = '1') and Prefix = "00" and SetEI = '0' then
+								IntCycle <= '1';
+								IntE_FF1 <= '0';
+								IntE_FF2 <= '0';
+							end if;
+						else
+							MCycle <= std_logic_vector(unsigned(MCycle) + 1);
+						end if;
+					end if;
+				else
+					if Auto_Wait = '1' nand Auto_Wait_t2 = '0' then
+
+						TState <= TState + 1;
+					end if;
+				end if;
+			end if;
+			if TState = 0 then
+				M1_n <= '0';
+			end if;
+			end if;
+		end if;
+	end process;
+
+	process (IntCycle, NMICycle, MCycle)
+	begin
+		Auto_Wait <= '0';
+		if IntCycle = '1' or NMICycle = '1' then
+			if MCycle = "001" then
+				Auto_Wait <= '1';
+			end if;
+		end if;
+	end process;
+
+end;

+ 371 - 0
vhdl/T80_ALU.vhd

@@ -0,0 +1,371 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 301 parity flag is just parity for 8080, also overflow for Z80, by Sean Riddle
+-- Ver 300 started tidyup
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- Z80 compatible microprocessor core
+--
+-- Version : 0247
+--
+-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t80/
+--
+-- Limitations :
+--
+-- File history :
+--
+--      0214 : Fixed mostly flags, only the block instructions now fail the zex regression test
+--
+--      0238 : Fixed zero flag for 16 bit SBC and ADC
+--
+--      0240 : Added GB operations
+--
+--      0242 : Cleanup
+--
+--      0247 : Cleanup
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity T80_ALU is
+	generic(
+		Mode : integer := 0;
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		Arith16         : in  std_logic;
+		Z16             : in  std_logic;
+		ALU_Op          : in  std_logic_vector(3 downto 0);
+		IR              : in  std_logic_vector(5 downto 0);
+		ISet            : in  std_logic_vector(1 downto 0);
+		BusA            : in  std_logic_vector(7 downto 0);
+		BusB            : in  std_logic_vector(7 downto 0);
+		F_In            : in  std_logic_vector(7 downto 0);
+		Q               : out std_logic_vector(7 downto 0);
+		F_Out           : out std_logic_vector(7 downto 0)
+	);
+end T80_ALU;
+
+architecture rtl of T80_ALU is
+
+	procedure AddSub(A        : std_logic_vector;
+					 B        : std_logic_vector;
+					 Sub      : std_logic;
+					 Carry_In : std_logic;
+			  signal Res      : out std_logic_vector;
+			  signal Carry    : out std_logic) is
+
+		variable B_i          : unsigned(A'length - 1 downto 0);
+		variable Res_i        : unsigned(A'length + 1 downto 0);
+	begin
+		if Sub = '1' then
+			B_i := not unsigned(B);
+		else
+			B_i :=     unsigned(B);
+		end if;
+
+		Res_i := unsigned("0" & A & Carry_In) + unsigned("0" & B_i & "1");
+		Carry <= Res_i(A'length + 1);
+		Res <= std_logic_vector(Res_i(A'length downto 1));
+	end;
+
+	-- AddSub variables (temporary signals)
+	signal UseCarry                : std_logic;
+	signal Carry7_v                : std_logic;
+	signal Overflow_v              : std_logic;
+	signal HalfCarry_v             : std_logic;
+	signal Carry_v                 : std_logic;
+	signal Q_v                     : std_logic_vector(7 downto 0);
+
+	signal BitMask                 : std_logic_vector(7 downto 0);
+
+begin
+
+	with IR(5 downto 3) select BitMask <= "00000001" when "000",
+										  "00000010" when "001",
+										  "00000100" when "010",
+										  "00001000" when "011",
+										  "00010000" when "100",
+										  "00100000" when "101",
+										  "01000000" when "110",
+										  "10000000" when others;
+
+	UseCarry <= not ALU_Op(2) and ALU_Op(0);
+	AddSub(BusA(3 downto 0), BusB(3 downto 0), ALU_Op(1), ALU_Op(1) xor (UseCarry and F_In(Flag_C)), Q_v(3 downto 0), HalfCarry_v);
+	AddSub(BusA(6 downto 4), BusB(6 downto 4), ALU_Op(1), HalfCarry_v, Q_v(6 downto 4), Carry7_v);
+	AddSub(BusA(7 downto 7), BusB(7 downto 7), ALU_Op(1), Carry7_v, Q_v(7 downto 7), Carry_v);
+
+	-- bug fix - parity flag is just parity for 8080, also overflow for Z80
+	process (Carry_v, Carry7_v, Q_v)
+	begin
+		if(Mode=2) then
+			OverFlow_v <= not (Q_v(0) xor Q_v(1) xor Q_v(2) xor Q_v(3) xor
+					   Q_v(4) xor Q_v(5) xor Q_v(6) xor Q_v(7));  else
+			OverFlow_v <= Carry_v xor Carry7_v;
+		end if;
+	end process;
+
+	process (Arith16, ALU_OP, F_In, BusA, BusB, IR, Q_v, Carry_v, HalfCarry_v, OverFlow_v, BitMask, ISet, Z16)
+		variable Q_t : std_logic_vector(7 downto 0);
+		variable DAA_Q : unsigned(8 downto 0);
+	begin
+		Q_t := "--------";
+		F_Out <= F_In;
+		DAA_Q := "---------";
+		case ALU_Op is
+		when "0000" | "0001" |  "0010" | "0011" | "0100" | "0101" | "0110" | "0111" =>
+			F_Out(Flag_N) <= '0';
+			F_Out(Flag_C) <= '0';
+			case ALU_OP(2 downto 0) is
+			when "000" | "001" => -- ADD, ADC
+				Q_t := Q_v;
+				F_Out(Flag_C) <= Carry_v;
+				F_Out(Flag_H) <= HalfCarry_v;
+				F_Out(Flag_P) <= OverFlow_v;
+			when "010" | "011" | "111" => -- SUB, SBC, CP
+				Q_t := Q_v;
+				F_Out(Flag_N) <= '1';
+				F_Out(Flag_C) <= not Carry_v;
+				F_Out(Flag_H) <= not HalfCarry_v;
+				F_Out(Flag_P) <= OverFlow_v;
+			when "100" => -- AND
+				Q_t(7 downto 0) := BusA and BusB;
+				F_Out(Flag_H) <= '1';
+			when "101" => -- XOR
+				Q_t(7 downto 0) := BusA xor BusB;
+				F_Out(Flag_H) <= '0';
+			when others => -- OR "110"
+				Q_t(7 downto 0) := BusA or BusB;
+				F_Out(Flag_H) <= '0';
+			end case;
+			if ALU_Op(2 downto 0) = "111" then -- CP
+				F_Out(Flag_X) <= BusB(3);
+				F_Out(Flag_Y) <= BusB(5);
+			else
+				F_Out(Flag_X) <= Q_t(3);
+				F_Out(Flag_Y) <= Q_t(5);
+			end if;
+			if Q_t(7 downto 0) = "00000000" then
+				F_Out(Flag_Z) <= '1';
+				if Z16 = '1' then
+					F_Out(Flag_Z) <= F_In(Flag_Z);      -- 16 bit ADC,SBC
+				end if;
+			else
+				F_Out(Flag_Z) <= '0';
+			end if;
+			F_Out(Flag_S) <= Q_t(7);
+			case ALU_Op(2 downto 0) is
+			when "000" | "001" | "010" | "011" | "111" => -- ADD, ADC, SUB, SBC, CP
+			when others =>
+				F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor
+					Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7));
+			end case;
+			if Arith16 = '1' then
+				F_Out(Flag_S) <= F_In(Flag_S);
+				F_Out(Flag_Z) <= F_In(Flag_Z);
+				F_Out(Flag_P) <= F_In(Flag_P);
+			end if;
+		when "1100" =>
+			-- DAA
+			F_Out(Flag_H) <= F_In(Flag_H);
+			F_Out(Flag_C) <= F_In(Flag_C);
+			DAA_Q(7 downto 0) := unsigned(BusA);
+			DAA_Q(8) := '0';
+			if F_In(Flag_N) = '0' then
+				-- After addition
+				-- Alow > 9 or H = 1
+				if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then
+					if (DAA_Q(3 downto 0) > 9) then
+						F_Out(Flag_H) <= '1';
+					else
+						F_Out(Flag_H) <= '0';
+					end if;
+					DAA_Q := DAA_Q + 6;
+				end if;
+				-- new Ahigh > 9 or C = 1
+				if DAA_Q(8 downto 4) > 9 or F_In(Flag_C) = '1' then
+					DAA_Q := DAA_Q + 96; -- 0x60
+				end if;
+			else
+				-- After subtraction
+				if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then
+					if DAA_Q(3 downto 0) > 5 then
+						F_Out(Flag_H) <= '0';
+					end if;
+					DAA_Q(7 downto 0) := DAA_Q(7 downto 0) - 6;
+				end if;
+				if unsigned(BusA) > 153 or F_In(Flag_C) = '1' then
+					DAA_Q := DAA_Q - 352; -- 0x160
+				end if;
+			end if;
+			F_Out(Flag_X) <= DAA_Q(3);
+			F_Out(Flag_Y) <= DAA_Q(5);
+			F_Out(Flag_C) <= F_In(Flag_C) or DAA_Q(8);
+			Q_t := std_logic_vector(DAA_Q(7 downto 0));
+			if DAA_Q(7 downto 0) = "00000000" then
+				F_Out(Flag_Z) <= '1';
+			else
+				F_Out(Flag_Z) <= '0';
+			end if;
+			F_Out(Flag_S) <= DAA_Q(7);
+			F_Out(Flag_P) <= not (DAA_Q(0) xor DAA_Q(1) xor DAA_Q(2) xor DAA_Q(3) xor
+				DAA_Q(4) xor DAA_Q(5) xor DAA_Q(6) xor DAA_Q(7));
+		when "1101" | "1110" =>
+			-- RLD, RRD
+			Q_t(7 downto 4) := BusA(7 downto 4);
+			if ALU_Op(0) = '1' then
+				Q_t(3 downto 0) := BusB(7 downto 4);
+			else
+				Q_t(3 downto 0) := BusB(3 downto 0);
+			end if;
+			F_Out(Flag_H) <= '0';
+			F_Out(Flag_N) <= '0';
+			F_Out(Flag_X) <= Q_t(3);
+			F_Out(Flag_Y) <= Q_t(5);
+			if Q_t(7 downto 0) = "00000000" then
+				F_Out(Flag_Z) <= '1';
+			else
+				F_Out(Flag_Z) <= '0';
+			end if;
+			F_Out(Flag_S) <= Q_t(7);
+			F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor
+				Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7));
+		when "1001" =>
+			-- BIT
+			Q_t(7 downto 0) := BusB and BitMask;
+			F_Out(Flag_S) <= Q_t(7);
+			if Q_t(7 downto 0) = "00000000" then
+				F_Out(Flag_Z) <= '1';
+				F_Out(Flag_P) <= '1';
+			else
+				F_Out(Flag_Z) <= '0';
+				F_Out(Flag_P) <= '0';
+			end if;
+			F_Out(Flag_H) <= '1';
+			F_Out(Flag_N) <= '0';
+			F_Out(Flag_X) <= '0';
+			F_Out(Flag_Y) <= '0';
+			if IR(2 downto 0) /= "110" then
+				F_Out(Flag_X) <= BusB(3);
+				F_Out(Flag_Y) <= BusB(5);
+			end if;
+		when "1010" =>
+			-- SET
+			Q_t(7 downto 0) := BusB or BitMask;
+		when "1011" =>
+			-- RES
+			Q_t(7 downto 0) := BusB and not BitMask;
+		when "1000" =>
+			-- ROT
+			case IR(5 downto 3) is
+			when "000" => -- RLC
+				Q_t(7 downto 1) := BusA(6 downto 0);
+				Q_t(0) := BusA(7);
+				F_Out(Flag_C) <= BusA(7);
+			when "010" => -- RL
+				Q_t(7 downto 1) := BusA(6 downto 0);
+				Q_t(0) := F_In(Flag_C);
+				F_Out(Flag_C) <= BusA(7);
+			when "001" => -- RRC
+				Q_t(6 downto 0) := BusA(7 downto 1);
+				Q_t(7) := BusA(0);
+				F_Out(Flag_C) <= BusA(0);
+			when "011" => -- RR
+				Q_t(6 downto 0) := BusA(7 downto 1);
+				Q_t(7) := F_In(Flag_C);
+				F_Out(Flag_C) <= BusA(0);
+			when "100" => -- SLA
+				Q_t(7 downto 1) := BusA(6 downto 0);
+				Q_t(0) := '0';
+				F_Out(Flag_C) <= BusA(7);
+			when "110" => -- SLL (Undocumented) / SWAP
+				if Mode = 3 then
+					Q_t(7 downto 4) := BusA(3 downto 0);
+					Q_t(3 downto 0) := BusA(7 downto 4);
+					F_Out(Flag_C) <= '0';
+				else
+					Q_t(7 downto 1) := BusA(6 downto 0);
+					Q_t(0) := '1';
+					F_Out(Flag_C) <= BusA(7);
+				end if;
+			when "101" => -- SRA
+				Q_t(6 downto 0) := BusA(7 downto 1);
+				Q_t(7) := BusA(7);
+				F_Out(Flag_C) <= BusA(0);
+			when others => -- SRL
+				Q_t(6 downto 0) := BusA(7 downto 1);
+				Q_t(7) := '0';
+				F_Out(Flag_C) <= BusA(0);
+			end case;
+			F_Out(Flag_H) <= '0';
+			F_Out(Flag_N) <= '0';
+			F_Out(Flag_X) <= Q_t(3);
+			F_Out(Flag_Y) <= Q_t(5);
+			F_Out(Flag_S) <= Q_t(7);
+			if Q_t(7 downto 0) = "00000000" then
+				F_Out(Flag_Z) <= '1';
+			else
+				F_Out(Flag_Z) <= '0';
+			end if;
+			F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor
+				Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7));
+			if ISet = "00" then
+				F_Out(Flag_P) <= F_In(Flag_P);
+				F_Out(Flag_S) <= F_In(Flag_S);
+				F_Out(Flag_Z) <= F_In(Flag_Z);
+			end if;
+		when others =>
+			null;
+		end case;
+		Q <= Q_t;
+	end process;
+end;

+ 2028 - 0
vhdl/T80_MCode.vhd

@@ -0,0 +1,2028 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010
+-- Ver 302 fixed IO cycle timing, tested thanks to Alessandro.
+-- Ver 300 started tidyup
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- Z80 compatible microprocessor core
+--
+-- Version : 0242
+--
+-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t80/
+--
+-- Limitations :
+--
+-- File history :
+--
+--      0208 : First complete release
+--
+--      0211 : Fixed IM 1
+--
+--      0214 : Fixed mostly flags, only the block instructions now fail the zex regression test
+--
+--      0235 : Added IM 2 fix by Mike Johnson
+--
+--      0238 : Added NoRead signal
+--
+--      0238b: Fixed instruction timing for POP and DJNZ
+--
+--      0240 : Added (IX/IY+d) states, removed op-codes from mode 2 and added all remaining mode 3 op-codes
+
+--      0240mj1 fix for HL inc/dec for INI, IND, INIR, INDR, OUTI, OUTD, OTIR, OTDR
+--
+--      0242 : Fixed I/O instruction timing, cleanup
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+use work.T80_Pack.all;
+
+entity T80_MCode is
+	generic(
+		Mode : integer := 0;
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		IR                              : in std_logic_vector(7 downto 0);
+		ISet                    : in std_logic_vector(1 downto 0);
+		MCycle                  : in std_logic_vector(2 downto 0);
+		F                               : in std_logic_vector(7 downto 0);
+		NMICycle                : in std_logic;
+		IntCycle                : in std_logic;
+		XY_State                : in std_logic_vector(1 downto 0);
+		MCycles                 : out std_logic_vector(2 downto 0);
+		TStates                 : out std_logic_vector(2 downto 0);
+		Prefix                  : out std_logic_vector(1 downto 0); -- None,CB,ED,DD/FD
+		Inc_PC                  : out std_logic;
+		Inc_WZ                  : out std_logic;
+		IncDec_16               : out std_logic_vector(3 downto 0); -- BC,DE,HL,SP   0 is inc
+		Read_To_Reg             : out std_logic;
+		Read_To_Acc             : out std_logic;
+		Set_BusA_To     : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI/DB,A,SP(L),SP(M),0,F
+		Set_BusB_To     : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI,A,SP(L),SP(M),1,F,PC(L),PC(M),0
+		ALU_Op                  : out std_logic_vector(3 downto 0);
+			-- ADD, ADC, SUB, SBC, AND, XOR, OR, CP, ROT, BIT, SET, RES, DAA, RLD, RRD, None
+		Save_ALU                : out std_logic;
+		PreserveC               : out std_logic;
+		Arith16                 : out std_logic;
+		Set_Addr_To             : out std_logic_vector(2 downto 0); -- aNone,aXY,aIOA,aSP,aBC,aDE,aZI
+		IORQ                    : out std_logic;
+		Jump                    : out std_logic;
+		JumpE                   : out std_logic;
+		JumpXY                  : out std_logic;
+		Call                    : out std_logic;
+		RstP                    : out std_logic;
+		LDZ                             : out std_logic;
+		LDW                             : out std_logic;
+		LDSPHL                  : out std_logic;
+		Special_LD              : out std_logic_vector(2 downto 0); -- A,I;A,R;I,A;R,A;None
+		ExchangeDH              : out std_logic;
+		ExchangeRp              : out std_logic;
+		ExchangeAF              : out std_logic;
+		ExchangeRS              : out std_logic;
+		I_DJNZ                  : out std_logic;
+		I_CPL                   : out std_logic;
+		I_CCF                   : out std_logic;
+		I_SCF                   : out std_logic;
+		I_RETN                  : out std_logic;
+		I_BT                    : out std_logic;
+		I_BC                    : out std_logic;
+		I_BTR                   : out std_logic;
+		I_RLD                   : out std_logic;
+		I_RRD                   : out std_logic;
+		I_INRC                  : out std_logic;
+		SetDI                   : out std_logic;
+		SetEI                   : out std_logic;
+		IMode                   : out std_logic_vector(1 downto 0);
+		Halt                    : out std_logic;
+		NoRead                  : out std_logic;
+		Write                   : out std_logic;
+		XYbit_undoc             : out std_logic
+	);
+end T80_MCode;
+
+architecture rtl of T80_MCode is
+
+	constant aNone      : std_logic_vector(2 downto 0) := "111";
+	constant aBC        : std_logic_vector(2 downto 0) := "000";
+	constant aDE        : std_logic_vector(2 downto 0) := "001";
+	constant aXY        : std_logic_vector(2 downto 0) := "010";
+	constant aIOA       : std_logic_vector(2 downto 0) := "100";
+	constant aSP        : std_logic_vector(2 downto 0) := "101";
+	constant aZI        : std_logic_vector(2 downto 0) := "110";
+
+	function is_cc_true(
+		F : std_logic_vector(7 downto 0);
+		cc : bit_vector(2 downto 0)
+		) return boolean is
+	begin
+		if Mode = 3 then
+			case cc is
+			when "000" => return F(7) = '0'; -- NZ
+			when "001" => return F(7) = '1'; -- Z
+			when "010" => return F(4) = '0'; -- NC
+			when "011" => return F(4) = '1'; -- C
+			when "100" => return false;
+			when "101" => return false;
+			when "110" => return false;
+			when "111" => return false;
+			end case;
+		else
+			case cc is
+			when "000" => return F(6) = '0'; -- NZ
+			when "001" => return F(6) = '1'; -- Z
+			when "010" => return F(0) = '0'; -- NC
+			when "011" => return F(0) = '1'; -- C
+			when "100" => return F(2) = '0'; -- PO
+			when "101" => return F(2) = '1'; -- PE
+			when "110" => return F(7) = '0'; -- P
+			when "111" => return F(7) = '1'; -- M
+			end case;
+		end if;
+	end;
+
+begin
+
+	process (IR, ISet, MCycle, F, NMICycle, IntCycle)
+		variable DDD : std_logic_vector(2 downto 0);
+		variable SSS : std_logic_vector(2 downto 0);
+		variable DPair : std_logic_vector(1 downto 0);
+		variable IRB : bit_vector(7 downto 0);
+	begin
+		DDD := IR(5 downto 3);
+		SSS := IR(2 downto 0);
+		DPair := IR(5 downto 4);
+		IRB := to_bitvector(IR);
+
+		MCycles <= "001";
+		if MCycle = "001" then
+			TStates <= "100";
+		else
+			TStates <= "011";
+		end if;
+		Prefix <= "00";
+		Inc_PC <= '0';
+		Inc_WZ <= '0';
+		IncDec_16 <= "0000";
+		Read_To_Acc <= '0';
+		Read_To_Reg <= '0';
+		Set_BusB_To <= "0000";
+		Set_BusA_To <= "0000";
+		ALU_Op <= "0" & IR(5 downto 3);
+		Save_ALU <= '0';
+		PreserveC <= '0';
+		Arith16 <= '0';
+		IORQ <= '0';
+		Set_Addr_To <= aNone;
+		Jump <= '0';
+		JumpE <= '0';
+		JumpXY <= '0';
+		Call <= '0';
+		RstP <= '0';
+		LDZ <= '0';
+		LDW <= '0';
+		LDSPHL <= '0';
+		Special_LD <= "000";
+		ExchangeDH <= '0';
+		ExchangeRp <= '0';
+		ExchangeAF <= '0';
+		ExchangeRS <= '0';
+		I_DJNZ <= '0';
+		I_CPL <= '0';
+		I_CCF <= '0';
+		I_SCF <= '0';
+		I_RETN <= '0';
+		I_BT <= '0';
+		I_BC <= '0';
+		I_BTR <= '0';
+		I_RLD <= '0';
+		I_RRD <= '0';
+		I_INRC <= '0';
+		SetDI <= '0';
+		SetEI <= '0';
+		IMode <= "11";
+		Halt <= '0';
+		NoRead <= '0';
+		Write <= '0';
+		XYbit_undoc <= '0';
+
+		case ISet is
+		when "00" =>
+
+------------------------------------------------------------------------------
+--
+--      Unprefixed instructions
+--
+------------------------------------------------------------------------------
+
+		case IRB is
+-- 8 BIT LOAD GROUP
+		when "01000000"|"01000001"|"01000010"|"01000011"|"01000100"|"01000101"|"01000111"
+			|"01001000"|"01001001"|"01001010"|"01001011"|"01001100"|"01001101"|"01001111"
+			|"01010000"|"01010001"|"01010010"|"01010011"|"01010100"|"01010101"|"01010111"
+			|"01011000"|"01011001"|"01011010"|"01011011"|"01011100"|"01011101"|"01011111"
+			|"01100000"|"01100001"|"01100010"|"01100011"|"01100100"|"01100101"|"01100111"
+			|"01101000"|"01101001"|"01101010"|"01101011"|"01101100"|"01101101"|"01101111"
+			|"01111000"|"01111001"|"01111010"|"01111011"|"01111100"|"01111101"|"01111111" =>
+			-- LD r,r'
+			Set_BusB_To(2 downto 0) <= SSS;
+			ExchangeRp <= '1';
+			Set_BusA_To(2 downto 0) <= DDD;
+			Read_To_Reg <= '1';
+		when "00000110"|"00001110"|"00010110"|"00011110"|"00100110"|"00101110"|"00111110" =>
+			-- LD r,n
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				Inc_PC <= '1';
+				Set_BusA_To(2 downto 0) <= DDD;
+				Read_To_Reg <= '1';
+			when others => null;
+			end case;
+		when "01000110"|"01001110"|"01010110"|"01011110"|"01100110"|"01101110"|"01111110" =>
+			-- LD r,(HL)
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aXY;
+			when 2 =>
+				Set_BusA_To(2 downto 0) <= DDD;
+				Read_To_Reg <= '1';
+			when others => null;
+			end case;
+		when "01110000"|"01110001"|"01110010"|"01110011"|"01110100"|"01110101"|"01110111" =>
+			-- LD (HL),r
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aXY;
+				Set_BusB_To(2 downto 0) <= SSS;
+				Set_BusB_To(3) <= '0';
+			when 2 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "00110110" =>
+			-- LD (HL),n
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				Inc_PC <= '1';
+				Set_Addr_To <= aXY;
+				Set_BusB_To(2 downto 0) <= SSS;
+				Set_BusB_To(3) <= '0';
+			when 3 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "00001010" =>
+			-- LD A,(BC)
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aBC;
+			when 2 =>
+				Read_To_Acc <= '1';
+			when others => null;
+			end case;
+		when "00011010" =>
+			-- LD A,(DE)
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aDE;
+			when 2 =>
+				Read_To_Acc <= '1';
+			when others => null;
+			end case;
+		when "00111010" =>
+			if Mode = 3 then
+				-- LDD A,(HL)
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					Read_To_Acc <= '1';
+					IncDec_16 <= "1110";
+				when others => null;
+				end case;
+			else
+				-- LD A,(nn)
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+				when 4 =>
+					Read_To_Acc <= '1';
+				when others => null;
+				end case;
+			end if;
+		when "00000010" =>
+			-- LD (BC),A
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aBC;
+				Set_BusB_To <= "0111";
+			when 2 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "00010010" =>
+			-- LD (DE),A
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aDE;
+				Set_BusB_To <= "0111";
+			when 2 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "00110010" =>
+			if Mode = 3 then
+				-- LDD (HL),A
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+					Set_BusB_To <= "0111";
+				when 2 =>
+					Write <= '1';
+					IncDec_16 <= "1110";
+				when others => null;
+				end case;
+			else
+				-- LD (nn),A
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					Set_BusB_To <= "0111";
+				when 4 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			end if;
+
+-- 16 BIT LOAD GROUP
+		when "00000001"|"00010001"|"00100001"|"00110001" =>
+			-- LD dd,nn
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				Inc_PC <= '1';
+				Read_To_Reg <= '1';
+				if DPAIR = "11" then
+					Set_BusA_To(3 downto 0) <= "1000";
+				else
+					Set_BusA_To(2 downto 1) <= DPAIR;
+					Set_BusA_To(0) <= '1';
+				end if;
+			when 3 =>
+				Inc_PC <= '1';
+				Read_To_Reg <= '1';
+				if DPAIR = "11" then
+					Set_BusA_To(3 downto 0) <= "1001";
+				else
+					Set_BusA_To(2 downto 1) <= DPAIR;
+					Set_BusA_To(0) <= '0';
+				end if;
+			when others => null;
+			end case;
+		when "00101010" =>
+			if Mode = 3 then
+				-- LDI A,(HL)
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					Read_To_Acc <= '1';
+					IncDec_16 <= "0110";
+				when others => null;
+				end case;
+			else
+				-- LD HL,(nn)
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					LDW <= '1';
+				when 4 =>
+					Set_BusA_To(2 downto 0) <= "101"; -- L
+					Read_To_Reg <= '1';
+					Inc_WZ <= '1';
+					Set_Addr_To <= aZI;
+				when 5 =>
+					Set_BusA_To(2 downto 0) <= "100"; -- H
+					Read_To_Reg <= '1';
+				when others => null;
+				end case;
+			end if;
+		when "00100010" =>
+			if Mode = 3 then
+				-- LDI (HL),A
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+					Set_BusB_To <= "0111";
+				when 2 =>
+					Write <= '1';
+					IncDec_16 <= "0110";
+				when others => null;
+				end case;
+			else
+				-- LD (nn),HL
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					LDW <= '1';
+					Set_BusB_To <= "0101"; -- L
+				when 4 =>
+					Inc_WZ <= '1';
+					Set_Addr_To <= aZI;
+					Write <= '1';
+					Set_BusB_To <= "0100"; -- H
+				when 5 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			end if;
+		when "11111001" =>
+			-- LD SP,HL
+			TStates <= "110";
+			LDSPHL <= '1';
+		when "11000101"|"11010101"|"11100101"|"11110101" =>
+			-- PUSH qq
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				TStates <= "101";
+				IncDec_16 <= "1111";
+				Set_Addr_TO <= aSP;
+				if DPAIR = "11" then
+					Set_BusB_To <= "0111";
+				else
+					Set_BusB_To(2 downto 1) <= DPAIR;
+					Set_BusB_To(0) <= '0';
+					Set_BusB_To(3) <= '0';
+				end if;
+			when 2 =>
+				IncDec_16 <= "1111";
+				Set_Addr_To <= aSP;
+				if DPAIR = "11" then
+					Set_BusB_To <= "1011";
+				else
+					Set_BusB_To(2 downto 1) <= DPAIR;
+					Set_BusB_To(0) <= '1';
+					Set_BusB_To(3) <= '0';
+				end if;
+				Write <= '1';
+			when 3 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "11000001"|"11010001"|"11100001"|"11110001" =>
+			-- POP qq
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aSP;
+			when 2 =>
+				IncDec_16 <= "0111";
+				Set_Addr_To <= aSP;
+				Read_To_Reg <= '1';
+				if DPAIR = "11" then
+					Set_BusA_To(3 downto 0) <= "1011";
+				else
+					Set_BusA_To(2 downto 1) <= DPAIR;
+					Set_BusA_To(0) <= '1';
+				end if;
+			when 3 =>
+				IncDec_16 <= "0111";
+				Read_To_Reg <= '1';
+				if DPAIR = "11" then
+					Set_BusA_To(3 downto 0) <= "0111";
+				else
+					Set_BusA_To(2 downto 1) <= DPAIR;
+					Set_BusA_To(0) <= '0';
+				end if;
+			when others => null;
+			end case;
+
+-- EXCHANGE, BLOCK TRANSFER AND SEARCH GROUP
+		when "11101011" =>
+			if Mode /= 3 then
+				-- EX DE,HL
+				ExchangeDH <= '1';
+			end if;
+		when "00001000" =>
+			if Mode = 3 then
+				-- LD (nn),SP
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					LDW <= '1';
+					Set_BusB_To <= "1000";
+				when 4 =>
+					Inc_WZ <= '1';
+					Set_Addr_To <= aZI;
+					Write <= '1';
+					Set_BusB_To <= "1001";
+				when 5 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			elsif Mode < 2 then
+				-- EX AF,AF'
+				ExchangeAF <= '1';
+			end if;
+		when "11011001" =>
+			if Mode = 3 then
+				-- RETI
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_TO <= aSP;
+				when 2 =>
+					IncDec_16 <= "0111";
+					Set_Addr_To <= aSP;
+					LDZ <= '1';
+				when 3 =>
+					Jump <= '1';
+					IncDec_16 <= "0111";
+					I_RETN <= '1';
+					SetEI <= '1';
+				when others => null;
+				end case;
+			elsif Mode < 2 then
+				-- EXX
+				ExchangeRS <= '1';
+			end if;
+		when "11100011" =>
+			if Mode /= 3 then
+				-- EX (SP),HL
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aSP;
+				when 2 =>
+					Read_To_Reg <= '1';
+					Set_BusA_To <= "0101";
+					Set_BusB_To <= "0101";
+					Set_Addr_To <= aSP;
+				when 3 =>
+					IncDec_16 <= "0111";
+					Set_Addr_To <= aSP;
+					TStates <= "100";
+					Write <= '1';
+				when 4 =>
+					Read_To_Reg <= '1';
+					Set_BusA_To <= "0100";
+					Set_BusB_To <= "0100";
+					Set_Addr_To <= aSP;
+				when 5 =>
+					IncDec_16 <= "1111";
+					TStates <= "101";
+					Write <= '1';
+				when others => null;
+				end case;
+			end if;
+
+-- 8 BIT ARITHMETIC AND LOGICAL GROUP
+		when "10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000111"
+			|"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001111"
+			|"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010111"
+			|"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011111"
+			|"10100000"|"10100001"|"10100010"|"10100011"|"10100100"|"10100101"|"10100111"
+			|"10101000"|"10101001"|"10101010"|"10101011"|"10101100"|"10101101"|"10101111"
+			|"10110000"|"10110001"|"10110010"|"10110011"|"10110100"|"10110101"|"10110111"
+			|"10111000"|"10111001"|"10111010"|"10111011"|"10111100"|"10111101"|"10111111" =>
+			-- ADD A,r
+			-- ADC A,r
+			-- SUB A,r
+			-- SBC A,r
+			-- AND A,r
+			-- OR A,r
+			-- XOR A,r
+			-- CP A,r
+			Set_BusB_To(2 downto 0) <= SSS;
+			Set_BusA_To(2 downto 0) <= "111";
+			Read_To_Reg <= '1';
+			Save_ALU <= '1';
+		when "10000110"|"10001110"|"10010110"|"10011110"|"10100110"|"10101110"|"10110110"|"10111110" =>
+			-- ADD A,(HL)
+			-- ADC A,(HL)
+			-- SUB A,(HL)
+			-- SBC A,(HL)
+			-- AND A,(HL)
+			-- OR A,(HL)
+			-- XOR A,(HL)
+			-- CP A,(HL)
+			MCycles <= "010";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aXY;
+			when 2 =>
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				Set_BusB_To(2 downto 0) <= SSS;
+				Set_BusA_To(2 downto 0) <= "111";
+			when others => null;
+			end case;
+		when "11000110"|"11001110"|"11010110"|"11011110"|"11100110"|"11101110"|"11110110"|"11111110" =>
+			-- ADD A,n
+			-- ADC A,n
+			-- SUB A,n
+			-- SBC A,n
+			-- AND A,n
+			-- OR A,n
+			-- XOR A,n
+			-- CP A,n
+			MCycles <= "010";
+			if MCycle = "010" then
+				Inc_PC <= '1';
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				Set_BusB_To(2 downto 0) <= SSS;
+				Set_BusA_To(2 downto 0) <= "111";
+			end if;
+		when "00000100"|"00001100"|"00010100"|"00011100"|"00100100"|"00101100"|"00111100" =>
+			-- INC r
+			Set_BusB_To <= "1010";
+			Set_BusA_To(2 downto 0) <= DDD;
+			Read_To_Reg <= '1';
+			Save_ALU <= '1';
+			PreserveC <= '1';
+			ALU_Op <= "0000";
+		when "00110100" =>
+			-- INC (HL)
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aXY;
+			when 2 =>
+				TStates <= "100";
+				Set_Addr_To <= aXY;
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				PreserveC <= '1';
+				ALU_Op <= "0000";
+				Set_BusB_To <= "1010";
+				Set_BusA_To(2 downto 0) <= DDD;
+			when 3 =>
+				Write <= '1';
+			when others => null;
+			end case;
+		when "00000101"|"00001101"|"00010101"|"00011101"|"00100101"|"00101101"|"00111101" =>
+			-- DEC r
+			Set_BusB_To <= "1010";
+			Set_BusA_To(2 downto 0) <= DDD;
+			Read_To_Reg <= '1';
+			Save_ALU <= '1';
+			PreserveC <= '1';
+			ALU_Op <= "0010";
+		when "00110101" =>
+			-- DEC (HL)
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_To <= aXY;
+			when 2 =>
+				TStates <= "100";
+				Set_Addr_To <= aXY;
+				ALU_Op <= "0010";
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				PreserveC <= '1';
+				Set_BusB_To <= "1010";
+				Set_BusA_To(2 downto 0) <= DDD;
+			when 3 =>
+				Write <= '1';
+			when others => null;
+			end case;
+
+-- GENERAL PURPOSE ARITHMETIC AND CPU CONTROL GROUPS
+		when "00100111" =>
+			-- DAA
+			Set_BusA_To(2 downto 0) <= "111";
+			Read_To_Reg <= '1';
+			ALU_Op <= "1100";
+			Save_ALU <= '1';
+		when "00101111" =>
+			-- CPL
+			I_CPL <= '1';
+		when "00111111" =>
+			-- CCF
+			I_CCF <= '1';
+		when "00110111" =>
+			-- SCF
+			I_SCF <= '1';
+		when "00000000" =>
+			if NMICycle = '1' then
+				-- NMI
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					TStates <= "101";
+					IncDec_16 <= "1111";
+					Set_Addr_To <= aSP;
+					Set_BusB_To <= "1101";
+				when 2 =>
+					TStates <= "100";
+					Write <= '1';
+					IncDec_16 <= "1111";
+					Set_Addr_To <= aSP;
+					Set_BusB_To <= "1100";
+				when 3 =>
+					TStates <= "100";
+					Write <= '1';
+				when others => null;
+				end case;
+			elsif IntCycle = '1' then
+				-- INT (IM 2)
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					LDZ <= '1';
+					TStates <= "101";
+					IncDec_16 <= "1111";
+					Set_Addr_To <= aSP;
+					Set_BusB_To <= "1101";
+				when 2 =>
+					TStates <= "100";
+					Write <= '1';
+					IncDec_16 <= "1111";
+					Set_Addr_To <= aSP;
+					Set_BusB_To <= "1100";
+				when 3 =>
+					TStates <= "100";
+					Write <= '1';
+				when 4 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 5 =>
+					Jump <= '1';
+				when others => null;
+				end case;
+			else
+				-- NOP
+			end if;
+		when "01110110" =>
+			-- HALT
+			Halt <= '1';
+		when "11110011" =>
+			-- DI
+			SetDI <= '1';
+		when "11111011" =>
+			-- EI
+			SetEI <= '1';
+
+-- 16 BIT ARITHMETIC GROUP
+		when "00001001"|"00011001"|"00101001"|"00111001" =>
+			-- ADD HL,ss
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				NoRead <= '1';
+				ALU_Op <= "0000";
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				Set_BusA_To(2 downto 0) <= "101";
+				case to_integer(unsigned(IR(5 downto 4))) is
+				when 0|1|2 =>
+					Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+					Set_BusB_To(0) <= '1';
+				when others =>
+					Set_BusB_To <= "1000";
+				end case;
+				TStates <= "100";
+				Arith16 <= '1';
+			when 3 =>
+				NoRead <= '1';
+				Read_To_Reg <= '1';
+				Save_ALU <= '1';
+				ALU_Op <= "0001";
+				Set_BusA_To(2 downto 0) <= "100";
+				case to_integer(unsigned(IR(5 downto 4))) is
+				when 0|1|2 =>
+					Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+				when others =>
+					Set_BusB_To <= "1001";
+				end case;
+				Arith16 <= '1';
+			when others =>
+			end case;
+		when "00000011"|"00010011"|"00100011"|"00110011" =>
+			-- INC ss
+			TStates <= "110";
+			IncDec_16(3 downto 2) <= "01";
+			IncDec_16(1 downto 0) <= DPair;
+		when "00001011"|"00011011"|"00101011"|"00111011" =>
+			-- DEC ss
+			TStates <= "110";
+			IncDec_16(3 downto 2) <= "11";
+			IncDec_16(1 downto 0) <= DPair;
+
+-- ROTATE AND SHIFT GROUP
+		when "00000111"
+			-- RLCA
+			|"00010111"
+			-- RLA
+			|"00001111"
+			-- RRCA
+			|"00011111" =>
+			-- RRA
+			Set_BusA_To(2 downto 0) <= "111";
+			ALU_Op <= "1000";
+			Read_To_Reg <= '1';
+			Save_ALU <= '1';
+
+-- JUMP GROUP
+		when "11000011" =>
+			-- JP nn
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				Inc_PC <= '1';
+				LDZ <= '1';
+			when 3 =>
+				Inc_PC <= '1';
+				Jump <= '1';
+			when others => null;
+			end case;
+		when "11000010"|"11001010"|"11010010"|"11011010"|"11100010"|"11101010"|"11110010"|"11111010" =>
+			if IR(5) = '1' and Mode = 3 then
+				case IRB(4 downto 3) is
+				when "00" =>
+					-- LD ($FF00+C),A
+					MCycles <= "010";
+					case to_integer(unsigned(MCycle)) is
+					when 1 =>
+						Set_Addr_To <= aBC;
+						Set_BusB_To     <= "0111";
+					when 2 =>
+						Write <= '1';
+						IORQ <= '1';
+					when others =>
+					end case;
+				when "01" =>
+					-- LD (nn),A
+					MCycles <= "100";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						Inc_PC <= '1';
+						LDZ <= '1';
+					when 3 =>
+						Set_Addr_To <= aZI;
+						Inc_PC <= '1';
+						Set_BusB_To <= "0111";
+					when 4 =>
+						Write <= '1';
+					when others => null;
+					end case;
+				when "10" =>
+					-- LD A,($FF00+C)
+					MCycles <= "010";
+					case to_integer(unsigned(MCycle)) is
+					when 1 =>
+						Set_Addr_To <= aBC;
+					when 2 =>
+						Read_To_Acc <= '1';
+						IORQ <= '1';
+					when others =>
+					end case;
+				when "11" =>
+					-- LD A,(nn)
+					MCycles <= "100";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						Inc_PC <= '1';
+						LDZ <= '1';
+					when 3 =>
+						Set_Addr_To <= aZI;
+						Inc_PC <= '1';
+					when 4 =>
+						Read_To_Acc <= '1';
+					when others => null;
+					end case;
+				end case;
+			else
+				-- JP cc,nn
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Inc_PC <= '1';
+					if is_cc_true(F, to_bitvector(IR(5 downto 3))) then
+						Jump <= '1';
+					end if;
+				when others => null;
+				end case;
+			end if;
+		when "00011000" =>
+			if Mode /= 2 then
+				-- JR e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+		when "00111000" =>
+			if Mode /= 2 then
+				-- JR C,e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					if F(Flag_C) = '0' then
+						MCycles <= "010";
+					end if;
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+		when "00110000" =>
+			if Mode /= 2 then
+				-- JR NC,e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					if F(Flag_C) = '1' then
+						MCycles <= "010";
+					end if;
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+		when "00101000" =>
+			if Mode /= 2 then
+				-- JR Z,e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					if F(Flag_Z) = '0' then
+						MCycles <= "010";
+					end if;
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+		when "00100000" =>
+			if Mode /= 2 then
+				-- JR NZ,e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					if F(Flag_Z) = '1' then
+						MCycles <= "010";
+					end if;
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+		when "11101001" =>
+			-- JP (HL)
+			JumpXY <= '1';
+		when "00010000" =>
+			if Mode = 3 then
+				I_DJNZ <= '1';
+			elsif Mode < 2 then
+				-- DJNZ,e
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					TStates <= "101";
+					I_DJNZ <= '1';
+					Set_BusB_To <= "1010";
+					Set_BusA_To(2 downto 0) <= "000";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					ALU_Op <= "0010";
+				when 2 =>
+					I_DJNZ <= '1';
+					Inc_PC <= '1';
+				when 3 =>
+					NoRead <= '1';
+					JumpE <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end if;
+
+-- CALL AND RETURN GROUP
+		when "11001101" =>
+			-- CALL nn
+			MCycles <= "101";
+			case to_integer(unsigned(MCycle)) is
+			when 2 =>
+				Inc_PC <= '1';
+				LDZ <= '1';
+			when 3 =>
+				IncDec_16 <= "1111";
+				Inc_PC <= '1';
+				TStates <= "100";
+				Set_Addr_To <= aSP;
+				LDW <= '1';
+				Set_BusB_To <= "1101";
+			when 4 =>
+				Write <= '1';
+				IncDec_16 <= "1111";
+				Set_Addr_To <= aSP;
+				Set_BusB_To <= "1100";
+			when 5 =>
+				Write <= '1';
+				Call <= '1';
+			when others => null;
+			end case;
+		when "11000100"|"11001100"|"11010100"|"11011100"|"11100100"|"11101100"|"11110100"|"11111100" =>
+			if IR(5) = '0' or Mode /= 3 then
+				-- CALL cc,nn
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Inc_PC <= '1';
+					LDW <= '1';
+					if is_cc_true(F, to_bitvector(IR(5 downto 3))) then
+						IncDec_16 <= "1111";
+						Set_Addr_TO <= aSP;
+						TStates <= "100";
+						Set_BusB_To <= "1101";
+					else
+						MCycles <= "011";
+					end if;
+				when 4 =>
+					Write <= '1';
+					IncDec_16 <= "1111";
+					Set_Addr_To <= aSP;
+					Set_BusB_To <= "1100";
+				when 5 =>
+					Write <= '1';
+					Call <= '1';
+				when others => null;
+				end case;
+			end if;
+		when "11001001" =>
+			-- RET
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				Set_Addr_TO <= aSP;
+			when 2 =>
+				IncDec_16 <= "0111";
+				Set_Addr_To <= aSP;
+				LDZ <= '1';
+			when 3 =>
+				Jump <= '1';
+				IncDec_16 <= "0111";
+			when others => null;
+			end case;
+		when "11000000"|"11001000"|"11010000"|"11011000"|"11100000"|"11101000"|"11110000"|"11111000" =>
+			if IR(5) = '1' and Mode = 3 then
+				case IRB(4 downto 3) is
+				when "00" =>
+					-- LD ($FF00+nn),A
+					MCycles <= "011";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						Inc_PC <= '1';
+						Set_Addr_To <= aIOA;
+						Set_BusB_To     <= "0111";
+					when 3 =>
+						Write <= '1';
+					when others => null;
+					end case;
+				when "01" =>
+					-- ADD SP,n
+					MCycles <= "011";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						ALU_Op <= "0000";
+						Inc_PC <= '1';
+						Read_To_Reg <= '1';
+						Save_ALU <= '1';
+						Set_BusA_To <= "1000";
+						Set_BusB_To <= "0110";
+					when 3 =>
+						NoRead <= '1';
+						Read_To_Reg <= '1';
+						Save_ALU <= '1';
+						ALU_Op <= "0001";
+						Set_BusA_To <= "1001";
+						Set_BusB_To <= "1110";  -- Incorrect unsigned !!!!!!!!!!!!!!!!!!!!!
+					when others =>
+					end case;
+				when "10" =>
+					-- LD A,($FF00+nn)
+					MCycles <= "011";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						Inc_PC <= '1';
+						Set_Addr_To <= aIOA;
+					when 3 =>
+						Read_To_Acc <= '1';
+					when others => null;
+					end case;
+				when "11" =>
+					-- LD HL,SP+n       -- Not correct !!!!!!!!!!!!!!!!!!!
+					MCycles <= "101";
+					case to_integer(unsigned(MCycle)) is
+					when 2 =>
+						Inc_PC <= '1';
+						LDZ <= '1';
+					when 3 =>
+						Set_Addr_To <= aZI;
+						Inc_PC <= '1';
+						LDW <= '1';
+					when 4 =>
+						Set_BusA_To(2 downto 0) <= "101"; -- L
+						Read_To_Reg <= '1';
+						Inc_WZ <= '1';
+						Set_Addr_To <= aZI;
+					when 5 =>
+						Set_BusA_To(2 downto 0) <= "100"; -- H
+						Read_To_Reg <= '1';
+					when others => null;
+					end case;
+				end case;
+			else
+				-- RET cc
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					if is_cc_true(F, to_bitvector(IR(5 downto 3))) then
+						Set_Addr_TO <= aSP;
+					else
+						MCycles <= "001";
+					end if;
+					TStates <= "101";
+				when 2 =>
+					IncDec_16 <= "0111";
+					Set_Addr_To <= aSP;
+					LDZ <= '1';
+				when 3 =>
+					Jump <= '1';
+					IncDec_16 <= "0111";
+				when others => null;
+				end case;
+			end if;
+		when "11000111"|"11001111"|"11010111"|"11011111"|"11100111"|"11101111"|"11110111"|"11111111" =>
+			-- RST p
+			MCycles <= "011";
+			case to_integer(unsigned(MCycle)) is
+			when 1 =>
+				TStates <= "101";
+				IncDec_16 <= "1111";
+				Set_Addr_To <= aSP;
+				Set_BusB_To <= "1101";
+			when 2 =>
+				Write <= '1';
+				IncDec_16 <= "1111";
+				Set_Addr_To <= aSP;
+				Set_BusB_To <= "1100";
+			when 3 =>
+				Write <= '1';
+				RstP <= '1';
+			when others => null;
+			end case;
+
+-- INPUT AND OUTPUT GROUP
+		when "11011011" =>
+			if Mode /= 3 then
+				-- IN A,(n)
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					Set_Addr_To <= aIOA;
+				when 3 =>
+					Read_To_Acc <= '1';
+					IORQ <= '1';
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+				when others => null;
+				end case;
+			end if;
+		when "11010011" =>
+			if Mode /= 3 then
+				-- OUT (n),A
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					Set_Addr_To <= aIOA;
+					Set_BusB_To <= "0111";
+				when 3 =>
+					Write <= '1';
+					IORQ <= '1';
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+				when others => null;
+				end case;
+			end if;
+
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+-- MULTIBYTE INSTRUCTIONS
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+
+		when "11001011" =>
+			if Mode /= 2 then
+				Prefix <= "01";
+			end if;
+
+		when "11101101" =>
+			if Mode < 2 then
+				Prefix <= "10";
+			end if;
+
+		when "11011101"|"11111101" =>
+			if Mode < 2 then
+				Prefix <= "11";
+			end if;
+
+		end case;
+
+		when "01" =>
+
+------------------------------------------------------------------------------
+--
+--      CB prefixed instructions
+--
+------------------------------------------------------------------------------
+
+			Set_BusA_To(2 downto 0) <= IR(2 downto 0);
+			Set_BusB_To(2 downto 0) <= IR(2 downto 0);
+
+			case IRB is
+			when "00000000"|"00000001"|"00000010"|"00000011"|"00000100"|"00000101"|"00000111"
+				|"00010000"|"00010001"|"00010010"|"00010011"|"00010100"|"00010101"|"00010111"
+				|"00001000"|"00001001"|"00001010"|"00001011"|"00001100"|"00001101"|"00001111"
+				|"00011000"|"00011001"|"00011010"|"00011011"|"00011100"|"00011101"|"00011111"
+				|"00100000"|"00100001"|"00100010"|"00100011"|"00100100"|"00100101"|"00100111"
+				|"00101000"|"00101001"|"00101010"|"00101011"|"00101100"|"00101101"|"00101111"
+				|"00110000"|"00110001"|"00110010"|"00110011"|"00110100"|"00110101"|"00110111"
+				|"00111000"|"00111001"|"00111010"|"00111011"|"00111100"|"00111101"|"00111111" =>
+				-- RLC r
+				-- RL r
+				-- RRC r
+				-- RR r
+				-- SLA r
+				-- SRA r
+				-- SRL r
+				-- SLL r (Undocumented) / SWAP r
+				if XY_State="00" then
+					if MCycle = "001" then
+					  ALU_Op <= "1000";
+					  Read_To_Reg <= '1';
+					  Save_ALU <= '1';
+					end if;
+				else
+				-- R/S (IX+d),Reg, undocumented
+					MCycles <= "011";
+					XYbit_undoc <= '1';
+					case to_integer(unsigned(MCycle)) is
+					when 1 | 7=>
+						Set_Addr_To <= aXY;
+					when 2 =>
+						ALU_Op <= "1000";
+						Read_To_Reg <= '1';
+						Save_ALU <= '1';
+						Set_Addr_To <= aXY;
+						TStates <= "100";
+					when 3 =>
+						Write <= '1';
+					when others => null;
+					end case;
+				end if;
+
+
+			when "00000110"|"00010110"|"00001110"|"00011110"|"00101110"|"00111110"|"00100110"|"00110110" =>
+				-- RLC (HL)
+				-- RL (HL)
+				-- RRC (HL)
+				-- RR (HL)
+				-- SRA (HL)
+				-- SRL (HL)
+				-- SLA (HL)
+				-- SLL (HL) (Undocumented) / SWAP (HL)
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 | 7 =>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					ALU_Op <= "1000";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_Addr_To <= aXY;
+					TStates <= "100";
+				when 3 =>
+					Write <= '1';
+				when others =>
+				end case;
+			when "01000000"|"01000001"|"01000010"|"01000011"|"01000100"|"01000101"|"01000111"
+				|"01001000"|"01001001"|"01001010"|"01001011"|"01001100"|"01001101"|"01001111"
+				|"01010000"|"01010001"|"01010010"|"01010011"|"01010100"|"01010101"|"01010111"
+				|"01011000"|"01011001"|"01011010"|"01011011"|"01011100"|"01011101"|"01011111"
+				|"01100000"|"01100001"|"01100010"|"01100011"|"01100100"|"01100101"|"01100111"
+				|"01101000"|"01101001"|"01101010"|"01101011"|"01101100"|"01101101"|"01101111"
+				|"01110000"|"01110001"|"01110010"|"01110011"|"01110100"|"01110101"|"01110111"
+				|"01111000"|"01111001"|"01111010"|"01111011"|"01111100"|"01111101"|"01111111" =>
+				-- BIT b,r
+				if XY_State="00" then
+					if MCycle = "001" then
+					  Set_BusB_To(2 downto 0) <= IR(2 downto 0);
+					  ALU_Op <= "1001";
+					end if;
+				else
+				-- BIT b,(IX+d), undocumented
+					MCycles <= "010";
+					XYbit_undoc <= '1';
+					case to_integer(unsigned(MCycle)) is
+					when 1 | 7=>
+						Set_Addr_To <= aXY;
+					when 2 =>
+						ALU_Op <= "1001";
+						TStates <= "100";
+					when others => null;
+					end case;
+				end if;
+			when "01000110"|"01001110"|"01010110"|"01011110"|"01100110"|"01101110"|"01110110"|"01111110" =>
+				-- BIT b,(HL)
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 | 7=>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					ALU_Op <= "1001";
+					TStates <= "100";
+				when others => null;
+				end case;
+			when "11000000"|"11000001"|"11000010"|"11000011"|"11000100"|"11000101"|"11000111"
+				|"11001000"|"11001001"|"11001010"|"11001011"|"11001100"|"11001101"|"11001111"
+				|"11010000"|"11010001"|"11010010"|"11010011"|"11010100"|"11010101"|"11010111"
+				|"11011000"|"11011001"|"11011010"|"11011011"|"11011100"|"11011101"|"11011111"
+				|"11100000"|"11100001"|"11100010"|"11100011"|"11100100"|"11100101"|"11100111"
+				|"11101000"|"11101001"|"11101010"|"11101011"|"11101100"|"11101101"|"11101111"
+				|"11110000"|"11110001"|"11110010"|"11110011"|"11110100"|"11110101"|"11110111"
+				|"11111000"|"11111001"|"11111010"|"11111011"|"11111100"|"11111101"|"11111111" =>
+				-- SET b,r
+				if XY_State="00" then
+					if MCycle = "001" then
+					  ALU_Op <= "1010";
+					  Read_To_Reg <= '1';
+					  Save_ALU <= '1';
+					end if;
+				else
+				-- SET b,(IX+d),Reg, undocumented
+					MCycles <= "011";
+					XYbit_undoc <= '1';
+					case to_integer(unsigned(MCycle)) is
+					when 1 | 7=>
+						Set_Addr_To <= aXY;
+					when 2 =>
+						ALU_Op <= "1010";
+						Read_To_Reg <= '1';
+						Save_ALU <= '1';
+						Set_Addr_To <= aXY;
+						TStates <= "100";
+					when 3 =>
+						Write <= '1';
+					when others => null;
+					end case;
+				end if;
+			when "11000110"|"11001110"|"11010110"|"11011110"|"11100110"|"11101110"|"11110110"|"11111110" =>
+				-- SET b,(HL)
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 | 7=>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					ALU_Op <= "1010";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_Addr_To <= aXY;
+					TStates <= "100";
+				when 3 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			when "10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000111"
+				|"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001111"
+				|"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010111"
+				|"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011111"
+				|"10100000"|"10100001"|"10100010"|"10100011"|"10100100"|"10100101"|"10100111"
+				|"10101000"|"10101001"|"10101010"|"10101011"|"10101100"|"10101101"|"10101111"
+				|"10110000"|"10110001"|"10110010"|"10110011"|"10110100"|"10110101"|"10110111"
+				|"10111000"|"10111001"|"10111010"|"10111011"|"10111100"|"10111101"|"10111111" =>
+				-- RES b,r
+				if XY_State="00" then
+					if MCycle = "001" then
+					  ALU_Op <= "1011";
+					  Read_To_Reg <= '1';
+					  Save_ALU <= '1';
+					end if;
+				else
+				-- RES b,(IX+d),Reg, undocumented
+					MCycles <= "011";
+					XYbit_undoc <= '1';
+					case to_integer(unsigned(MCycle)) is
+					when 1 | 7=>
+						Set_Addr_To <= aXY;
+					when 2 =>
+						ALU_Op <= "1011";
+						Read_To_Reg <= '1';
+						Save_ALU <= '1';
+						Set_Addr_To <= aXY;
+						TStates <= "100";
+					when 3 =>
+						Write <= '1';
+					when others => null;
+					end case;
+				end if;
+				
+			when "10000110"|"10001110"|"10010110"|"10011110"|"10100110"|"10101110"|"10110110"|"10111110" =>
+				-- RES b,(HL)
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 | 7 =>
+					Set_Addr_To <= aXY;
+				when 2 =>
+					ALU_Op <= "1011";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_Addr_To <= aXY;
+					TStates <= "100";
+				when 3 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			end case;
+
+		when others =>
+
+------------------------------------------------------------------------------
+--
+--      ED prefixed instructions
+--
+------------------------------------------------------------------------------
+
+			case IRB is
+			when "00000000"|"00000001"|"00000010"|"00000011"|"00000100"|"00000101"|"00000110"|"00000111"
+				|"00001000"|"00001001"|"00001010"|"00001011"|"00001100"|"00001101"|"00001110"|"00001111"
+				|"00010000"|"00010001"|"00010010"|"00010011"|"00010100"|"00010101"|"00010110"|"00010111"
+				|"00011000"|"00011001"|"00011010"|"00011011"|"00011100"|"00011101"|"00011110"|"00011111"
+				|"00100000"|"00100001"|"00100010"|"00100011"|"00100100"|"00100101"|"00100110"|"00100111"
+				|"00101000"|"00101001"|"00101010"|"00101011"|"00101100"|"00101101"|"00101110"|"00101111"
+				|"00110000"|"00110001"|"00110010"|"00110011"|"00110100"|"00110101"|"00110110"|"00110111"
+				|"00111000"|"00111001"|"00111010"|"00111011"|"00111100"|"00111101"|"00111110"|"00111111"
+
+
+				|"10000000"|"10000001"|"10000010"|"10000011"|"10000100"|"10000101"|"10000110"|"10000111"
+				|"10001000"|"10001001"|"10001010"|"10001011"|"10001100"|"10001101"|"10001110"|"10001111"
+				|"10010000"|"10010001"|"10010010"|"10010011"|"10010100"|"10010101"|"10010110"|"10010111"
+				|"10011000"|"10011001"|"10011010"|"10011011"|"10011100"|"10011101"|"10011110"|"10011111"
+				|                                            "10100100"|"10100101"|"10100110"|"10100111"
+				|                                            "10101100"|"10101101"|"10101110"|"10101111"
+				|                                            "10110100"|"10110101"|"10110110"|"10110111"
+				|                                            "10111100"|"10111101"|"10111110"|"10111111"
+				|"11000000"|"11000001"|"11000010"|"11000011"|"11000100"|"11000101"|"11000110"|"11000111"
+				|"11001000"|"11001001"|"11001010"|"11001011"|"11001100"|"11001101"|"11001110"|"11001111"
+				|"11010000"|"11010001"|"11010010"|"11010011"|"11010100"|"11010101"|"11010110"|"11010111"
+				|"11011000"|"11011001"|"11011010"|"11011011"|"11011100"|"11011101"|"11011110"|"11011111"
+				|"11100000"|"11100001"|"11100010"|"11100011"|"11100100"|"11100101"|"11100110"|"11100111"
+				|"11101000"|"11101001"|"11101010"|"11101011"|"11101100"|"11101101"|"11101110"|"11101111"
+				|"11110000"|"11110001"|"11110010"|"11110011"|"11110100"|"11110101"|"11110110"|"11110111"
+				|"11111000"|"11111001"|"11111010"|"11111011"|"11111100"|"11111101"|"11111110"|"11111111" =>
+				null; -- NOP, undocumented
+			when "01111110"|"01111111" =>
+				-- NOP, undocumented
+				null;
+-- 8 BIT LOAD GROUP
+			when "01010111" =>
+				-- LD A,I
+				Special_LD <= "100";
+				TStates <= "101";
+			when "01011111" =>
+				-- LD A,R
+				Special_LD <= "101";
+				TStates <= "101";
+			when "01000111" =>
+				-- LD I,A
+				Special_LD <= "110";
+				TStates <= "101";
+			when "01001111" =>
+				-- LD R,A
+				Special_LD <= "111";
+				TStates <= "101";
+-- 16 BIT LOAD GROUP
+			when "01001011"|"01011011"|"01101011"|"01111011" =>
+				-- LD dd,(nn)
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					LDW <= '1';
+				when 4 =>
+					Read_To_Reg <= '1';
+					if IR(5 downto 4) = "11" then
+						Set_BusA_To <= "1000";
+					else
+						Set_BusA_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusA_To(0) <= '1';
+					end if;
+					Inc_WZ <= '1';
+					Set_Addr_To <= aZI;
+				when 5 =>
+					Read_To_Reg <= '1';
+					if IR(5 downto 4) = "11" then
+						Set_BusA_To <= "1001";
+					else
+						Set_BusA_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusA_To(0) <= '0';
+					end if;
+				when others => null;
+				end case;
+			when "01000011"|"01010011"|"01100011"|"01110011" =>
+				-- LD (nn),dd
+				MCycles <= "101";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Inc_PC <= '1';
+					LDZ <= '1';
+				when 3 =>
+					Set_Addr_To <= aZI;
+					Inc_PC <= '1';
+					LDW <= '1';
+					if IR(5 downto 4) = "11" then
+						Set_BusB_To <= "1000";
+					else
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusB_To(0) <= '1';
+						Set_BusB_To(3) <= '0';
+					end if;
+				when 4 =>
+					Inc_WZ <= '1';
+					Set_Addr_To <= aZI;
+					Write <= '1';
+					if IR(5 downto 4) = "11" then
+						Set_BusB_To <= "1001";
+					else
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusB_To(0) <= '0';
+						Set_BusB_To(3) <= '0';
+					end if;
+				when 5 =>
+					Write <= '1';
+				when others => null;
+				end case;
+			when "10100000" | "10101000" | "10110000" | "10111000" =>
+				-- LDI, LDD, LDIR, LDDR
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+					IncDec_16 <= "1100"; -- BC
+				when 2 =>
+					Set_BusB_To <= "0110";
+					Set_BusA_To(2 downto 0) <= "111";
+					ALU_Op <= "0000";
+					Set_Addr_To <= aDE;
+					if IR(3) = '0' then
+						IncDec_16 <= "0110"; -- IX
+					else
+						IncDec_16 <= "1110";
+					end if;
+				when 3 =>
+					I_BT <= '1';
+					TStates <= "101";
+					Write <= '1';
+					if IR(3) = '0' then
+						IncDec_16 <= "0101"; -- DE
+					else
+						IncDec_16 <= "1101";
+					end if;
+				when 4 =>
+					NoRead <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			when "10100001" | "10101001" | "10110001" | "10111001" =>
+				-- CPI, CPD, CPIR, CPDR
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aXY;
+					IncDec_16 <= "1100"; -- BC
+				when 2 =>
+					Set_BusB_To <= "0110";
+					Set_BusA_To(2 downto 0) <= "111";
+					ALU_Op <= "0111";
+					Save_ALU <= '1';
+					PreserveC <= '1';
+					if IR(3) = '0' then
+						IncDec_16 <= "0110";
+					else
+						IncDec_16 <= "1110";
+					end if;
+				when 3 =>
+					NoRead <= '1';
+					I_BC <= '1';
+					TStates <= "101";
+				when 4 =>
+					NoRead <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			when "01000100"|"01001100"|"01010100"|"01011100"|"01100100"|"01101100"|"01110100"|"01111100" =>
+				-- NEG
+				Alu_OP <= "0010";
+				Set_BusB_To <= "0111";
+				Set_BusA_To <= "1010";
+				Read_To_Acc <= '1';
+				Save_ALU <= '1';
+			when "01000110"|"01001110"|"01100110"|"01101110" =>
+				-- IM 0
+				IMode <= "00";
+			when "01010110"|"01110110" =>
+				-- IM 1
+				IMode <= "01";
+			when "01011110"|"01110111" =>
+				-- IM 2
+				IMode <= "10";
+-- 16 bit arithmetic
+			when "01001010"|"01011010"|"01101010"|"01111010" =>
+				-- ADC HL,ss
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					NoRead <= '1';
+					ALU_Op <= "0001";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_BusA_To(2 downto 0) <= "101";
+					case to_integer(unsigned(IR(5 downto 4))) is
+					when 0|1|2 =>
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+					Set_BusB_To(0) <= '1';
+						when others =>
+						Set_BusB_To <= "1000";
+					end case;
+					TStates <= "100";
+				when 3 =>
+					NoRead <= '1';
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					ALU_Op <= "0001";
+					Set_BusA_To(2 downto 0) <= "100";
+					case to_integer(unsigned(IR(5 downto 4))) is
+					when 0|1|2 =>
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusB_To(0) <= '0';
+					when others =>
+						Set_BusB_To <= "1001";
+					end case;
+				when others =>
+				end case;
+			when "01000010"|"01010010"|"01100010"|"01110010" =>
+				-- SBC HL,ss
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					NoRead <= '1';
+					ALU_Op <= "0011";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_BusA_To(2 downto 0) <= "101";
+					case to_integer(unsigned(IR(5 downto 4))) is
+					when 0|1|2 =>
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+						Set_BusB_To(0) <= '1';
+					when others =>
+						Set_BusB_To <= "1000";
+					end case;
+					TStates <= "100";
+				when 3 =>
+					NoRead <= '1';
+					ALU_Op <= "0011";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					Set_BusA_To(2 downto 0) <= "100";
+					case to_integer(unsigned(IR(5 downto 4))) is
+					when 0|1|2 =>
+						Set_BusB_To(2 downto 1) <= IR(5 downto 4);
+					when others =>
+							Set_BusB_To <= "1001";
+					end case;
+				when others =>
+				end case;
+			when "01101111" =>
+				-- RLD
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					NoRead <= '1';
+					Set_Addr_To <= aXY;
+				when 3 =>
+					Read_To_Reg <= '1';
+					Set_BusB_To(2 downto 0) <= "110";
+					Set_BusA_To(2 downto 0) <= "111";
+					ALU_Op <= "1101";
+					TStates <= "100";
+					Set_Addr_To <= aXY;
+					Save_ALU <= '1';
+				when 4 =>
+					I_RLD <= '1';
+					Write <= '1';
+				when others =>
+				end case;
+			when "01100111" =>
+				-- RRD
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 2 =>
+					Set_Addr_To <= aXY;
+				when 3 =>
+					Read_To_Reg <= '1';
+					Set_BusB_To(2 downto 0) <= "110";
+					Set_BusA_To(2 downto 0) <= "111";
+					ALU_Op <= "1110";
+					TStates <= "100";
+					Set_Addr_To <= aXY;
+					Save_ALU <= '1';
+				when 4 =>
+					I_RRD <= '1';
+					Write <= '1';
+				when others =>
+				end case;
+			when "01000101"|"01001101"|"01010101"|"01011101"|"01100101"|"01101101"|"01110101"|"01111101" =>
+				-- RETI, RETN
+				MCycles <= "011";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_TO <= aSP;
+				when 2 =>
+					IncDec_16 <= "0111";
+					Set_Addr_To <= aSP;
+					LDZ <= '1';
+				when 3 =>
+					Jump <= '1';
+					IncDec_16 <= "0111";
+					I_RETN <= '1';
+				when others => null;
+				end case;
+			when "01000000"|"01001000"|"01010000"|"01011000"|"01100000"|"01101000"|"01110000"|"01111000" =>
+				-- IN r,(C)
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aBC;
+				when 2 =>
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+					IORQ <= '1';
+					if IR(5 downto 3) /= "110" then
+						Read_To_Reg <= '1';
+						Set_BusA_To(2 downto 0) <= IR(5 downto 3);
+					end if;
+					I_INRC <= '1';
+				when others =>
+				end case;
+			when "01000001"|"01001001"|"01010001"|"01011001"|"01100001"|"01101001"|"01110001"|"01111001" =>
+				-- OUT (C),r
+				-- OUT (C),0
+				MCycles <= "010";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aBC;
+					Set_BusB_To(2 downto 0)     <= IR(5 downto 3);
+					if IR(5 downto 3) = "110" then
+						Set_BusB_To(3) <= '1';
+					end if;
+				when 2 =>
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+					Write <= '1';
+					IORQ <= '1';
+				when others =>
+				end case;
+			when "10100010" | "10101010" | "10110010" | "10111010" =>
+				-- INI, IND, INIR, INDR
+				-- note B is decremented AFTER being put on the bus
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					Set_Addr_To <= aBC;
+					Set_BusB_To <= "1010";
+					Set_BusA_To <= "0000";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					ALU_Op <= "0010";
+				when 2 =>
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+					IORQ <= '1';
+					Set_BusB_To <= "0110";
+					Set_Addr_To <= aXY;
+				when 3 =>
+					if IR(3) = '0' then
+						--IncDec_16 <= "0010";
+						IncDec_16 <= "0110";
+					else
+						--IncDec_16 <= "1010";
+						IncDec_16 <= "1110";
+					end if;
+					TStates <= "100";
+					Write <= '1';
+					I_BTR <= '1';
+				when 4 =>
+					NoRead <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			when "10100011" | "10101011" | "10110011" | "10111011" =>
+				-- OUTI, OUTD, OTIR, OTDR
+				-- note B is decremented BEFORE being put on the bus.
+				-- mikej fix for hl inc
+				MCycles <= "100";
+				case to_integer(unsigned(MCycle)) is
+				when 1 =>
+					TStates <= "101";
+					Set_Addr_To <= aXY;
+					Set_BusB_To <= "1010";
+					Set_BusA_To <= "0000";
+					Read_To_Reg <= '1';
+					Save_ALU <= '1';
+					ALU_Op <= "0010";
+				when 2 =>
+					Set_BusB_To <= "0110";
+					Set_Addr_To <= aBC;
+				when 3 =>
+					if IR(3) = '0' then
+						IncDec_16 <= "0110"; -- mikej
+					else
+						IncDec_16 <= "1110"; -- mikej
+					end if;
+					TStates <= "100"; -- MIKEJ should be 4 for IO cycle
+					IORQ <= '1';
+					Write <= '1';
+					I_BTR <= '1';
+				when 4 =>
+					NoRead <= '1';
+					TStates <= "101";
+				when others => null;
+				end case;
+			end case;
+
+		end case;
+
+		if Mode = 1 then
+			if MCycle = "001" then
+--              TStates <= "100";
+			else
+				TStates <= "011";
+			end if;
+		end if;
+
+		if Mode = 3 then
+			if MCycle = "001" then
+--              TStates <= "100";
+			else
+				TStates <= "100";
+			end if;
+		end if;
+
+		if Mode < 2 then
+			if MCycle = "110" then
+				Inc_PC <= '1';
+				if Mode = 1 then
+					Set_Addr_To <= aXY;
+					TStates <= "100";
+					Set_BusB_To(2 downto 0) <= SSS;
+					Set_BusB_To(3) <= '0';
+				end if;
+				if IRB = "00110110" or IRB = "11001011" then
+					Set_Addr_To <= aNone;
+				end if;
+			end if;
+			if MCycle = "111" then
+				if Mode = 0 then
+					TStates <= "101";
+				end if;
+				if ISet /= "01" then
+					Set_Addr_To <= aXY;
+				end if;
+				Set_BusB_To(2 downto 0) <= SSS;
+				Set_BusB_To(3) <= '0';
+				if IRB = "00110110" or ISet = "01" then
+					-- LD (HL),n
+					Inc_PC <= '1';
+				else
+					NoRead <= '1';
+				end if;
+			end if;
+		end if;
+
+	end process;
+
+end;

+ 220 - 0
vhdl/T80_Pack.vhd

@@ -0,0 +1,220 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 303 add undocumented DDCB and FDCB opcodes by TobiFlex 20.04.2010
+-- Ver 300 started tidyup
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- Z80 compatible microprocessor core
+--
+-- Version : 0242
+--
+-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t80/
+--
+-- Limitations :
+--
+-- File history :
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+
+package T80_Pack is
+
+	component T80
+	generic(
+		Mode : integer := 0;    -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
+		IOWait : integer := 0;  -- 1 => Single cycle I/O, 1 => Std I/O cycle
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		RESET_n         : in std_logic;
+		CLK_n           : in std_logic;
+		CEN                     : in std_logic;
+		WAIT_n          : in std_logic;
+		INT_n           : in std_logic;
+		NMI_n           : in std_logic;
+		BUSRQ_n         : in std_logic;
+		M1_n            : out std_logic;
+		IORQ            : out std_logic;
+		NoRead          : out std_logic;
+		Write           : out std_logic;
+		RFSH_n          : out std_logic;
+		HALT_n          : out std_logic;
+		BUSAK_n         : out std_logic;
+		A                       : out std_logic_vector(15 downto 0);
+		DInst           : in std_logic_vector(7 downto 0);
+		DI                      : in std_logic_vector(7 downto 0);
+		DO                      : out std_logic_vector(7 downto 0);
+		MC                      : out std_logic_vector(2 downto 0);
+		TS                      : out std_logic_vector(2 downto 0);
+		IntCycle_n      : out std_logic;
+		IntE            : out std_logic;
+		Stop            : out std_logic
+	);
+	end component;
+
+	component T80_Reg
+	port(
+		Clk                     : in std_logic;
+		CEN                     : in std_logic;
+		WEH                     : in std_logic;
+		WEL                     : in std_logic;
+		AddrA           : in std_logic_vector(2 downto 0);
+		AddrB           : in std_logic_vector(2 downto 0);
+		AddrC           : in std_logic_vector(2 downto 0);
+		DIH                     : in std_logic_vector(7 downto 0);
+		DIL                     : in std_logic_vector(7 downto 0);
+		DOAH            : out std_logic_vector(7 downto 0);
+		DOAL            : out std_logic_vector(7 downto 0);
+		DOBH            : out std_logic_vector(7 downto 0);
+		DOBL            : out std_logic_vector(7 downto 0);
+		DOCH            : out std_logic_vector(7 downto 0);
+		DOCL            : out std_logic_vector(7 downto 0)
+	);
+	end component;
+
+	component T80_MCode
+	generic(
+		Mode   : integer := 0;
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		IR                      : in  std_logic_vector(7 downto 0);
+		ISet                    : in  std_logic_vector(1 downto 0);
+		MCycle                  : in  std_logic_vector(2 downto 0);
+		F                       : in  std_logic_vector(7 downto 0);
+		NMICycle                : in  std_logic;
+		IntCycle                : in  std_logic;
+		XY_State                : in  std_logic_vector(1 downto 0);
+		MCycles                 : out std_logic_vector(2 downto 0);
+		TStates                 : out std_logic_vector(2 downto 0);
+		Prefix                  : out std_logic_vector(1 downto 0); -- None,BC,ED,DD/FD
+		Inc_PC                  : out std_logic;
+		Inc_WZ                  : out std_logic;
+		IncDec_16               : out std_logic_vector(3 downto 0); -- BC,DE,HL,SP   0 is inc
+		Read_To_Reg             : out std_logic;
+		Read_To_Acc             : out std_logic;
+		Set_BusA_To             : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI/DB,A,SP(L),SP(M),0,F
+		Set_BusB_To             : out std_logic_vector(3 downto 0); -- B,C,D,E,H,L,DI,A,SP(L),SP(M),1,F,PC(L),PC(M),0
+		ALU_Op                  : out std_logic_vector(3 downto 0);
+			-- ADD, ADC, SUB, SBC, AND, XOR, OR, CP, ROT, BIT, SET, RES, DAA, RLD, RRD, None
+		Save_ALU                : out std_logic;
+		PreserveC               : out std_logic;
+		Arith16                 : out std_logic;
+		Set_Addr_To             : out std_logic_vector(2 downto 0); -- aNone,aXY,aIOA,aSP,aBC,aDE,aZI
+		IORQ                    : out std_logic;
+		Jump                    : out std_logic;
+		JumpE                   : out std_logic;
+		JumpXY                  : out std_logic;
+		Call                    : out std_logic;
+		RstP                    : out std_logic;
+		LDZ                     : out std_logic;
+		LDW                     : out std_logic;
+		LDSPHL                  : out std_logic;
+		Special_LD              : out std_logic_vector(2 downto 0); -- A,I;A,R;I,A;R,A;None
+		ExchangeDH              : out std_logic;
+		ExchangeRp              : out std_logic;
+		ExchangeAF              : out std_logic;
+		ExchangeRS              : out std_logic;
+		I_DJNZ                  : out std_logic;
+		I_CPL                   : out std_logic;
+		I_CCF                   : out std_logic;
+		I_SCF                   : out std_logic;
+		I_RETN                  : out std_logic;
+		I_BT                    : out std_logic;
+		I_BC                    : out std_logic;
+		I_BTR                   : out std_logic;
+		I_RLD                   : out std_logic;
+		I_RRD                   : out std_logic;
+		I_INRC                  : out std_logic;
+		SetDI                   : out std_logic;
+		SetEI                   : out std_logic;
+		IMode                   : out std_logic_vector(1 downto 0);
+		Halt                    : out std_logic;
+		NoRead                  : out std_logic;
+		Write                   : out std_logic;
+		XYbit_undoc             : out std_logic
+	);
+	end component;
+
+	component T80_ALU
+	generic(
+		Mode   : integer := 0;
+		Flag_C : integer := 0;
+		Flag_N : integer := 1;
+		Flag_P : integer := 2;
+		Flag_X : integer := 3;
+		Flag_H : integer := 4;
+		Flag_Y : integer := 5;
+		Flag_Z : integer := 6;
+		Flag_S : integer := 7
+	);
+	port(
+		Arith16         : in  std_logic;
+		Z16             : in  std_logic;
+		ALU_Op          : in  std_logic_vector(3 downto 0);
+		IR              : in  std_logic_vector(5 downto 0);
+		ISet            : in  std_logic_vector(1 downto 0);
+		BusA            : in  std_logic_vector(7 downto 0);
+		BusB            : in  std_logic_vector(7 downto 0);
+		F_In            : in  std_logic_vector(7 downto 0);
+		Q               : out std_logic_vector(7 downto 0);
+		F_Out           : out std_logic_vector(7 downto 0)
+	);
+	end component;
+
+end;

+ 114 - 0
vhdl/T80_Reg.vhd

@@ -0,0 +1,114 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 300 started tidyup
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- T80 Registers, technology independent
+--
+-- Version : 0244
+--
+-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t51/
+--
+-- Limitations :
+--
+-- File history :
+--
+--      0242 : Initial release
+--
+--      0244 : Changed to single register file
+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity T80_Reg is
+	port(
+		Clk                     : in std_logic;
+		CEN                     : in std_logic;
+		WEH                     : in std_logic;
+		WEL                     : in std_logic;
+		AddrA           : in std_logic_vector(2 downto 0);
+		AddrB           : in std_logic_vector(2 downto 0);
+		AddrC           : in std_logic_vector(2 downto 0);
+		DIH                     : in std_logic_vector(7 downto 0);
+		DIL                     : in std_logic_vector(7 downto 0);
+		DOAH            : out std_logic_vector(7 downto 0);
+		DOAL            : out std_logic_vector(7 downto 0);
+		DOBH            : out std_logic_vector(7 downto 0);
+		DOBL            : out std_logic_vector(7 downto 0);
+		DOCH            : out std_logic_vector(7 downto 0);
+		DOCL            : out std_logic_vector(7 downto 0)
+	);
+end T80_Reg;
+
+architecture rtl of T80_Reg is
+
+	type Register_Image is array (natural range <>) of std_logic_vector(7 downto 0);
+	signal      RegsH   : Register_Image(0 to 7);
+	signal      RegsL   : Register_Image(0 to 7);
+
+begin
+
+	process (Clk)
+	begin
+		if Clk'event and Clk = '1' then
+			if CEN = '1' then
+				if WEH = '1' then
+					RegsH(to_integer(unsigned(AddrA))) <= DIH;
+				end if;
+				if WEL = '1' then
+					RegsL(to_integer(unsigned(AddrA))) <= DIL;
+				end if;
+			end if;
+		end if;
+	end process;
+
+	DOAH <= RegsH(to_integer(unsigned(AddrA)));
+	DOAL <= RegsL(to_integer(unsigned(AddrA)));
+	DOBH <= RegsH(to_integer(unsigned(AddrB)));
+	DOBL <= RegsL(to_integer(unsigned(AddrB)));
+	DOCH <= RegsH(to_integer(unsigned(AddrC)));
+	DOCL <= RegsL(to_integer(unsigned(AddrC)));
+
+end;

+ 192 - 0
vhdl/T80se.vhd

@@ -0,0 +1,192 @@
+-- ****
+-- T80(b) core. In an effort to merge and maintain bug fixes ....
+--
+--
+-- Ver 300 started tidyup
+-- MikeJ March 2005
+-- Latest version from www.fpgaarcade.com (original www.opencores.org)
+--
+-- ****
+--
+-- Z80 compatible microprocessor core, synchronous top level with clock enable
+-- Different timing than the original z80
+-- Inputs needs to be synchronous and outputs may glitch
+--
+-- Version : 0240
+--
+-- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org)
+--
+-- All rights reserved
+--
+-- Redistribution and use in source and synthezised forms, with or without
+-- modification, are permitted provided that the following conditions are met:
+--
+-- Redistributions of source code must retain the above copyright notice,
+-- this list of conditions and the following disclaimer.
+--
+-- Redistributions in synthesized form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+--
+-- Neither the name of the author nor the names of other contributors may
+-- be used to endorse or promote products derived from this software without
+-- specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+-- POSSIBILITY OF SUCH DAMAGE.
+--
+-- Please report bugs to the author, but before you do so, please
+-- make sure that this is not a derivative work and that
+-- you have the latest version of this file.
+--
+-- The latest version of this file can be found at:
+--      http://www.opencores.org/cvsweb.shtml/t80/
+--
+-- Limitations :
+--
+-- File history :
+--
+--      0235 : First release
+--
+--      0236 : Added T2Write generic
+--
+--      0237 : Fixed T2Write with wait state
+--
+--      0238 : Updated for T80 interface change
+--
+--      0240 : Updated for T80 interface change
+--
+--      0242 : Updated for T80 interface change
+--
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+use work.T80_Pack.all;
+
+entity T80se is
+	generic(
+		Mode : integer := 0;    -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
+		T2Write : integer := 0;  -- 0 => WR_n active in T3, /=0 => WR_n active in T2
+		IOWait : integer := 1   -- 0 => Single cycle I/O, 1 => Std I/O cycle
+	);
+	port(
+		RESET_n         : in  std_logic;
+		CLK_n           : in  std_logic;
+		CLKEN           : in  std_logic;
+		WAIT_n          : in  std_logic;
+		INT_n           : in  std_logic;
+		NMI_n           : in  std_logic;
+		BUSRQ_n         : in  std_logic;
+		M1_n            : out std_logic;
+		MREQ_n          : out std_logic;
+		IORQ_n          : out std_logic;
+		RD_n            : out std_logic;
+		WR_n            : out std_logic;
+		RFSH_n          : out std_logic;
+		HALT_n          : out std_logic;
+		BUSAK_n         : out std_logic;
+		A               : out std_logic_vector(15 downto 0);
+		DI              : in  std_logic_vector(7 downto 0);
+		DO              : out std_logic_vector(7 downto 0)
+	);
+end T80se;
+
+architecture rtl of T80se is
+
+	signal IntCycle_n   : std_logic;
+	signal NoRead       : std_logic;
+	signal Write        : std_logic;
+	signal IORQ         : std_logic;
+	signal DI_Reg       : std_logic_vector(7 downto 0);
+	signal MCycle       : std_logic_vector(2 downto 0);
+	signal TState       : std_logic_vector(2 downto 0);
+
+begin
+
+	u0 : T80
+		generic map(
+			Mode => Mode,
+			IOWait => IOWait)
+		port map(
+			CEN        => CLKEN,
+			M1_n       => M1_n,
+			IORQ       => IORQ,
+			NoRead     => NoRead,
+			Write      => Write,
+			RFSH_n     => RFSH_n,
+			HALT_n     => HALT_n,
+			WAIT_n     => Wait_n,
+			INT_n      => INT_n,
+			NMI_n      => NMI_n,
+			RESET_n    => RESET_n,
+			BUSRQ_n    => BUSRQ_n,
+			BUSAK_n    => BUSAK_n,
+			CLK_n      => CLK_n,
+			A          => A,
+			DInst      => DI,
+			DI         => DI_Reg,
+			DO         => DO,
+			MC         => MCycle,
+			TS         => TState,
+			IntCycle_n => IntCycle_n);
+
+	process (RESET_n, CLK_n)
+	begin
+		if RESET_n = '0' then
+			RD_n <= '1';
+			WR_n <= '1';
+			IORQ_n <= '1';
+			MREQ_n <= '1';
+			DI_Reg <= "00000000";
+		elsif CLK_n'event and CLK_n = '1' then
+			if CLKEN = '1' then
+				RD_n <= '1';
+				WR_n <= '1';
+				IORQ_n <= '1';
+				MREQ_n <= '1';
+				if MCycle = "001" then
+					if TState = "001" or (TState = "010" and Wait_n = '0') then
+						RD_n <= not IntCycle_n;
+						MREQ_n <= not IntCycle_n;
+						IORQ_n <= IntCycle_n;
+					end if;
+					if TState = "011" then
+						MREQ_n <= '0';
+					end if;
+				else
+					if (TState = "001" or (TState = "010" and Wait_n = '0')) and NoRead = '0' and Write = '0' then
+						RD_n <= '0';
+						IORQ_n <= not IORQ;
+						MREQ_n <= IORQ;
+					end if;
+					if T2Write = 0 then
+						if TState = "010" and Write = '1' then
+							WR_n <= '0';
+							IORQ_n <= not IORQ;
+							MREQ_n <= IORQ;
+						end if;
+					else
+						if (TState = "001" or (TState = "010" and Wait_n = '0')) and Write = '1' then
+							WR_n <= '0';
+							IORQ_n <= not IORQ;
+							MREQ_n <= IORQ;
+						end if;
+					end if;
+				end if;
+				if TState = "010" and Wait_n = '1' then
+					DI_Reg <= DI;
+				end if;
+			end if;
+		end if;
+	end process;
+
+end;

+ 96 - 0
vhdl/Z80cpu.vhd

@@ -0,0 +1,96 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Wrap the T80 CPU core and produce more easily comprehended signals      |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+use work.T80_Pack.all;
+
+entity Z80cpu is
+    port (
+        -- reset
+        reset               : in  std_logic;
+
+        -- clocking
+        clk                 : in  std_logic;
+        clk_enable          : in  std_logic;
+
+        -- indicates when we're in the M1 cycle (start of an instruction)
+        m1_cycle            : out std_logic;
+        
+        -- memory and I/O interface
+        req_mem             : out std_logic;    -- memory request?
+        req_io              : out std_logic;    -- i/o request?
+        req_read            : out std_logic;    -- read?
+        req_write           : out std_logic;    -- write?
+        mem_wait            : in  std_logic;    -- memory or i/o can force the CPU to wait
+        address             : out std_logic_vector(15 downto 0);
+        data_in             : in  std_logic_vector(7 downto 0);
+        data_out            : out std_logic_vector(7 downto 0);
+
+        -- interrupts
+        interrupt           : in  std_logic;
+        nmi                 : in  std_logic
+         );
+end Z80cpu;
+
+architecture behavioural of Z80cpu is
+    signal RESET_n : std_logic;
+    signal WAIT_n : std_logic;
+    signal INT_n : std_logic;
+    signal NMI_n : std_logic;
+    signal M1_n : std_logic;
+    signal MREQ_n : std_logic;
+    signal IORQ_n : std_logic;
+    signal RFSH_n : std_logic;
+    signal RD_n : std_logic;
+    signal WR_n : std_logic;
+begin
+
+    RESET_n  <= not reset;
+    WAIT_n   <= not mem_wait;
+    INT_n    <= not interrupt;
+    NMI_n    <= not nmi;
+    m1_cycle <= not M1_n;
+
+    req_mem   <= (not MREQ_n) and (RFSH_n);
+    req_io    <= (not IORQ_n) and (M1_n); -- IORQ is active during M1 when handling interrupts (it's well documented, but I found out the hard way...)
+    req_read  <= (not RD_n) and (RFSH_n);
+    req_write <= (not WR_n);
+
+    cpu : entity work.T80se
+    generic map (
+        Mode => 1,    -- 0 => Z80, 1 => Fast Z80, 2 => 8080, 3 => GB
+        T2Write => 1, -- 0 => WR_n active in T3, /=0 => WR_n active in T2
+        IOWait => 0   -- 0 => single cycle I/O, 1 => standard I/O cycle
+    )
+    port map (
+        RESET_n => RESET_n,
+        CLK_n => clk,
+        CLKEN => clk_enable,
+        WAIT_n => WAIT_n,
+        INT_n => INT_n,
+        NMI_n => NMI_n,
+        BUSRQ_n => '1',
+        BUSAK_n => open,
+        M1_n => M1_n,
+        MREQ_n => MREQ_n,
+        IORQ_n => IORQ_n,
+        RD_n => RD_n,
+        WR_n => WR_n,
+        RFSH_n => RFSH_n,
+        HALT_n => open,
+        A => address,
+        DI => data_in,
+        DO => data_out
+    );
+
+end;

+ 74 - 0
vhdl/clkscale.vhd

@@ -0,0 +1,74 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| An attempt to modulate the CPU clock so it can be slowed down without   |--
+--| also modulating the clock to the peripherals (which would break the     |--
+--| UART and DRAM at least). This works but not all the peripherals are     |--
+--| currently compatible (the UART, at least, doesn't handle this well).    |--
+--| Strongly uggest you avoid using this before you fix the peripherals.    |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity clkscale is
+    port ( clk              : in  std_logic;
+           reset            : in  std_logic;
+           cpu_address      : in  std_logic_vector(2 downto 0);
+           data_in          : in  std_logic_vector(7 downto 0);
+           data_out         : out std_logic_vector(7 downto 0);
+           enable           : in  std_logic;
+           read_notwrite    : in  std_logic;
+           clk_enable       : out std_logic
+    );
+end clkscale;
+
+-- a counter which counts up until it reaches a target value.
+-- when the counter is at the target value the clock is enabled
+-- for one cycle and the counter is reset. the clock is disabled
+-- the rest of the time. this means the clock is enabled in the
+-- proportion 1/(1+r) where r is the register value.
+
+architecture Behavioral of clkscale is
+
+    signal counter_target : unsigned(7 downto 0) := (others => '0');
+    signal counter_value  : unsigned(7 downto 0) := (others => '0');
+    signal output         : std_logic;
+
+begin
+
+    data_out <= std_logic_vector(counter_target);
+    clk_enable <= output;
+
+    clkscale_proc: process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then 
+                counter_target <= to_unsigned(0, 8);
+                counter_value  <= to_unsigned(0, 8);
+                output <= '1';
+            else
+                -- reset on target, enable clock for one cycle
+                if counter_value = counter_target then
+                    counter_value <= to_unsigned(0, 8);
+                    output <= '1';
+                else
+                    counter_value <= counter_value + 1;
+                    output <= '0';
+                end if;
+
+                -- register write
+                if enable = '1' and read_notwrite = '0' then
+                    counter_target <= unsigned(data_in);
+                end if;
+            end if;
+        end if;
+    end process;
+end Behavioral;
+

+ 93 - 0
vhdl/fifo.vhd

@@ -0,0 +1,93 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| FIFO implementation with high water mark. Could be improved; currently  |--
+--| it is impossible to use the last byte in the FIFO (because it cannot    |--
+--| distinguish completely-full from completely-empty)                      |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.std_logic_1164.all;
+use IEEE.numeric_std.all;
+
+entity fifo is
+    generic(
+        depth_log2  : integer := 10; -- 5 gives 32 bytes, implements without a BRAM.
+        hwm_space   : integer := 5;  -- minimum bytes free in buffer before we assert flow control signals
+        width       : integer := 8
+    );
+    port(
+        clk         : in  std_logic;
+        reset       : in  std_logic;
+        write_en    : in  std_logic;
+        write_ready : out std_logic; -- is there space to write?
+        read_en     : in  std_logic;
+        read_ready  : out std_logic; -- is there data waiting to read?
+        data_in     : in  std_logic_vector(width-1 downto 0);
+        data_out    : out std_logic_vector(width-1 downto 0);
+        high_water_mark : out std_logic
+    );
+end fifo;
+
+architecture behaviour of fifo is
+    type fifo_entry is array (natural range <>) of std_logic_vector(width-1 downto 0);
+    signal fifo_contents : fifo_entry(0 to (2 ** depth_log2) - 1); -- this is the FIFO buffer memory
+
+    signal read_ptr  : unsigned(depth_log2-1 downto 0) := (others => '0');
+    signal write_ptr : unsigned(depth_log2-1 downto 0) := (others => '0');
+    signal full      : std_logic;
+    signal empty     : std_logic;
+begin
+
+    is_empty: process(read_ptr, write_ptr)
+    begin
+        if read_ptr = write_ptr then
+            empty <= '1';
+        else
+            empty <= '0';
+        end if;
+        if read_ptr = (write_ptr+1) then
+            full <= '1';
+        else
+            full <= '0';
+        end if;
+        if (write_ptr - read_ptr) >= ((2 ** depth_log2) - 1 - hwm_space) then
+            high_water_mark <= '1';
+        else
+            high_water_mark <= '0';
+        end if;
+    end process;
+
+    fifo_update: process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then
+                -- reset
+                read_ptr  <= to_unsigned(0, depth_log2);
+                write_ptr <= to_unsigned(0, depth_log2);
+            else
+                -- normal operation
+                if write_en = '1' and full = '0' then
+                    fifo_contents(to_integer(write_ptr)) <= data_in;
+                    write_ptr <= write_ptr + 1;
+                end if;
+
+                if read_en = '1' and empty = '0' then
+                    read_ptr <= read_ptr + 1;
+                end if;
+
+                data_out <= fifo_contents(to_integer(read_ptr));
+
+            end if;
+        end if;
+    end process;
+
+    write_ready <= not full;
+    read_ready  <= not empty;
+
+end;

+ 64 - 0
vhdl/gpio.vhd

@@ -0,0 +1,64 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Simple GPIO interface providing 8 bits each of input and output         |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity gpio is
+    port ( clk              : in  std_logic;
+           reset            : in  std_logic;
+           cpu_address      : in  std_logic_vector(2 downto 0);
+           data_in          : in  std_logic_vector(7 downto 0);
+           data_out         : out std_logic_vector(7 downto 0);
+           enable           : in  std_logic;
+           read_notwrite    : in  std_logic;
+           input_pins       : in  std_logic_vector(7 downto 0);
+           output_pins      : out std_logic_vector(7 downto 0)
+    );
+end gpio;
+
+architecture Behavioral of gpio is
+
+    signal captured_inputs  : std_logic_vector(7 downto 0);
+    signal register_outputs : std_logic_vector(7 downto 0) := (others => '1');
+
+begin
+
+    with cpu_address select
+        data_out <=
+            captured_inputs   when "000",
+            register_outputs  when "001",
+            register_outputs  when others;
+
+    output_pins <= register_outputs;
+
+    gpio_proc: process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then
+                captured_inputs <= (others => '0');
+                register_outputs <= (others => '1');
+            else
+                captured_inputs <= input_pins;
+
+                if enable = '1' and read_notwrite = '0' then 
+                    case cpu_address is
+                        when "000" => -- no change
+                        when "001" => register_outputs <= data_in;
+                        when others => -- no change
+                    end case;
+                end if;
+            end if;
+        end if;
+    end process;
+end Behavioral;
+

+ 183 - 0
vhdl/timer.vhd

@@ -0,0 +1,183 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| A simple timer peripheral for timing intervals and generating periodic  |--
+--| interrupts.                                                             |--
+--+-------------------------------------------------------------------------+--
+--
+-- There are two timers; a 1MHz 32-bit counter which always counts up (unless reset to 0)
+-- and whose value can be transferred atomically to a 32-bit latch, and a 1MHz 24-bit down
+-- counter which triggers an interrupt and is reset to a programmable value upon reaching
+-- zero. Writes to the register at base+1 perform timer operations according to the value
+-- written. The 1MHz is derived by prescaling the system clock.
+--
+-- register layout:
+--
+-- address   read value                           write operation
+-- --------- ------------------------------------ -------------------------------------
+-- base+0    status register                      set status register  
+-- base+1      (unused)                           perform operation according to value written
+-- base+2      (unused)                             (no operation)
+-- base+3      (unused)                             (no operation)
+-- base+4    muxed register value (low byte)      update muxed register value
+-- base+5    muxed register value                 update muxed register value
+-- base+6    muxed register value                 update muxed register value
+-- base+7    muxed register value (high byte)     update muxed register value
+--
+--
+-- operation values (for writes to base+1):
+--   00 -- acknowledge interrupt
+--   01 -- reset upcounter value to zero
+--   02 -- update latched value from upcounter value
+--   03 -- reset downcounter
+--   10 -- set register mux select to upcounter current
+--   11 -- set register mux select to upcounter latched
+--   12 -- set register mux select to downcounter current
+--   13 -- set register mux select to downcountre reset
+--
+--
+-- control/status register layout:
+--   bits 0, 1, -- register mux select (controls which register is visible in registers at base+4 through base+7):
+--        0  0  upcounter current value
+--        0  1  upcounter latched value
+--        1  0  downcounter current value
+--        1  1  downcounter reset value
+--     (bits 2, 3, 4, 5 are currently unused)
+--   bit 6 -- countdown timer interrupt enable (0=disable, 1=enable)
+--   bit 7 -- interrupt flag (0=no interrupt, 1=one or interrupts occurred but not yet acknowledged)
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity timer is
+    generic (
+           clk_frequency    : natural := (128 * 1000000)
+    );
+    port ( clk              : in  std_logic;
+           reset            : in  std_logic;
+           cpu_address      : in  std_logic_vector(2 downto 0);
+           data_in          : in  std_logic_vector(7 downto 0);
+           data_out         : out std_logic_vector(7 downto 0);
+           enable           : in  std_logic;
+           req_read         : in  std_logic;
+           req_write        : in  std_logic;
+           interrupt        : out std_logic
+    );
+end timer;
+
+architecture Behavioral of timer is
+
+    signal upcounter_value      : unsigned(31 downto 0) := (others => '0');
+    signal upcounter_latch      : unsigned(31 downto 0) := (others => '0');
+    signal downcounter_value    : unsigned(31 downto 0) := (others => '0');
+    signal downcounter_start    : unsigned(31 downto 0) := (others => '0');
+
+    -- if using frequencies > 128MHz this counter will need to be wider than 7 bits
+    signal counter_prescale     : unsigned(6 downto 0)  := (others => '0');
+    constant prescale_wrap      : unsigned(6 downto 0)  := to_unsigned((clk_frequency / 1000000) - 1, 7); -- aim for a 1MHz counter
+
+    signal interrupt_enable     : std_logic := '0';
+    signal interrupt_signal     : std_logic := '0';
+    signal regmux_select        : std_logic_vector(1 downto 0) := "00";
+
+    signal regmux_output        : std_logic_vector(31 downto 0);
+    signal regmux_updated       : std_logic_vector(31 downto 0);
+    signal status_register_value: std_logic_vector(7 downto 0);
+
+begin
+
+    interrupt <= (interrupt_signal and interrupt_enable);
+
+    with cpu_address select
+        data_out <=
+            status_register_value when "000",
+            regmux_output(7  downto  0) when "100",
+            regmux_output(15 downto  8) when "101",
+            regmux_output(23 downto 16) when "110",
+            regmux_output(31 downto 24) when "111",
+            status_register_value when others;
+
+    status_register_value <= interrupt_signal & interrupt_enable & "0000" & regmux_select;
+
+    with regmux_select select
+        regmux_output <= 
+            std_logic_vector(upcounter_value  ) when "00",
+            std_logic_vector(upcounter_latch  ) when "01",
+            std_logic_vector(downcounter_value) when "10",
+            std_logic_vector(downcounter_start) when "11",
+            std_logic_vector(downcounter_start) when others;
+
+    with cpu_address(1 downto 0) select
+        regmux_updated <= 
+             regmux_output(31 downto 8)  & data_in                               when "00",
+             regmux_output(31 downto 16) & data_in & regmux_output(7  downto 0)  when "01",
+             regmux_output(31 downto 24) & data_in & regmux_output(15 downto 0)  when "10",
+                                           data_in & regmux_output(23 downto 0)  when "11",
+                                           data_in & regmux_output(23 downto 0)  when others;
+
+    counter_proc: process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then
+                upcounter_value    <= (others => '0');
+                upcounter_latch    <= (others => '0');
+                downcounter_value  <= (others => '0');
+                downcounter_start  <= (others => '0');
+                counter_prescale   <= (others => '0');
+                interrupt_enable   <= '0';
+                interrupt_signal   <= '0';
+                regmux_select      <= "00";
+            else
+                -- prescaled counter
+                if counter_prescale = prescale_wrap then
+                    counter_prescale <= (others => '0'); -- reset prescale counter
+                    upcounter_value <= upcounter_value + 1;
+                    if downcounter_value = 0 then
+                        downcounter_value <= downcounter_start;
+                        interrupt_signal <= '1';
+                    else
+                        downcounter_value <= downcounter_value - 1;
+                    end if;
+                else
+                    counter_prescale <= counter_prescale + 1;
+                end if;
+
+                if enable = '1' and req_write = '1' then
+                    if cpu_address = "000" then
+                        interrupt_signal <= data_in(7);
+                        interrupt_enable <= data_in(6);
+                        regmux_select <= data_in(1 downto 0);
+                    elsif cpu_address = "001" then
+                        case data_in is
+                            when "00000000" => interrupt_signal <= '0';
+                            when "00000001" => upcounter_value <= (others => '0');
+                            when "00000010" => upcounter_latch <= upcounter_value;
+                            when "00000011" => downcounter_value <= downcounter_start;
+                            when "00010000" => regmux_select <= "00";
+                            when "00010001" => regmux_select <= "01";
+                            when "00010010" => regmux_select <= "10";
+                            when "00010011" => regmux_select <= "11";
+                            when others =>
+                        end case;
+                    elsif cpu_address(2) = '1' then
+                        case regmux_select is
+                            when "00" => upcounter_value   <= unsigned(regmux_updated);
+                            when "01" => upcounter_latch   <= unsigned(regmux_updated);
+                            when "10" => downcounter_value <= unsigned(regmux_updated);
+                            when "11" => downcounter_start <= unsigned(regmux_updated);
+                            when others =>
+                        end case;
+                    end if;
+                end if;
+            end if;
+        end if;
+    end process;
+end Behavioral;
+

+ 563 - 0
vhdl/top_level.vhd

@@ -0,0 +1,563 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Top level module: connects modules to each other and the outside world  |--
+--+-------------------------------------------------------------------------+--
+--
+-- See README.txt for more details
+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+library UNISIM;
+use UNISIM.VComponents.all;
+
+entity top_level is
+    Port ( sysclk_32m          : in    std_logic;
+           leds                : out   std_logic_vector(4 downto 0);
+           reset_button        : in    std_logic;
+           console_select      : in    std_logic;
+
+           -- UART0 (to FTDI USB chip, no flow control)
+           serial_rx           : in    std_logic;
+           serial_tx           : out   std_logic;
+
+           -- UART0 (to MAX3232 level shifter chip, hardware flow control)
+           uart1_rx            : in    std_logic;
+           uart1_cts           : in    std_logic;
+           uart1_tx            : out   std_logic;
+           uart1_rts           : out   std_logic;
+
+           -- SPI flash chip
+           flash_spi_cs        : out   std_logic;
+           flash_spi_clk       : out   std_logic;
+           flash_spi_mosi      : out   std_logic;
+           flash_spi_miso      : in    std_logic;
+
+           -- SD card socket
+           sdcard_spi_cs       : out   std_logic;
+           sdcard_spi_clk      : out   std_logic;
+           sdcard_spi_mosi     : out   std_logic;
+           sdcard_spi_miso     : in    std_logic;
+
+           -- SDRAM chip
+           SDRAM_CLK           : out   std_logic;
+           SDRAM_CKE           : out   std_logic;
+           SDRAM_CS            : out   std_logic;
+           SDRAM_nRAS          : out   std_logic;
+           SDRAM_nCAS          : out   std_logic;
+           SDRAM_nWE           : out   std_logic;
+           SDRAM_DQM           : out   std_logic_vector( 1 downto 0);
+           SDRAM_ADDR          : out   std_logic_vector (12 downto 0);
+           SDRAM_BA            : out   std_logic_vector( 1 downto 0);
+           SDRAM_DQ            : inout std_logic_vector (15 downto 0)
+       );
+end top_level;
+
+architecture Behavioral of top_level is
+    constant clk_freq_mhz        : natural := 128; -- this is the frequency which the PLL outputs, in MHz.
+
+    -- SDRAM configuration
+    constant sdram_address_width : natural := 22;
+    constant sdram_column_bits   : natural := 8;
+    constant cycles_per_refresh  : natural := (64000*clk_freq_mhz)/4096-1;
+
+    -- For simulation, we don't need a long init stage. but for real DRAM we need approx 101us.
+    -- The constant below has a different value when interpreted by the synthesis and simulator 
+    -- tools in order to achieve the desired timing in each.
+    constant sdram_startup_cycles: natural := 101 * clk_freq_mhz
+    -- pragma translate_off
+    - 10000 -- reduce the value the simulator uses
+    -- pragma translate_on
+    ;
+
+    -- signals for clocking
+    signal clk_feedback         : std_logic;  -- PLL clock feedback
+    signal clk_unbuffered       : std_logic;  -- unbuffered system clock
+    signal clk                  : std_logic;  -- buffered system clock (all logic should be clocked by this)
+
+    -- console latch
+    signal console_select_clk1  : std_logic;
+    signal console_select_sync  : std_logic;
+    signal swap_uart01          : std_logic := '0';
+
+    -- system reset signals
+    signal power_on_reset       : std_logic_vector(1 downto 0) := (others => '1');
+    signal system_reset         : std_logic;
+    signal reset_button_clk1    : std_logic;
+    signal reset_button_sync    : std_logic; -- reset button signal, synchronised to our clock
+    signal reset_request_uart   : std_logic; -- reset request signal from FTDI UART (when you send "!~!~!~" to the UART, this line is asserted)
+
+    -- CPU control
+    signal coldboot             : std_logic;
+    signal cpu_clk_enable       : std_logic;
+    signal cpu_m1_cycle         : std_logic;
+    signal cpu_req_mem          : std_logic;
+    signal cpu_req_io           : std_logic;
+    signal req_mem              : std_logic;
+    signal req_io               : std_logic;
+    signal req_read             : std_logic;
+    signal req_write            : std_logic;
+    signal virtual_address      : std_logic_vector(15 downto 0);
+    signal physical_address     : std_logic_vector(25 downto 0);
+    signal mem_wait             : std_logic;
+    signal cpu_wait             : std_logic;
+    signal dram_wait            : std_logic;
+    signal mmu_wait             : std_logic;
+    signal spimaster0_wait      : std_logic;
+    signal spimaster1_wait      : std_logic;
+
+    -- chip selects
+    signal mmu_cs               : std_logic;
+    signal rom_cs               : std_logic;
+    signal sram_cs              : std_logic;
+    signal dram_cs              : std_logic;
+    signal uartA_cs             : std_logic;
+    signal uartB_cs             : std_logic;
+    signal uart0_cs             : std_logic;
+    signal uart1_cs             : std_logic;
+    signal timer_cs             : std_logic;
+    signal spimaster0_cs        : std_logic;
+    signal spimaster1_cs        : std_logic;
+    signal clkscale_cs          : std_logic;
+    signal gpio_cs              : std_logic;
+
+    -- data bus
+    signal cpu_data_in          : std_logic_vector(7 downto 0);
+    signal cpu_data_out         : std_logic_vector(7 downto 0);
+    signal rom_data_out         : std_logic_vector(7 downto 0);
+    signal sram_data_out        : std_logic_vector(7 downto 0);
+    signal dram_data_out        : std_logic_vector(7 downto 0);
+    signal uart0_data_out       : std_logic_vector(7 downto 0);
+    signal uart1_data_out       : std_logic_vector(7 downto 0);
+    signal timer_data_out       : std_logic_vector(7 downto 0);
+    signal spimaster0_data_out  : std_logic_vector(7 downto 0);
+    signal spimaster1_data_out  : std_logic_vector(7 downto 0);
+    signal mmu_data_out         : std_logic_vector(7 downto 0);
+    signal clkscale_out         : std_logic_vector(7 downto 0);
+    signal gpio_data_out        : std_logic_vector(7 downto 0);
+
+    -- GPIO
+    signal gpio_input           : std_logic_vector(7 downto 0);
+    signal gpio_output          : std_logic_vector(7 downto 0);
+
+    -- Interrupts
+    signal cpu_interrupt_in     : std_logic;
+    signal timer_interrupt      : std_logic;
+    signal uart0_interrupt      : std_logic;
+    signal uart1_interrupt      : std_logic;
+
+begin
+    -- Hold CPU reset high for 8 clock cycles on startup,
+    -- and when the user presses their reset button.
+    process(clk)
+    begin
+        if rising_edge(clk) then
+            -- Xilinx advises using two flip-flops are used to bring external
+            -- signals which feed control logic into our clock domain.
+            reset_button_clk1 <= reset_button;
+            reset_button_sync <= reset_button_clk1;
+            console_select_clk1 <= console_select;
+            console_select_sync <= console_select_clk1;
+
+            -- reset the system when requested
+            if (power_on_reset(0) = '1' or reset_button_sync = '1' or reset_request_uart = '1') then
+                system_reset <= '1';
+            else
+                system_reset <= '0';
+            end if;
+
+            -- shift 0s into the power_on_reset shift register from the MSB
+            power_on_reset <= '0' & power_on_reset(power_on_reset'length-1 downto 1);
+
+            -- During reset, latch the console select jumper. This is used to
+            -- optionally swap over the UART roles and move the system console to
+            -- the second serial port on the IO board.
+            if system_reset = '1' then
+                swap_uart01 <= console_select_sync;
+            else
+                swap_uart01 <= swap_uart01;
+            end if;
+        end if;
+    end process;
+
+    -- GPIO input signal routing
+    gpio_input <= coldboot & swap_uart01 & "000000";
+
+    -- GPIO output signal routing
+    leds(0) <= gpio_output(0);
+    leds(1) <= gpio_output(1);
+    leds(2) <= gpio_output(2);
+    leds(3) <= gpio_output(3);
+
+    -- User LED (LED1) on Papilio Pro indicates when the CPU is being asked to wait (eg by the SDRAM cache)
+    leds(4) <= cpu_wait;
+
+    -- Interrupt signal for the CPU
+    cpu_interrupt_in <= (timer_interrupt or uart0_interrupt or uart1_interrupt);
+
+    -- Z80 CPU core
+    cpu: entity work.Z80cpu
+    port map (
+                 reset => system_reset,
+                 clk => clk,
+                 clk_enable => cpu_clk_enable,
+                 m1_cycle => cpu_m1_cycle,
+                 interrupt => cpu_interrupt_in,
+                 nmi => '0',
+                 req_mem => cpu_req_mem,
+                 req_io => cpu_req_io,
+                 req_read => req_read,
+                 req_write => req_write,
+                 mem_wait => cpu_wait,
+                 address => virtual_address,
+                 data_in => cpu_data_in,
+                 data_out => cpu_data_out
+             );
+
+    -- Memory management unit
+    mmu: entity work.MMU
+    port map (
+                reset => system_reset,
+                clk => clk,
+                address_in => virtual_address,
+                address_out => physical_address,
+                cpu_data_in => cpu_data_out,
+                cpu_data_out => mmu_data_out,
+                req_mem_in => cpu_req_mem,
+                req_io_in  => cpu_req_io,
+                req_mem_out => req_mem,
+                req_io_out => req_io,
+                req_read => req_read,
+                req_write => req_write,
+                io_cs => mmu_cs,
+                cpu_wait => mmu_wait,
+                access_violated => open -- for now!!
+            );
+
+    -- This process determines which IO or memory device the CPU is addressing
+    -- and asserts the appropriate chip select signals.
+    cs_process: process(req_mem, req_io, physical_address, virtual_address, uartA_cs, uartB_cs, swap_uart01)
+    begin
+        -- memory chip selects: default to unselected
+        rom_cs   <= '0';
+        sram_cs  <= '0';
+        dram_cs  <= '0';
+
+        -- io chip selects: default to unselected
+        uartA_cs      <= '0';
+        uartB_cs      <= '0';
+        mmu_cs        <= '0';
+        timer_cs      <= '0';
+        spimaster0_cs <= '0';
+        spimaster1_cs <= '0';
+        clkscale_cs   <= '0';
+        gpio_cs       <= '0';
+
+        -- memory address decoding
+        -- address space is organised as:
+        -- 0x0 000 000 - 0x0 FFF FFF  16MB DRAM (cached)    (mapped to 8MB DRAM twice)
+        -- 0x1 000 000 - 0x1 FFF FFF  16MB DRAM (uncached)  (mapped to 8MB DRAM twice)
+        -- 0x2 000 000 - 0x2 000 FFF   4KB monitor ROM      (FPGA block RAM)
+        -- 0x2 001 000 - 0x2 001 FFF   4KB SRAM             (FPGA block RAM)
+        -- 0x2 002 000 - 0x3 FFF FFF  unused space for future expansion
+        if physical_address(25) = '0' then
+            -- bottom 32MB: DRAM handles this
+            dram_cs <= req_mem;
+        else
+            -- top 32MB: other memory devices
+            case physical_address(24 downto 12) is
+                when "0000000000000" =>  rom_cs <= req_mem;
+                when "0000000000001" => sram_cs <= req_mem;
+                when others => -- undecoded memory space
+            end case;
+        end if;
+
+        -- IO address decoding
+        case virtual_address(7 downto 3) is
+            when "00000" => uartA_cs            <= req_io;  -- 00 ... 07
+            when "00010" => timer_cs            <= req_io;  -- 10 ... 17
+            when "00011" => spimaster0_cs       <= req_io;  -- 18 ... 1F
+            when "00100" => gpio_cs             <= req_io;  -- 20 ... 27
+            when "00101" => uartB_cs            <= req_io;  -- 28 ... 2F
+            when "00110" => spimaster1_cs       <= req_io;  -- 30 ... 37
+                                                            -- unused ports
+            when "11110" => clkscale_cs         <= req_io;  -- F0 ... F7
+            when "11111" => mmu_cs              <= req_io;  -- F8 ... FF
+            when others =>
+        end case;
+
+        -- send the UART chip select to the appropriate UART depending
+        -- on whether they have been swapped over or not.
+        if swap_uart01 = '0' then
+            uart0_cs <= uartB_cs;
+            uart1_cs <= uartA_cs;
+        else
+            uart0_cs <= uartA_cs;
+            uart1_cs <= uartB_cs;
+        end if;
+    end process;
+
+    -- the selected memory device can request the CPU to wait
+    mem_wait <=
+       dram_wait       when dram_cs='1' else
+       spimaster0_wait when spimaster0_cs='1' else
+       spimaster1_wait when spimaster1_cs='1' else
+       '0';
+
+    -- the MMU can, at any time, request the CPU wait (this is used when 
+    -- translating IO to memory requests, to implement a wait state for 
+    -- the "17th page")
+    cpu_wait <= (mem_wait or mmu_wait);
+
+    -- input mux for CPU data bus
+    cpu_data_in <=
+       rom_data_out        when        rom_cs='1' else
+       dram_data_out       when       dram_cs='1' else
+       sram_data_out       when       sram_cs='1' else
+       uart0_data_out      when      uart0_cs='1' else
+       uart1_data_out      when      uart1_cs='1' else
+       timer_data_out      when      timer_cs='1' else
+       mmu_data_out        when        mmu_cs='1' else
+       spimaster0_data_out when spimaster0_cs='1' else
+       spimaster1_data_out when spimaster1_cs='1' else
+       clkscale_out        when   clkscale_cs='1' else
+       gpio_data_out       when       gpio_cs='1' else
+       rom_data_out; -- default case
+
+   dram: entity work.DRAM
+   generic map(
+               sdram_address_width => sdram_address_width,
+               sdram_column_bits   => sdram_column_bits,
+               sdram_startup_cycles=> sdram_startup_cycles,
+               cycles_per_refresh  => cycles_per_refresh
+              )
+   port map(
+               clk             => clk,
+               reset           => '0', -- important to note that we DO NOT reset the SDRAM controller on reset (it would stop refreshing, which would be bad)
+
+               -- interface to synthetic CPU
+               cs => dram_cs,
+               req_read => req_read,
+               req_write => req_write,
+               mem_address => physical_address(24 downto 0),
+               mem_wait => dram_wait,
+               data_in => cpu_data_out,
+               data_out => dram_data_out,
+               coldboot => coldboot,
+
+               -- interface to hardware SDRAM chip
+               SDRAM_CLK       => SDRAM_CLK,
+               SDRAM_CKE       => SDRAM_CKE,
+               SDRAM_CS        => SDRAM_CS,
+               SDRAM_nRAS      => SDRAM_nRAS,
+               SDRAM_nCAS      => SDRAM_nCAS,
+               SDRAM_nWE       => SDRAM_nWE,
+               SDRAM_DQM       => SDRAM_DQM,
+               SDRAM_BA        => SDRAM_BA,
+               SDRAM_ADDR      => SDRAM_ADDR,
+               SDRAM_DQ        => SDRAM_DQ
+           );
+
+   -- 4KB system ROM implemented in block RAM
+   rom: entity work.MonZ80
+   port map(
+               clk => clk,
+               A => physical_address(11 downto 0),
+               D => rom_data_out
+           );
+
+   -- 4KB SRAM memory implemented in block RAM
+   sram: entity work.SSRAM
+   generic map(
+                  AddrWidth => 12
+              )
+   port map(
+               clk => clk,
+               ce => sram_cs,
+               we => req_write,
+               A => physical_address(11 downto 0),
+               DIn => cpu_data_out,
+               DOut => sram_data_out
+           );
+
+   -- UART connected to FTDI USB UART
+   uart0: entity work.uart_interface
+   generic map ( watch_for_reset => 1, clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               reset_out => reset_request_uart, -- result of watching for reset sequence on the input
+               serial_in => serial_rx,
+               serial_out => serial_tx,
+               serial_rts => open,
+               serial_cts => '0',
+               cpu_address => virtual_address(2 downto 0),
+               cpu_data_in => cpu_data_out,
+               cpu_data_out => uart0_data_out,
+               enable => uart0_cs,
+               interrupt => uart0_interrupt,
+               req_read => req_read,
+               req_write => req_write
+           );
+
+   -- UART connected to MAX3232 on optional IO board
+   uart1: entity work.uart_interface
+   generic map ( flow_control => 1, clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               serial_in => uart1_rx,
+               serial_out => uart1_tx,
+               serial_rts => uart1_rts,
+               serial_cts => uart1_cts,
+               cpu_address => virtual_address(2 downto 0),
+               cpu_data_in => cpu_data_out,
+               cpu_data_out => uart1_data_out,
+               enable => uart1_cs,
+               interrupt => uart1_interrupt,
+               req_read => req_read,
+               req_write => req_write
+           );
+
+   -- Timer device (internally scales the clock to 1MHz)
+   timer: entity work.timer
+   generic map ( clk_frequency => (clk_freq_mhz * 1000000) )
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => timer_data_out,
+               enable => timer_cs,
+               req_read => req_read,
+               req_write => req_write,
+               interrupt => timer_interrupt
+           );
+
+   -- SPI master device connected to Papilio Pro 8MB flash ROM
+   spimaster0: entity work.spimaster
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               cpu_wait => spimaster0_wait,
+               data_in => cpu_data_out,
+               data_out => spimaster0_data_out,
+               enable => spimaster0_cs,
+               req_read => req_read,
+               req_write => req_write,
+               slave_cs => flash_spi_cs,
+               slave_clk => flash_spi_clk,
+               slave_mosi => flash_spi_mosi,
+               slave_miso => flash_spi_miso
+           );
+
+   -- SPI master device connected to SD card socket on the IO board
+   spimaster1: entity work.spimaster
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               cpu_wait => spimaster1_wait,
+               data_in => cpu_data_out,
+               data_out => spimaster1_data_out,
+               enable => spimaster1_cs,
+               req_read => req_read,
+               req_write => req_write,
+               slave_cs => sdcard_spi_cs,
+               slave_clk => sdcard_spi_clk,
+               slave_mosi => sdcard_spi_mosi,
+               slave_miso => sdcard_spi_miso
+           );
+
+   -- GPIO to FPGA pins and/or internal signals
+   gpio: entity work.gpio
+   port map(
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => gpio_data_out,
+               enable => gpio_cs,
+               read_notwrite => req_read,
+               input_pins => gpio_input,
+               output_pins => gpio_output
+           );
+
+   -- An attempt to allow the CPU clock to be scaled back to run
+   -- at slower speeds without affecting the clock signal sent to
+   -- IO devices. Basically this was an attempt to make CP/M games 
+   -- playable :) Very limited success. Might be simpler to remove
+   -- this entirely.
+   clkscale: entity work.clkscale
+   port map (
+               clk => clk,
+               reset => system_reset,
+               cpu_address => virtual_address(2 downto 0),
+               data_in => cpu_data_out,
+               data_out => clkscale_out,
+               enable => clkscale_cs,
+               read_notwrite => req_read,
+               clk_enable => cpu_clk_enable
+           );
+
+   -- PLL scales 32MHz Papilio Pro oscillator frequency to 128MHz
+   -- clock for our logic.
+   clock_pll: PLL_BASE 
+   generic map (
+               BANDWIDTH      => "OPTIMIZED",        -- "HIGH", "LOW" or "OPTIMIZED" 
+               CLKFBOUT_MULT  => 16,                 -- Multiply value for all CLKOUT clock outputs (1-64)
+               CLKFBOUT_PHASE => 0.0,                -- Phase offset in degrees of the clock feedback output (0.0-360.0).
+               CLKIN_PERIOD   => 31.25,              -- Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
+                                                     -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for CLKOUT# clock output (1-128)
+               CLKOUT0_DIVIDE => 4,                  -- 32MHz * 16 / 4 = 128MHz. Adjust clk_freq_mhz constant (above) if you change this.
+               CLKOUT1_DIVIDE => 1,
+               CLKOUT2_DIVIDE => 1,       
+               CLKOUT3_DIVIDE => 1,
+               CLKOUT4_DIVIDE => 1,       
+               CLKOUT5_DIVIDE => 1,
+                                               -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for CLKOUT# clock output (0.01-0.99).
+               CLKOUT0_DUTY_CYCLE => 0.5, CLKOUT1_DUTY_CYCLE => 0.5,
+               CLKOUT2_DUTY_CYCLE => 0.5, CLKOUT3_DUTY_CYCLE => 0.5,
+               CLKOUT4_DUTY_CYCLE => 0.5, CLKOUT5_DUTY_CYCLE => 0.5,
+                                               -- CLKOUT0_PHASE - CLKOUT5_PHASE: Output phase relationship for CLKOUT# clock output (-360.0-360.0).
+               CLKOUT0_PHASE => 0.0,      CLKOUT1_PHASE => 0.0, -- Capture clock
+               CLKOUT2_PHASE => 0.0,      CLKOUT3_PHASE => 0.0,
+               CLKOUT4_PHASE => 0.0,      CLKOUT5_PHASE => 0.0,
+
+               CLK_FEEDBACK => "CLKFBOUT",           -- Clock source to drive CLKFBIN ("CLKFBOUT" or "CLKOUT0")
+               COMPENSATION => "SYSTEM_SYNCHRONOUS", -- "SYSTEM_SYNCHRONOUS", "SOURCE_SYNCHRONOUS", "EXTERNAL" 
+               DIVCLK_DIVIDE => 1,                   -- Division value for all output clocks (1-52)
+               REF_JITTER => 0.1,                    -- Reference Clock Jitter in UI (0.000-0.999).
+               RESET_ON_LOSS_OF_LOCK => FALSE        -- Must be set to FALSE
+           ) 
+   port map(
+               CLKIN    => sysclk_32m,   -- 1-bit input: Clock input
+               CLKFBOUT => clk_feedback, -- 1-bit output: PLL_BASE feedback output
+               CLKFBIN  => clk_feedback, -- 1-bit input: Feedback clock input
+                                         -- CLKOUT0 - CLKOUT5: 1-bit (each) output: Clock outputs
+               CLKOUT0 => clk_unbuffered, -- 64MHz clock output
+               CLKOUT1 => open,
+               CLKOUT2 => open,      
+               CLKOUT3 => open,
+               CLKOUT4 => open,      
+               CLKOUT5 => open,
+               LOCKED  => open,  -- 1-bit output: PLL_BASE lock status output
+               RST     => '0'    -- 1-bit input: Reset input
+           );
+
+   -- Buffering of clocks
+   BUFG_clk: BUFG 
+   port map(
+               O => clk,     
+               I => clk_unbuffered
+            );
+
+end Behavioral;

+ 193 - 0
vhdl/uart.vhd

@@ -0,0 +1,193 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| UART implementation                                                     |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity uart is
+    generic (
+           clk_frequency    : natural := (128 * 1000000)
+    );
+    port ( clk              : in  std_logic;
+           serial_out       : out std_logic;
+           serial_in        : in  std_logic;
+           data_in          : in  std_logic_vector(7 downto 0);
+           data_in_load     : in  std_logic;
+           data_out         : out std_logic_vector(7 downto 0);
+           data_out_ready   : out std_logic;
+           bad_bit          : out std_logic;
+           transmitter_busy : out std_logic;
+           can_transmit     : in  std_logic
+    );
+end uart;
+
+architecture Behavioral of uart is
+
+    -- tested at 1,000,000bps with 48MHz clock. Works (apparently).
+    constant rx_sample_interval : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 * 16) - 1, 14);  -- clock speed / (baud x 16) - 1 ; eg 32MHz / (9600 * 16) - 1 = 207
+    constant bit_duration       : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 *  1) - 1, 14);  -- clock speed / baud - 1 ; eg 32MHz / 9600 - 1 = 3332
+
+    signal tx_counter   : unsigned(13 downto 0) := to_unsigned(0, 14);
+    signal tx_shift_reg : std_logic_vector(8 downto 0) := "111111111";
+    signal tx_bits_left : unsigned(3 downto 0) := to_unsigned(0, 4);
+    signal tx_busy      : std_logic;
+
+    signal rx_counter   : unsigned(13 downto 0) := to_unsigned(0, 14);
+    signal rx_shift_reg : std_logic_vector(8 downto 0) := "000000000";
+    signal rx_bits_got  : unsigned(3 downto 0) := to_unsigned(0, 4);
+    signal rx_state     : unsigned(7 downto 0) := (others => '0'); -- 10 bits x 16 samples each = at least 160 states.
+    signal rx_out_ready : std_logic := '0';
+    signal data_out_buf : std_logic_vector(7 downto 0) := "00000000";
+
+    signal rx_clkin1    : std_logic := '0';
+    signal rx_clkin2    : std_logic := '0';
+    signal rx_sample1   : std_logic := '0';
+    signal rx_sample2   : std_logic := '0';
+    signal rx_sample3   : std_logic := '0';
+    signal rx_sample_majority : std_logic;
+
+    signal rx_badbit    : std_logic := '0';
+
+begin
+
+    -- -- receiver -- --
+    --
+    -- Incoming data is oversampled 16 times. We check three samples in the
+    -- middle of each bit and take a simple majority. There is provision for
+    -- rejecting noise where a start bit should have been. We compensate for
+    -- small amounts of clock drift by potentially cutting a stop bit short.
+    --
+    -- This is not dissimilar to how the AVR USART receiver works.
+    --
+
+    data_out_ready <= rx_out_ready;
+    bad_bit <= rx_badbit;
+    data_out <= data_out_buf;
+
+    rx_sample_majority <= (rx_sample1 and rx_sample2) or (rx_sample1 and rx_sample3) or (rx_sample2 and rx_sample3); -- simple majority wins
+
+    receiver: process(clk)
+    begin
+        if rising_edge(clk) then
+            -- Bring serial_in into our clock domain
+            rx_clkin1 <= serial_in;
+            rx_clkin2 <= rx_clkin1; -- rx_clkin2 should now be safe to use.
+
+            -- We latch the incoming serial data at full clock speed (NOT divided down)
+            rx_sample1 <= rx_clkin2;
+            rx_out_ready <= '0';
+
+            -- bad bit
+            rx_badbit <= rx_badbit;
+
+            -- clock divider
+            rx_counter <= rx_counter + 1;
+            if rx_counter = rx_sample_interval then
+                rx_counter <= (others => '0');
+
+                if rx_state = "00000000" then 
+                    -- line is in the idle state, we're waiting for a start bit!
+                    -- the anticipation is killing me.
+                    if rx_sample1 = '0' then
+                        rx_state <= "00000001"; -- and we're off!
+                        rx_counter <= (others => '0');
+                    end if;
+                elsif rx_state = "10011010" then 
+                    -- wait for the line to be idle. we don't leave this state until the serial line 
+                    -- goes high (it should be already, because we should be in mid stop bit).
+                    if rx_sample1 = '1' then
+                        rx_state <= "00000000";
+                    end if;
+                else
+                    -- we're in the normal bit reception pattern
+                    rx_state <= rx_state + 1;
+
+                    -- rx_sample1 contains the incoming serial data, latched
+                    rx_sample3 <= rx_sample2;
+                    rx_sample2 <= rx_sample1;
+
+                    -- when we have the three middle samples, update the shift register
+                    if rx_state(3 downto 0) = "1001" then
+                        rx_shift_reg <= rx_sample_majority & rx_shift_reg(rx_shift_reg'length-1 downto 1);
+                        -- false start bit noise rejection: if we read the start bit as a logical 1, start over again.
+                        if (rx_state(7 downto 4) = "0000") and (rx_sample_majority = '1') then
+                            rx_badbit <= '1';
+                            rx_state <= "00000000";
+                        end if;
+                        -- check stop bit framing and alert CPU if valid byte received
+                        if (rx_state(7 downto 4) = "1001") then 
+                            if (rx_sample_majority = '1') then
+                                data_out_buf <= rx_shift_reg(8 downto 1);
+                                rx_out_ready <= '1';
+                                rx_badbit <= '0';
+                            else
+                                rx_badbit <= '1';
+                            end if;
+                            -- if line is high we can skip waiting for the line to go idle which buys us a little more tolerance for clock drift.
+                            if rx_sample1 = '1' then
+                                rx_state <= "00000000";
+                            end if;
+                        end if;
+                    end if;
+                end if;
+            end if;
+        end if;
+    end process;
+    
+
+    -- -- transmitter -- --
+    --
+    -- just clock out the bits, damn it.
+    --
+
+    serial_out <= tx_shift_reg(0); -- we always output the bottom bit of the shift register.
+    transmitter_busy <= tx_busy; -- or data_in_load;
+
+    transmitter: process(clk)
+    begin
+
+        if rising_edge(clk) then
+            tx_busy <= '1';
+
+            if tx_bits_left = 0 then
+                -- idle
+                if data_in_load = '1' then
+                    tx_shift_reg <= data_in & '0';              -- data bits, start bit
+                    tx_bits_left <= to_unsigned(10, 4);         -- total ten bits to transmit including stop bit
+                    tx_counter <= (others => '0');              -- reset counter
+                else
+                    if can_transmit = '0' then
+                        tx_busy <= '1';
+                    else
+                        tx_busy <= '0';
+                    end if;
+                end if;
+            else
+                -- busy
+                if (tx_counter = 0) and (tx_bits_left = 10) and (can_transmit = '0') then
+                    -- do nothing, we're waiting for our peer to indicate that we can transmit
+                    tx_counter <= (others => '0');
+                else
+                    tx_counter <= tx_counter + 1;
+                    if tx_counter = bit_duration then
+                        -- shift out the next bit
+                        tx_shift_reg <= '1' & tx_shift_reg(8 downto 1); -- stop bit and line idle state are both 1 so shift that in the top
+                        tx_counter <= (others => '0');              -- reset counter
+                        tx_bits_left <= tx_bits_left - 1;
+                    end if;
+                end if;
+            end if;
+        end if;
+    end process;
+
+end Behavioral;
+

+ 216 - 0
vhdl/uart_interface.vhd

@@ -0,0 +1,216 @@
+--+-----------------------------------+-------------------------------------+--
+--|                      ___   ___    | (c) 2013-2014 William R Sowerbutts  |--
+--|   ___  ___   ___ ___( _ ) / _ \   | will@sowerbutts.com                 |--
+--|  / __|/ _ \ / __|_  / _ \| | | |  |                                     |--
+--|  \__ \ (_) | (__ / / (_) | |_| |  | A Z80 FPGA computer, just for fun   |--
+--|  |___/\___/ \___/___\___/ \___/   |                                     |--
+--|                                   |              http://sowerbutts.com/ |--
+--+-----------------------------------+-------------------------------------+--
+--| Combine the UART with a receive FIFO and provide an interface to the    |--
+--| microprocessor.                                                         |--
+--+-------------------------------------------------------------------------+--
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity uart_interface is
+    Generic ( watch_for_reset : integer := 0;
+              clk_frequency   : natural := (128*1000000);
+              flow_control    : integer := 0) ;
+    Port ( clk           : in  std_logic;
+           reset         : in  std_logic;
+              -- rs232
+           serial_in     : in  std_logic;
+           serial_out    : out std_logic;
+              -- flow control (optional)
+           serial_cts    : in  std_logic;
+           serial_rts    : out std_logic;
+              -- memory interface
+           cpu_address   : in  std_logic_vector (2 downto 0);
+           cpu_data_in   : in  std_logic_vector (7 downto 0);
+           cpu_data_out  : out std_logic_vector (7 downto 0);
+           reset_out     : out std_logic;
+           enable        : in  std_logic;
+           interrupt     : out std_logic;
+           req_read      : in  std_logic;
+           req_write     : in  std_logic);
+end uart_interface;
+
+architecture Behavioral of uart_interface is
+    signal uart_data_in         : std_logic_vector(7 downto 0);
+    signal uart_data_out        : std_logic_vector(7 downto 0);
+    signal fifo_data_out        : std_logic_vector(7 downto 0);
+    signal fifo_data_ready      : std_logic;
+    signal uart_rx_ready        : std_logic;
+    signal uart_tx_busy         : std_logic;
+    signal uart_badbit          : std_logic;
+    signal uart_data_load       : std_logic;
+    signal fifo_data_ack        : std_logic;
+    signal uart_status_register : std_logic_vector(7 downto 0);
+    signal rx_interrupt_enable  : std_logic := '0';
+    signal tx_interrupt_enable  : std_logic := '0';
+    signal rx_interrupt_signal  : std_logic := '0';
+    signal tx_interrupt_signal  : std_logic := '0';
+    signal uart_tx_was_busy     : std_logic := '0';
+    signal fifo_data_was_ready  : std_logic := '0';
+    signal cts_clk1             : std_logic;
+    signal cts_clk2             : std_logic;
+    signal fifo_nearly_full     : std_logic;
+    signal can_transmit         : std_logic;
+
+    type reset_seq_state is (
+                 st_idle,
+                 st_seen1,
+                 st_seen2,
+                 st_seen3,
+                 st_seen4,
+                 st_seen5
+             );
+    signal reset_seq : reset_seq_state := st_idle;
+    signal reset_saw_byte0  : std_logic;
+    signal reset_saw_byte1  : std_logic;
+begin
+
+    -- this whole module could really do with a bit of a rethink.
+    with cpu_address select
+        cpu_data_out <=
+            uart_status_register when "000",
+            fifo_data_out when others;
+
+    uart_data_in <= cpu_data_in;
+
+    uart_status_register <= fifo_data_ready & uart_tx_busy & '0' & uart_badbit & rx_interrupt_enable & tx_interrupt_enable & rx_interrupt_signal & tx_interrupt_signal;
+
+    interrupt <= (rx_interrupt_signal and rx_interrupt_enable) or (tx_interrupt_signal and tx_interrupt_enable);
+
+    -- this decodes cpu_address="001" in a rather longwinded way.
+    uart_data_load <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_write;
+    fifo_data_ack  <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_read;
+
+    -- optional hardware flow control
+    process(fifo_nearly_full, cts_clk2)
+    begin
+        if flow_control = 1 then
+            serial_rts <= fifo_nearly_full;
+            can_transmit <= (not cts_clk2);
+        else
+            serial_rts <= '0';
+            can_transmit <= '1';
+        end if;
+    end process;
+    
+    -- optional reset on data sequence
+    process(uart_data_out)
+    begin
+        if watch_for_reset = 1 then
+            if uart_data_out = "00100001" then
+                reset_saw_byte0 <= '1';
+                reset_saw_byte1 <= '0';
+            elsif uart_data_out = "01111110" then
+                reset_saw_byte0 <= '0';
+                reset_saw_byte1 <= '1';
+            else
+                reset_saw_byte0 <= '0';
+                reset_saw_byte1 <= '0';
+            end if;
+        end if;
+    end process;
+
+
+    process(clk)
+    begin
+        if rising_edge(clk) then
+            if reset = '1' then
+                fifo_data_was_ready <= '0';
+                rx_interrupt_enable <= '0';
+                rx_interrupt_signal <= '0';
+                tx_interrupt_enable <= '0';
+                tx_interrupt_signal <= '0';
+                uart_tx_was_busy <= '0';
+                reset_out <= '0';
+                reset_seq <= st_idle;
+                cts_clk1 <= '0';
+                cts_clk2 <= '0';
+            else
+                tx_interrupt_signal <= tx_interrupt_signal;
+                rx_interrupt_signal <= rx_interrupt_signal;
+
+                -- bring CTS into our clock domain
+                if flow_control = 1 then
+                    cts_clk1 <= serial_cts;
+                    cts_clk2 <= cts_clk1;
+                end if;
+
+                -- handle writes to the status register
+                if enable = '1' and req_write = '1' and cpu_address = "000" then
+                    rx_interrupt_enable <= cpu_data_in(3);
+                    tx_interrupt_enable <= cpu_data_in(2);
+                    rx_interrupt_signal <= cpu_data_in(1);
+                    tx_interrupt_signal <= cpu_data_in(0);
+                end if;
+
+                uart_tx_was_busy <= uart_tx_busy;
+                fifo_data_was_ready <= fifo_data_ready;
+
+                if uart_tx_was_busy = '1' and uart_tx_busy = '0' then
+                    tx_interrupt_signal <= '1';
+                end if;
+
+                if fifo_data_ready = '1' and fifo_data_was_ready = '0' then
+                    rx_interrupt_signal <= '1';
+                end if;
+
+                if watch_for_reset = 1 then
+                    if uart_rx_ready = '1' then
+                        reset_seq <= st_idle; -- end up here unless we match the conditions below
+                        reset_out <= '0';
+                        case reset_seq is
+                            when st_idle  => if reset_saw_byte0 = '1' then reset_seq <= st_seen1; end if;
+                            when st_seen1 => if reset_saw_byte1 = '1' then reset_seq <= st_seen2; end if;
+                            when st_seen2 => if reset_saw_byte0 = '1' then reset_seq <= st_seen3; end if;
+                            when st_seen3 => if reset_saw_byte1 = '1' then reset_seq <= st_seen4; end if;
+                            when st_seen4 => if reset_saw_byte0 = '1' then reset_seq <= st_seen5; end if;
+                            when st_seen5 => if reset_saw_byte1 = '1' then reset_out <= '1'; end if;
+                            when others =>
+                        end case;
+                    else
+                        reset_seq <= reset_seq;
+                        reset_out <= '0';
+                    end if;
+                end if;
+            end if;
+        end if;
+    end process;
+
+    fifo_instance: entity work.fifo
+    PORT MAP(
+        clk => clk,
+        reset => reset,
+        data_in => uart_data_out,
+        data_out => fifo_data_out,
+        read_ready => fifo_data_ready,
+        read_en => fifo_data_ack,
+        write_ready => open,
+        write_en => uart_rx_ready,
+        high_water_mark => fifo_nearly_full
+    );
+
+    uart_instance: entity work.uart 
+    GENERIC MAP(
+        clk_frequency => clk_frequency
+    )
+    PORT MAP(
+        clk => clk,
+        serial_out => serial_out,
+        serial_in => serial_in,
+        data_in => uart_data_in,
+        data_in_load => uart_data_load,
+        data_out => uart_data_out,
+        data_out_ready => uart_rx_ready,
+        bad_bit => uart_badbit,
+        transmitter_busy => uart_tx_busy,
+        can_transmit => can_transmit
+    );
+
+end Behavioral;