--------------------------------------------------------------------------------
-- MC6809 CPU
--------------------------------------------------------------------------------
-- DO 8/2017
--------------------------------------------------------------------------------

-- EN   : Clock enable.

-- Data bus :
--    Transfers occurs when REQ=ACK=1.
--    A,DW : Valid when REQ=ACK=1
--    DR   : Sampled one cycle after REQ=ACK=1
--    WR   : 0=Read 1=Write

-- PHZ : "00" = Instruction Fetch
--     : "01" = Data access
--     : "1x" = Internal cycle

-- Interrupts are LEVEL sensitive, positive.
-- To wake the CPU during a SYNC instruction, either an interrupt is triggered,
--  or the wake signal is enabled (no 3 cycles interrupts pulses)

-- FAST : When FAST=0, this CPU tries to execute instructions with the same
--        number of cycles as a real MC6809. When FAST=1, many instructions are
--        faster. FAST level can be changed at any time.

-- DEBUG : It is possible to halt the CPU, get a copy of all internal registers
--         modify them, then restart the CPU. It can be used for debugging, or
--         for making snapshots of the state of a system.

-- <Invalid opcodes may behave differently from a real MC6809>
-- <No support for HM6309 instructions>

--------------------------------------------------------------------------------
-- This design can be used for any purpose.

-- Please send any bug report or remark to : dev@temlib.org
--------------------------------------------------------------------------------
-- Version 201708

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;


USE std.textio.ALL;

LIBRARY work;
USE work.base_pack.ALL;
USE work.mc6809_pack.ALL;

ENTITY tb_mc6809 IS
END ENTITY tb_mc6809;

-- TFR, EXG :
--  0000 : A:B       1000 : A
--  0001 : X         1001 : B
--  0010 : Y         1010 : CCR
--  0011 : US        1011 : DPR
--  0100 : PC


