--APPENDIX D.  Behavioral VHDL Code for M6805 CPU

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.CONV_STD_LOGIC_VECTOR;
use std.textio.all;
entity M6805 is
port(clk: in std_logic;
	rst_b:	in std_logic;  			-- active low reset signal
	IRQ, SCint: in std_logic);		-- hardware interrupt signals
end M6805;

architecture behv of M6805 is
  type RAMtype is array (0 to 8191) of std_logic_vector(7 downto 0);
	signal mem: RAMtype:= (others=>(others=> '0'));
	signal memory : std_logic_vector(7 downto 0);
	signal Opcode,A,X,Md: std_logic_vector(7 downto 0) := (others => '0');
	alias OP: std_logic_vector(3 downto 0) is Opcode(3 downto 0);
	alias mode: std_logic_vector(3 downto 0) is Opcode(7 downto 4);
  type state_type is (reset, fetch, addr1, addr2, addX, data, rd_mod_wr, 
		writeback, testBR, push1, push2, push3, push4, push5, cycle8, 
		cycle9, cycle10, pop5, pop4, pop3, pop2, pop1);
  signal ST : state_type;
  signal CCR: std_logic_vector(3 downto 0);  	-- H not implemented
  alias I: std_logic is CCR(3);  alias N : std_logic is CCR(2);
	alias Z : std_logic is CCR(1);  alias C : std_logic is CCR(0);
	signal PC, MAR: std_logic_vector (12 downto 0);
	alias PCH : std_logic_vector(4 downto 0) is PC(12 downto 8);
	alias PCL : std_logic_vector(7 downto 0) is PC(7 downto 0);
	alias MARH : std_logic_vector(4 downto 0) is MAR(12 downto 8);
	alias MARL : std_logic_vector(7 downto 0) is MAR(7 downto 0);
	signal SP: std_logic_vector(5 downto 0);
	constant zero: std_logic_vector(4 downto 0) := "00000";

	-- lower 4 bytes of opcode
	subtype ot is std_logic_vector(3 downto 0);		
	constant SUB: ot:="0000"; constant CMP: ot:="0001"; 
	constant SBC: ot:="0010"; constant CPX: ot:="0011"; 
	constant ANDa: ot:="0100"; constant BITa: ot:="0101";
 	constant LDA: ot:="0110"; constant STA: ot:="0111"; 
	constant EOR: ot:="1000"; constant ADC: ot:="1001"; 
	constant ORA: ot:="1010"; constant ADD: ot:="1011";
	constant JMP: ot:="1100"; constant JSR: ot:= "1101"; 
	constant LDX: ot:="1110"; constant STX: ot:="1111";
	constant RTI: ot:="0000"; constant RTS: ot:="0001";
	constant SWI: ot:="0011"; constant TAX: ot:="0111";
	constant CLC: ot:="1000"; constant SEC: ot:="1001";
	constant CLI: ot:="1010"; constant SEI: ot:="1011";
	constant RSP: ot:="1100"; constant NOP: ot:="1101"; 
	constant TXA: ot:="1111"; constant NEG: ot:="0000";
	constant COM: ot:="0011"; constant LSR: ot:="0100"; 
	constant RORx: ot:="0110"; constant ASR: ot:="0111";
	constant LSL: ot:="1000"; constant ROLx: ot:="1001"; 
	constant DEC: ot:="1010"; constant INC: ot:="1100";
	constant TST: ot:="1101"; constant CLR: ot:="1111"; 
	constant BRA: ot:="0000"; constant BRN: ot:="0001";
	constant BHI: ot:="0010"; constant BLS: ot:="0011"; 
	constant BCC: ot:="0100"; constant BCS: ot:="0101";
	constant BNE: ot:="0110"; constant BEQ: ot:="0111"; 
	constant BPL: ot:="1010"; constant BMI: ot:="1011";
	constant BMC: ot:="1100"; constant BMS: ot:="1101"; 
	constant BIL: ot:="1110"; constant BIH: ot:="1111";
  
   
	-- upper 4 bytes of opcode
	constant REL: ot:="0010"; constant DIRM: ot:="0011"; 
	constant INHA: ot:="0100"; constant INHX: ot:="0101";
	constant IX1M: ot:="0110"; constant IXM: ot:="0111"; 
	constant INH1: ot:="1000"; constant INH2: ot:="1001";
	constant IMM: ot:="1010"; constant DIR: ot:="1011"; 
	constant EXT: ot:="1100"; constant IX2: ot:="1101";
	constant IX1: ot:="1110"; constant IX: ot:="1111";
	-- define interrupt vector addresses