ARCHITECTURE sim OF tb_mc6809 IS
  SIGNAL ram : arr_uv8(0 TO 2047);
  CONSTANT rom : arr_uv8 :=(
    x"12",x"12",x"12", -- NOP

    x"86",x"12", -- LDA #12
    x"C6",x"34", -- LDB #23
    x"8E",x"12",x"34", -- LDX #1234
    x"10",x"CE",x"01",x"10", -- LDS #0110
    x"10",x"8E",x"23",x"45", -- LDY #2345

    x"1F",x"89", -- TFR A->B
    x"CC",x"56",x"78", -- LDD #5678
    x"1F",x"14", -- TFR X->S
    x"1E",x"42", -- EXG S<->Y
    x"1E",x"98", -- EXG A<->B

    x"86",x"00", -- LDA #00
    x"C6",x"01", -- LDB #01
    x"1F",x"8A", -- TFR A -> CCR
    x"1F",x"9B", -- TFR B -> DPR
    x"20",x"02", -- BRA +2
    x"86",x"FF", -- LDA #FF : Skipped
    x"21",x"FE", -- BRN -2
    x"86",x"12", -- LDA #12
    x"10",x"24",x"00",x"02", -- LBcc +2
    x"86",x"FF", -- LDA #FF : Skipped
    x"10",x"21",x"FF",x"FE", -- BRN -2
    x"97",x"00", -- STA d>00
    x"B7",x"01",x"02", -- STA $0102
    x"D6",x"00", -- LDB d>00
    x"F6",x"01",x"02", -- LDB $0102
    x"9E",x"00", -- LDX d>00
    x"BE",x"01",x"02", -- LDX $0102
    
    x"9F",x"02", -- STX d>00
    x"BF",x"01",x"04", -- STX $0102
    
    x"86",x"08", -- LDA #08
    x"8E",x"12",x"34", -- LDX #1234
    
    x"AF",x"E4",       -- STX, S index, no offset
    x"AF",x"E8",x"02", -- STX, S index, 8bits offset
    x"AF",x"E9",x"01",x"04", -- STX, S index, 16bits offset
    x"AF",x"E6",       -- STX, S index, A reg. offset
    x"AF",x"EB",       -- STX; S index, D reg. offset
    x"AF",x"8C",x"02", -- STX, PC relative, 8bits offset
    x"AF",x"8D",x"40",x"20", -- STX, PC relative, 16bits offset
    x"A7",x"E4",       -- STA, S index, no offset
    
    x"8E",x"02",x"00", -- LDX #0200
    x"A7",x"80",       -- STA X++
    x"A7",x"81",       -- STA X++2
    x"A7",x"82",       -- STA --X
    x"A7",x"83",       -- STA 2--X
    
    x"AE",x"E4",       -- LDX, S index, no offset
    x"AE",x"E8",x"02", -- LDX, S index, 8bits offset
    x"AE",x"E9",x"01",x"04", -- LDX, S index, 16bits offset
    x"AE",x"E6",       -- LDX, S index, A reg. offset
    x"AE",x"EB",       -- LDX; S index, D reg. offset
    x"AE",x"8C",x"02", -- LDX, PC relative, 8bits offset
    x"AE",x"8D",x"40",x"20", -- LDX, PC relative, 16bits offset
    x"A6",x"E4",       -- LDA, S index, no offset
    
    x"8E",x"02",x"00", -- LDX #0200
    x"A6",x"80",       -- LDA X++
    x"A6",x"81",       -- LDA X++2
    x"A6",x"82",       -- LDA --X
    x"A6",x"83",       -- LDA 2--X

    x"31",x"E4",       -- LEAY S index, no offset
    x"31",x"E8",x"02", -- LEAY S index, 8bits offset
    x"31",x"E9",x"01",x"04", -- LEAY S index, 16bits offset
    x"31",x"E6",       -- LEAY S index, A reg. offset
    x"31",x"EB",       -- LEAY S index, D reg. offset
    x"31",x"8C",x"02", -- LEAY PC relative, 8bits offset
    x"31",x"8D",x"40",x"20", -- LEA, PC relative, 16bits offset
    
    x"AB",x"E4",       -- ADDA S index, no offset
    x"E3",x"E4",       -- ADDD S index, no offset
    x"48",             -- ASLA
    x"68",x"E4",       -- ASL  S index, no offset
    
    x"10",x"CE",x"01",x"00", -- LDS #100
    x"8E",x"02",x"00", -- LDX #0200
    x"10",x"8E",x"01",x"00", -- LDY #0100
    x"10",x"BF",x"02",x"00", -- STY $0200
    x"A6",x"94",             -- LDA |X] indirect
    x"68",x"91",             -- ASL [X++2] indirect
    
    x"A6",x"9F",x"02",x"00",  -- LDA [0x200]

    x"8D",x"02", -- BSR +2
    x"86",x"FF", -- LDA #FF : Skipped
    x"17",x"00",x"02", -- LBSR +2
    x"20",x"01",      -- BRA +2
    x"39",          -- RTS
    x"6E",x"8C",x"02", -- JMP PC,+2 (PC-relative indexed)
    x"12",x"12",
    x"AD",x"8C",x"02", -- JSR PC,+2 (PC-relative indexed)
    x"86",x"FF", -- LDA #FF : Skipped
    x"12",x"12",
    x"7E",x"C0",x"DF", -- JMP Extended
    x"86",x"FF", -- LDA #FF : Skipped
    x"16",x"00",x"02", -- LBRA relative
    x"86",x"FF",

    
    -- JMP indirect
    x"1F",x"50",  -- TFR PC->D
    x"C3",x"00",x"09", -- ADDD #10
    x"ED",x"84", -- STD X
    x"6E",x"94", -- JMP [X] indirect
    x"86",x"FF", -- LDA #FF : Skipped
    
    -- JSR indirect
    x"1F",x"50",  -- TFR PC->D
    x"C3",x"00",x"09", -- ADDD #10
    x"ED",x"84", -- STD X
    x"AD",x"94", -- JSR [X] indirect
    x"86",x"FF", -- LDA #FF : Skipped

    x"CE",x"56",x"78", -- LDU #5678

    x"34",x"FF", -- PSHS PC,U,Y,X,DP,B,A,CC
    x"34",x"55", -- PSHS    U,  X,   B,  CC
    x"34",x"01", -- PSHS                 CC
    x"34",x"00", -- PSHS
    x"35",x"00", -- PULS
    x"35",x"01", -- PULS                 CC
    x"35",x"63", -- PULS    U,Y,      ,A,CC
    x"35",x"7F", -- PULS    U,Y,X,DP,B,A,CC

    x"1A",x"F0", -- ORCC #F0
    x"1C",x"55", -- ANDCC #55
    x"1A",x"0F", -- ORCC #0F
    x"1C",x"AA", -- ANDCC #AA

    x"CC",x"05",x"0A", -- LDD #050A
    x"3D",             -- MUL
    x"3A",             -- ABX
    x"1D",             -- SEX
    x"50",             -- NEGB
    x"1D",             -- SEX

    x"95",x"00",        -- BITA d>10
    x"91",x"00",        -- CMPA d>10
    x"0D",x"00",        -- TST  d>10

    x"86",x"3B", -- LDA #3B = RTI
    x"B7",x"00",x"80", -- STA $0080
    x"CC",x"00",x"80", -- LDD #0080
    x"FD",x"FF",x"FC", -- STD $FFFC = NMI VEC
    x"FD",x"FF",x"F8", -- STD $FFF8 = IRQ VEC
    x"FD",x"FF",x"F6", -- STD $FFF6 = FIRQ VEC
    x"FD",x"FF",x"FA", -- STD $FFFA = SWI VEC
    x"FD",x"FF",x"F4", -- STD $FFF4 = SWI2 VEC
    x"FD",x"FF",x"F2", -- STD $FFF2 = SWI3 VEC
    
    x"86",x"60",       -- LDA #60 : Delay !
    
    -- NMI, CWAI
    x"B7",x"80",x"00", -- STA $8000 : NMI
    x"3C",x"00", -- CWAI
    
    -- IRQ, SYNC
    x"B7",x"80",x"01", -- STA $8001 : IRQ
    x"13", -- SYNC
    x"86",x"22", -- LDA #22 : Final
    
    -- FIRQ, SYNC
    x"B7",x"80",x"02", -- STA $8002 : FIRQ
    x"13", -- SYNC
    x"86",x"33", -- LDA #33 : Final

    x"86",x"10",       -- LDA #60 : Delay !
    x"B7",x"80",x"02", -- STA $8002 : FIRQ
    x"12",x"12",x"12",x"12",x"12",
    x"12",x"12",x"12",x"12",x"12",
    x"12",x"12",x"12",x"12",x"12",
    x"86",x"44", -- LDA #44 : Final
    
    -- SWI, SWI2,SWI3
    x"3F", -- SWI
    x"10",x"3F", -- SWI2
    x"11",x"3F", -- SWI3
    
    x"12",
    x"86",x"11", -- LDA #11 : Final
    
    -- <JMP extended>
    x"7E",x"C1",x"72",
    x"12",
    -- <JSR extended>
    x"BD",x"C1",x"76", -- JMP extended
    x"12", -- NOP
    -- <JMP direct>
    x"86",x"C1", -- LDA #C1
    x"1F",x"8B", -- TFR A->DP
    x"0E",x"7C", -- JMP direct

    -- <JSR direct>
    x"9D",x"7F", -- JSR direct
    x"12",
    -- <STB -1,S : 5bits immediate> Timings
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"7F", -- STB -1,S
    x"E7",x"E4", -- STB S
    x"E7",x"E4", -- STB S
    x"E7",x"E4", -- STB S
    x"E7",x"E4", -- STB S
    x"E7",x"E4", -- STB S
    
    x"12",x"12",x"12",x"12",x"12",
    x"12",x"12",x"12",x"12",x"12",
    x"12",x"12",x"12",x"12",x"12",
    
    
    x"20",x"FE",       -- BRA -2
    x"12",x"12");

  
  -- INC / DEC
  --  LD / ST 8 et 16bits
  -- ALUOP 8 & 16bits
  -- ALUOP imm
  -- aLUOP direct,extednded 
  -- ALUOP indexed
  -- aLUOP indexed, indirect
  -- 
  SIGNAL ram_vec : arr_uv8(0 TO 15) :=(
    x"C0",x"00", -- FFF0 : Reserved
    x"C0",x"00", -- FFF2 : SWI3
    x"C0",x"00", -- FFF4 : SWI2
    x"C0",x"00", -- FFF6 : FIRQ
    x"C0",x"00", -- FFF8 : IRQ
    x"C0",x"00", -- FFFA : SWI
    x"C0",x"00", -- FFFC : NMI
    x"C0",x"00");-- FFFE : RESET
  
  
  COMPONENT mc6809 IS
    PORT (
      ad       : OUT uv16;
      dw       : OUT uv8;
      dr       : IN  uv8;
      req      : OUT std_logic;
      ack      : IN  std_logic;
      wr       : OUT std_logic;
      ph       : OUT uv3;
      irq      : IN  std_logic;
      firq     : IN  std_logic;
      nmi      : IN  std_logic;
      reset    : IN  std_logic;
      wake     : IN  std_logic;
      fast     : IN  std_logic;
      clk      : IN  std_logic;
      reset_na : IN  std_logic);
  END COMPONENT mc6809;


  SIGNAL ad       : uv16;
  SIGNAL dw       : uv8;
  SIGNAL dr       : uv8;
  SIGNAL req      : std_logic;
  SIGNAL ack      : std_logic;
  SIGNAL wr       : std_logic;
  SIGNAL ph       : uv3;
  SIGNAL reset         : std_logic ;
  SIGNAL wake,wake_pre : std_logic :='0';
  SIGNAL irq,irq_pre   : std_logic :='0';
  SIGNAL firq,firq_pre : std_logic :='0';
  SIGNAL nmi,nmi_pre   : std_logic :='0';
  SIGNAL fast     : std_logic;
  SIGNAL clk      : std_logic :='0';
  SIGNAL reset_na : std_logic;

  SIGNAL tick     : std_logic;
  SIGNAL delay    : uv8 :=x"00";
  