--	constant SWI_VEC : integer := 8188;	-- 0x1FFC and 0x1FFd
--	constant IRQ_VEC : integer := 8186;	-- 0x1FFA and 0x1FFB
--	constant SCI_VEC : integer := 8182;	-- 0x1FF6 and 0x1FF7

	
procedure ALU_OP				-- perform ALU operation
	(Md : in std_logic_vector(7 downto 0);
	 signal A, X : inout std_logic_vector(7 downto 0);
	 signal N, Z, C : inout std_logic) is 
variable res : std_logic_vector(8 downto 0);	-- result of ALU operation
variable updateNZ : Boolean := TRUE;		-- update NZ flags by default
begin
   case OP is
   	when LDA => res := '0'&Md; A <= res(7 downto 0);	
   	when LDX => res := '0'&Md;  X <= res(7 downto 0);
   	when ADD => res := ('0'&A) + ('0'&Md);
       C <= res(8); A <= res(7 downto 0);
   	when ADC =>  res:=  ('0'&A) + ('0'&Md) + C;
        C <= res(8); A <= res(7 downto 0);
   	when SUB =>  res:= ('0'&A) - ('0'&Md);
       C <= res(8); A <= res(7 downto 0);
   	when SBC =>  res:= ('0'&A) - ('0'&Md) - C;
       C <= res(8); A <= res(7 downto 0);
   	when CMP =>  res:= ('0'&A) - ('0'&Md); C <= res(8);
   	when CPX =>   res:= ('0'&X) - ('0'&Md);  C <= res(8);
   	when ANDa => res :=  '0'&(A and Md) ; A <= res(7 downto 0);
   	when BITa => res := '0'&(A and Md); 
   	when ORA => res := '0'&(A or Md);  A <= res(7 downto 0);
   	when EOR => res := '0'&(A xor Md); A <= res(7 downto 0);
   	when others => updateNZ := FALSE;
   end case;
   if updateNZ then N <= res(7);  
     if res(7 downto 0) = "00000000" then Z <= '1'; else Z <= '0'; end if;
   end if;
end ALU_OP;

Procedure ALU1 			-- perform operation on single operand
	(signal op1: inout std_logic_vector(7 downto 0);
	 signal N, Z, C : inout std_logic) is	
variable res9 : std_logic_vector(8 downto 0);
variable res8 : std_logic_vector(7 downto 0);
begin
case OP is
   	when NEG => res9 := not('0'&op1) + 1; 
       C <= res9(8);  res8 := res9(7 downto 0);
  	when COM => res8 := not op1;  C <= '1';
		when LSR => res8 := '0'&op1(7 downto 1); C <= op1(0);
  	when RORx => res8 := C&op1(7 downto 1);  C <= op1(0);
  	when ASR => res8 := op1(7)&op1(7 downto 1); C <= op1(0); 
  	when LSL =>  res8:= op1(6 downto 0)&'0'; C <= op1(7);
  	when ROLx => res8 := op1(6 downto 0)&C; C <= op1(7);
  	when DEC => res8 := op1 - 1;  
  	when INC => res8 := op1 + 1; 
  	when CLR => res8 := "00000000";
  	when TST => res8 := op1;  
  	when others => assert (false) report "illegal opcode";
end case;
op1 <= res8;  N <= res8(7);
if (res8 = "00000000") then Z <= '1'; else Z <= '0'; end if;
end ALU1;

Procedure fill_memory(signal mem: inout RAMType)  is
   type HexTable is array(character range <>) of integer;
-- valid hex chars: 0, 1, ... A, B, C, D, E, F (upper-case only)
constant lookup : HexTable('0' to 'F'):=
	(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1,
	 -1, -1, -1, -1, 10, 11, 12, 13, 14, 15);
file infile: text  open  read_mode is "mem1.txt";	-- open file for reading
-- file infile: text is in "mem1.txt";	-- VHDL '87 version
variable buff: line;
variable addr_s: string(4 downto 1);
variable data_s : string(3 downto 1); -- data_s(1) has a space
variable addr1, byte_cnt: integer;
variable data: integer range 255 downto 0;
begin
	while (not endfile(infile)) loop
		readline (infile, buff);
		read (buff, addr_s);	-- read addr hexnum
		read(buff, byte_cnt);	-- read number of bytes to read
		addr1 := lookup(addr_s(4))*4096 + lookup(addr_s(3))*256
			+ lookup(addr_s(2))*16 + lookup(addr_s(1));
		readline (infile, buff);
		for i in 1 to byte_cnt loop
			read (buff, data_s);  -- read 2 digit hex data and a space
			data:= lookup(data_s(3))*16 + lookup(data_s(2));
			mem(addr1) <= CONV_STD_LOGIC_VECTOR(data, 8);
			addr1:= addr1 + 1;
		end loop;
	end loop;
end fill_memory;

begin
cpu_cycles: process
   variable reg_mem, hw_interrupt, BR: Boolean;
   variable sign_ext: std_logic_vector(4 downto 0);

begin
reg_mem:= (mode = imm) or (mode = dir) or (mode = ext) or (mode = ix) or 
         (mode = ix1) or (mode = ix2);
hw_interrupt := (I = '0') and (IRQ = '1' or SCint = '1');

wait until rising_edge(CLK);
if (rst_b = '0') then ST <= reset; fill_memory(mem);
else 
case ST is
when reset => SP <= "111111";
	if (rst_b = '1') then ST <= cycle8; end if;
when fetch => 
  if reg_mem then ALU_OP(Md, A, X, N, Z, C); end if;	
		-- complete previous operation
  if hw_interrupt then ST <= push1;
      else  Opcode <= mem(CONV_INTEGER(PC)); PC <= PC+1;  -- fetch opcode
      ST <= addr1; end if;

when addr1 =>
   case mode is
			when inha => ALU1(A, N, Z, C);  ST <= fetch;	-- do operation on A
			when inhx => ALU1(X, N, Z, C);   ST <= fetch;	-- do operation on X
  		when imm => Md <= mem(CONV_INTEGER(PC));	-- get immediate data
         PC <= PC+1; ST <= fetch;
     	when inh1 =>  
         if OP = SWI then ST <= push1;
           elsif OP = RTS then ST<= pop2; SP <= SP+1;
           elsif OP = RTI then ST <= pop5; SP <= SP+1;
         end if;
    	when inh2 =>
         case OP is
            when TAX =>  X <= A;
            when CLC =>  C <= '0';
            when SEC => C <= '1';
            when CLI =>  I <= '0';
            when SEI => I <= '1';
            when RSP => SP <= "111111";
            when TXA => A <= X;
            when others => assert (false) 
							report "illegal opcode, mode = inh2";
         end case;
         ST <= fetch;
    	when dir =>
          if OP = JMP then PC <= zero&mem(CONV_INTEGER(PC)); ST <= fetch;
          else MAR <= zero&mem(CONV_INTEGER(PC)); PC <=PC+1;	
						-- get direct address
          		if (OP=JSR) then  ST <= push1; else ST <= data; end if;
          end if;
  		when dirM =>  MAR <= zero&mem(CONV_INTEGER(PC));  PC <= PC+1;
            ST <= data;
    	when ix =>
          if OP = JMP then PC <= zero&X; ST <= fetch;
          else MAR <= zero&X;			
               if (OP=JSR) then  ST <= push1; else ST <= data; end if;
          end if;
       when ixm =>  MAR <= zero&X;  ST <= data; 
       when ext | ix2 =>  MARH <= mem(CONV_INTEGER(PC))(4 downto 0);	
					-- get high byte
          PC <= PC+1; ST <= addr2;
  		when ix1 | ix1m =>  MAR <= zero&mem(CONV_INTEGER(PC));  -- get offset
          PC <= PC+1;  ST <= addX; 
   		when rel =>  Md <= mem(CONV_INTEGER(PC));		   -- get offset
           PC <= PC+1; ST <= testBR;
       when others => ST <= fetch; 
					assert(false) report "address mode not implemented";
   end case;

when addr2 =>
	if (mode = ix2) then MARL <= mem(CONV_INTEGER(PC)); PC <= PC+1;  
				-- get low byte
		ST <= addX;
	elsif OP=JMP then PC <= MARH&mem(CONV_INTEGER(PC)); ST <= fetch;
	else MARL <= mem(CONV_INTEGER(PC)); PC <= PC+1;  -- get low byte
		if OP=JSR then ST <= push1; else ST <= data; end if;
	end if;
       