BEGIN

  reset_na<='0','1' AFTER 10 us;
  clk<=NOT clk AFTER 500 ns;
  reset<='0','1' AFTER 15 us, '0' AFTER 20 us;
  
  i_mc6809: mc6809
    PORT MAP (
      ad       => ad,
      dw       => dw,
      dr       => dr,
      req      => req,
      ack      => ack,
      wr       => wr,
      ph       => ph,
      irq      => irq,
      firq     => firq,
      nmi      => nmi,
      reset    => reset,
      wake     => wake,
      fast     => fast,
      clk      => clk,
      reset_na => reset_na);

  mem:PROCESS(clk) IS
  BEGIN
    IF rising_edge(clk) THEN
      -- RAM
      IF ad<x"1000" THEN
        dr<=ram(to_integer(ad));
        IF wr='1' AND ack='1' THEN
          ram(to_integer(ad))<=dw;
        END IF;
      END IF;
      -- VECTORS
      IF ad>=x"FFF0" THEN
        dr<=ram_vec(to_integer(ad-x"FFF0"));
        IF wr='1' AND ack='1' THEN
          ram_vec(to_integer(ad-x"FFF0"))<=dw;
        END IF;
      END IF;
      -- ROM
      IF ad>=x"C000" AND ad<=x"CFFF" THEN
        dr<=rom(to_integer(ad-x"C000"));
        IF wr='1' AND ack='1' THEN
          REPORT "WRITE TO ROM ! : AD=" & to_hstring(ad) & " DW=" &
            to_hstring(dw) SEVERITY error;
        END IF;
      END IF;
      
      -- Interrupt generation
      
      IF delay>x"00" THEN
        delay<=delay-1;
      ELSE
        nmi<=nmi_pre;
        irq<=irq_pre;
        firq<=firq_pre;
        wake<=wake_pre;
        nmi_pre<='0';
        irq_pre<='0';
        firq_pre<='0';
        wake_pre<='0';
      END IF;
      IF ad=x"8000" AND wr='1' AND ack='1' THEN
        nmi_pre<='1';
        delay<=dw;
      ELSIF ad=x"8001" AND wr='1' AND ack='1' THEN
        irq_pre<='1';
        delay<=dw;
      ELSIF ad=x"8002" AND wr='1' AND ack='1' THEN
        firq_pre<='1';
        delay<=dw;
      ELSIF ad=x"8003" AND wr='1' AND ack='1' THEN
        wake_pre<='1';  
        delay<=dw;
      END IF;
      
     END IF;
  END PROCESS mem;

  TickGen:PROCESS IS
  BEGIN
    tick<='0';
    wure(clk,4);
    tick<='1';
    wure(clk);
  END PROCESS TickGen;

  ack<=tick;
  

  fast<='0';
  
END ARCHITECTURE sim;