when addX =>  if (OP=JMP and reg_mem) then PC <= MAR + (zero&X); ST <= fetch; 
   else MAR <= MAR + (zero&X);
       if (OP=JSR and reg_mem) then ST <= push1; else ST <= data; end if;
   end if;

when data =>
     if OP = STA then mem(CONV_INTEGER(MAR)) <= A;  N <= A(7); 
        if (A = "00000000") then Z <= '1'; else Z <= '0'; end if;
     elsif OP = STX then mem(CONV_INTEGER(MAR)) <= X; N <= X(7);
        if X = "00000000" then Z <= '1'; else Z <= '0'; end if;
     else Md <= mem(CONV_INTEGER(MAR));
     end if;
     if ((mode = dirM) or (mode = ixm) or (mode = ix1m)) then
			ST <= rd_mod_wr; else ST <= fetch; end if;

when rd_mod_wr =>  ALU1(Md, N, Z, C); ST <= writeback;
when writeback => mem(CONV_INTEGER(MAR)) <= Md; ST <= fetch;

when testBR =>  
  case OP is
     when BRA => BR := TRUE;
     when BRN => BR := FALSE;
     when BHI => BR := (C or Z) = '0';
     when BLS => BR := (C or Z) = '1';
     when BCC => BR := C = '0';
     when BCS => BR := C = '1';
     when BNE => BR := Z = '0';
     when BEQ => BR := Z = '1';
     when BPL => BR := N = '0';
     when BMI => BR := N = '1';
     when BMC => BR := I = '0';
     when BMS => BR := I = '1';
     when others => assert(false) report "illegal branch instruction";
   	end case;
  if Md(7) = '1' then sign_ext := "11111"; else sign_ext := zero; end if;
  if BR then PC <= PC + (sign_ext&Md); end if;
  ST <= fetch;

  when push1 =>  mem(CONV_INTEGER("0000011"&SP)) <= PCL;	-- push LO byte
        SP <= SP - 1;  ST <= push2;
  when push2 => mem(CONV_INTEGER("0000011"&SP)) <="000"&PCH;  -- push HI byte
        SP <= SP - 1; 
        if (hw_interrupt or OP = SWI) then ST <= push3; 
            else PC <= MAR;  ST <= fetch; end if;			-- JSR
  when push3 =>  mem(CONV_INTEGER("0000011"&SP)) <= X;		-- push X
        SP <= SP - 1;  ST <= push4;
  when push4 =>  mem(CONV_INTEGER("0000011"&SP)) <= A;		-- push A
  	    SP <= SP - 1;  ST <= push5;
  when push5 =>  mem(CONV_INTEGER("0000011"&SP)) <= "0000"&CCR;	-- push CCR
        SP <= SP - 1;  ST <= cycle8;
  when cycle8 =>  I <= '1'; ST <= cycle9;
			if OP = SWI then MAR <= "1111111111100";-- get interrupt vector addr.
			elsif IRQ = '1' then MAR <= "1111111111010";
			elsif SCint = '1' then MAR <= "1111111110110";
			else MAR <= "1111111111111";			-- reset vector addr.
			end if;
  when cycle9 => PCH <= mem(CONV_INTEGER(MAR))(4 downto 0); -- get high byte
			MAR <= MAR + 1; ST <= cycle10;
  when cycle10 => PCL <= mem(CONV_INTEGER(MAR)); ST <= fetch; -- get low byte
  when pop5 => CCR <= mem(CONV_INTEGER("0000011"&SP))(3 downto 0);-- pop CCR
        SP <= SP + 1;  ST <= pop4;
  when pop4 => A <= mem(CONV_INTEGER("0000011"&SP));		-- pop A
        SP <= SP + 1;  ST <= pop3;
  when pop3 => X <= mem(CONV_INTEGER("0000011"&SP));		-- pop X
       SP <= SP + 1;  ST <= pop2;
  when pop2 => 
         PCH <= mem(CONV_INTEGER("0000011"&SP))(4 downto 0);   -- pop HI byte
      	SP <= SP + 1;  ST <= pop1;
  when pop1 => PCL <= mem(CONV_INTEGER("0000011"&SP));	    -- pop LO byte
        ST <= fetch;
  when others => null;
end case;
end if;   -- if (rst_b = '1')
end process;
end behv;

