--------------------------------------------------------------------------------
-- VECTREX
--------------------------------------------------------------------------------
-- IOs :
-- - R6522 VIA
-- - AY-3-8912 Sound generator
-- - MC1408 DAC + Analog muxes + integrators
--------------------------------------------------------------------------------
-- DO 8/2017
--------------------------------------------------------------------------------

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

USE std.textio.ALL;

LIBRARY work;
USE work.base_pack.ALL;

ENTITY vecio IS
  PORT (
    --------------------------
    ad       : IN  uv16;
    dw       : IN  uv8;
    dr       : OUT uv8;
    req      : IN  std_logic;
    ack      : OUT std_logic;
    wr       : IN  std_logic;

    irq      : OUT std_logic; -- IO  VIA6522
    firq     : OUT std_logic; -- 
    --------------------------
    spot_x   : OUT uv16;
    spot_y   : OUT uv16;
    spot_z   : OUT uv8;
    spot_en  : OUT std_logic;

    dac_x    : OUT uv24;
    dac_y    : OUT uv24;

    rotate   : IN  std_logic;
    --------------------------
    sound    : OUT signed(15 DOWNTO 0);
    
    --------------------------
    left_udlr  : IN uv4; -- Up/Down/Left/Right
    left_x     : IN uv8;
    left_y     : IN uv8;
    right_udlr : IN uv4; -- Up/Down/Left/Right
    right_x    : IN uv8;
    right_y    : IN uv8;
    
    --------------------------
    tick_cpu : IN std_logic; -- 1.5MHz
    tick_vec : IN std_logic;
    
    --------------------------
    clk      : IN  std_logic;
    reset_na : IN std_logic
    );
END ENTITY vecio;


ARCHITECTURE rtl OF vecio IS

  SIGNAL adlo : uv4;
  SIGNAL wreq : std_logic;

  ------------------------------------------------
  SIGNAL via_ddra,via_ddrb,via_oa,via_ob,via_ia,via_ib : uv8;
  
  ------------------------------------------------
  SIGNAL via_t1_latch,via_t2_latch,via_t1_cnt,via_t2_cnt : uv16;
  SIGNAL via_t1_en,via_t2_en : std_logic;
  SIGNAL via_t1_ien,via_t2_ien : std_logic;
  SIGNAL via_t1_int,via_t1_int_clr,via_t1_int_set : std_logic;
  SIGNAL via_t2_int,via_t2_int_clr,via_t2_int_set : std_logic;
  SIGNAL via_t1_ctrl : uv2;
  SIGNAL via_t2_ctrl : std_logic;
  
  SIGNAL via_shift : uv8;
  SIGNAL via_shift_ctrl : uv3;
  SIGNAL via_shift_ien : std_logic;
  SIGNAL via_shift_int,via_shift_int_clr,via_shift_int_set : std_logic;
  SIGNAL via_shift_out : std_logic;
  SIGNAL via_shift_en  : std_logic;
  SIGNAL via_shift_count : uv3;
  
  SIGNAL via_ca1_ien,via_ca2_ien : std_logic;
  SIGNAL via_cb1_ien,via_cb2_ien : std_logic;
  SIGNAL via_latch_a,via_latch_b : std_logic;
  SIGNAL via_ca1_i  : std_logic;
  SIGNAL via_ca2_i,via_ca2_o : std_logic;
  SIGNAL via_cb1_i,via_cb1_o : std_logic;
  SIGNAL via_cb2_i,via_cb2_o : std_logic;
  SIGNAL via_ca2_ctrl,via_cb2_ctrl : uv3;
  SIGNAL via_ca1_ctrl,via_cb1_ctrl : std_logic;
  SIGNAL via_cb1_int,via_cb2_int,via_ca1_int,via_ca2_int : std_logic;
  
  SIGNAL via_irq,via_irq_pre : std_logic;
  
  ------------------------------------------------
  SIGNAL snd_bc1,snd_bdir : std_logic;
  SIGNAL snd_wreg : std_logic;
  SIGNAL snd_dw,snd_dr : uv8;
  SIGNAL snd_reg : uv4;
  SIGNAL snd_cpt8 : uv3;
  SIGNAL snd_cpt128 : uv7;
  SIGNAL snd_tone_a_per,snd_tone_b_per,snd_tone_c_per : uv12;
  SIGNAL snd_tone_a_cpt,snd_tone_b_cpt,snd_tone_c_cpt : uv12;
  SIGNAL snd_tone_a_amp,snd_tone_b_amp,snd_tone_c_amp : uv4;
  SIGNAL snd_tone_a_out,snd_tone_b_out,snd_tone_c_out : std_logic;
  SIGNAL snd_tone_a_en ,snd_tone_b_en ,snd_tone_c_en  : std_logic;
  SIGNAL snd_tone_a_m  ,snd_tone_b_m  ,snd_tone_c_m   : std_logic;
  
  SIGNAL snd_env_per,snd_env_cpt : uv16;
  SIGNAL snd_env_shape : uv4;
  SIGNAL snd_env_seq : uv5;
  SIGNAL snd_env_amp : uv4;
  
  SIGNAL snd_noise_a_en,snd_noise_b_en,snd_noise_c_en : std_logic;
  SIGNAL snd_noise_per,snd_noise_cpt : uv5;
  SIGNAL snd_noise_lfsr : uv32;
  SIGNAL snd_noise_out  : std_logic;
  
  SIGNAL snd_env_sus : std_logic;
  
  SIGNAL snd_io_a_en,snd_io_b_en : std_logic;
  SIGNAL snd_io_oa,snd_io_ob,snd_io_ia,snd_io_ib : uv8;

  SIGNAL xxx_lev_a,xxx_lev_b,xxx_lev_c : signed(4 DOWNTO 0);
  SIGNAL xxx_sono : integer RANGE -100000 TO 100000;
  
  ------------------------------------------------
  SIGNAL right_right : std_logic;
  
  ------------------------------------------------
  SIGNAL dac_mux : uv2;
  SIGNAL dac_sh,dac_blank,dac_ramp,dac_zero : std_logic;
  SIGNAL dac_lev : uv8;

  SIGNAL dac_lev_zero,dac_z,dac_sh_x,dac_sh_y,snd_dac : uv8;
  SIGNAL dac_x24,dac_y24 : uv(23 DOWNTO 0);
  SIGNAL xsca,ysca : signed(25 DOWNTO 0);
  SIGNAL scale : uv8 :=x"05";
  
  CONSTANT SND_LOG : arr_uv8(0 TO 15):=(
    x"13",x"17",x"1B",x"20",x"26",x"2D",x"36",x"40",
    x"4C",x"5A",x"6B",x"80",x"98",x"B4",x"D6",x"FF");

  SIGNAL pot : uv8;
  
  ------------------------------------------------
    
BEGIN

  adlo<=ad(3 DOWNTO 0); -- xD000 ... xD00F
  wreq <=wr AND req;

  ack<=req;

  via_cb1_int<='0';
  via_cb2_int<='0';
  via_ca1_int<='0';
  via_ca2_int<='0';
  
  ----------------------------------------------------------
  VIA:PROCESS(clk,reset_na) IS
  BEGIN
    IF reset_na='0' THEN
      via_t1_en<='0';
      via_t2_en<='0';
      
      via_t1_int<='0';
      via_t2_int<='0';
      --via_cb1_int<='0';
      --via_cb2_int<='0';
      via_shift_int<='0';
      --via_ca1_int<='0';
      --via_ca2_int<='0';
      
      via_t1_ien<='0';
      via_t2_ien<='0';
      via_cb1_ien<='0';
      via_cb2_ien<='0';
      via_shift_ien<='0';
      via_ca1_ien<='0';
      via_ca2_ien<='0';
      
      via_t1_int_clr<='1';
      via_t2_int_clr<='1';
      via_shift_int_clr<='1';

      via_ddra<=x"00";
      via_ddrb<=x"00";

      via_shift_en<='0';
      
    ELSIF rising_edge(clk) THEN
      
      via_t1_int_clr<='0';
      via_t2_int_clr<='0';
      via_shift_int_clr<='0';
      
      ------------------------------------------------------
      CASE adlo IS
        WHEN x"0" => -- Port B data
          IF wreq='1' THEN
            via_ob<=dw;
          END IF;
          dr<=(via_ddrb AND via_ob) OR (NOT via_ddrb AND via_ib);
          
        WHEN x"1" => -- Port A data
          IF wreq='1' THEN
            via_oa<=dw;
          END IF;
          dr<=(via_ddra AND via_oa) OR (NOT via_ddra AND via_ia);
          
        WHEN x"2" => -- Port B direction
          IF wreq='1' THEN
            via_ddrb<=dw;
          END IF;
          dr<=via_ddrb;
            
        WHEN x"3" => -- Port A direction
          IF wreq='1' THEN
            via_ddra<=dw;
          END IF;
          dr<=via_ddra;
          
        WHEN x"4" => -- Timer 1 : Low Counter
          IF wreq='1' THEN
            via_t1_latch(7 DOWNTO 0)<=dw;
          END IF;
          dr<=via_t1_cnt(7 DOWNTO 0);
          via_t1_int_clr<=req AND NOT wr;
          
        WHEN x"5" => -- Timer 1 : High Counter
          IF wreq='1' THEN
            via_t1_latch(15 DOWNTO 8)<=dw;
            via_t1_cnt(15 DOWNTO 8)<=dw;
            via_t1_en<='1';
            via_t1_cnt(7 DOWNTO 0)<=via_t1_latch(7 DOWNTO 0);
          END IF;
          dr<=via_t1_cnt(15 DOWNTO 8);
          via_t1_int_clr<=req AND wr;
          
        WHEN x"6" => -- Timer 1 : Low Latch
          IF wreq='1' THEN
            via_t1_latch(7 DOWNTO 0)<=dw;
          END IF;
          dr<=via_t1_latch(7 DOWNTO 0);
          via_t1_int_clr<=req AND NOT wr;
          
        WHEN x"7" => -- Timer 1 : High Latch
          IF wreq='1' THEN
            via_t1_latch(15 DOWNTO 8)<=dw;
          END IF;
          dr<=via_t1_latch(15 DOWNTO 8);
          via_t1_int_clr<=req AND wr;
          
        WHEN x"8" => -- Timer 2 : Low Latch/Counter
          IF wreq='1' THEN
            via_t2_latch(7 DOWNTO 0)<=dw;
          END IF;
          dr<=via_t2_cnt(7 DOWNTO 0);
          via_t2_int_clr<=req AND NOT wr;
          
        WHEN x"9" => -- Timer 2 :High Latch/Counter
          IF wreq='1' THEN
            via_t2_latch(15 DOWNTO 8)<=dw;
            via_t2_cnt(15 DOWNTO 8)<=dw;
            via_t2_cnt(7 DOWNTO 0)<=via_t2_latch(7 DOWNTO 0);
            via_t2_en<='1';
          END IF;
          dr<=via_t2_cnt(15 DOWNTO 8);
          via_t2_int_clr<=req AND wr;
          
        WHEN x"A" => -- Shift register
          IF wreq='1' THEN
            via_shift<=dw;
            via_shift_en<='1';
          END IF;
          dr<=via_shift;
          via_shift_int_clr<='1';

        WHEN x"B" => -- Aux. control reg.
          -- Timers / Shift register control
          IF wreq='1' THEN
            via_t1_ctrl<=dw(7 DOWNTO 6);
            via_t2_ctrl<=dw(5);
            via_shift_ctrl<=dw(4 DOWNTO 2);
            via_latch_b<=dw(1);
            via_latch_a<=dw(0);
          END IF;
          dr<=via_t1_ctrl & via_t2_ctrl & via_shift_ctrl &
              via_latch_b & via_latch_a;

        WHEN x"C" => -- Peripheral control reg.
          IF wreq='1' THEN
            via_cb2_ctrl<=dw(7 DOWNTO 5);
            via_cb1_ctrl<=dw(4);
            via_ca2_ctrl<=dw(3 DOWNTO 1);
            via_ca1_ctrl<=dw(0);
          END IF;
          dr<=via_cb2_ctrl & via_cb1_ctrl & via_ca2_ctrl & via_ca1_ctrl;
          
        WHEN x"D" => -- Interrupt flag reg.
          dr<=via_irq & via_t1_int & via_t2_int & via_cb1_int & via_cb2_int &
              via_shift_int & via_ca1_int & via_ca2_int;
          
          via_t1_int_clr<=wreq AND dw(6);
          via_t2_int_clr<=wreq AND dw(5);
          via_shift_int_clr<=wreq AND dw(3);
          
        WHEN x"E" => -- Interrupt enable reg.
          IF wreq='1' THEN
            via_t1_ien  <=(via_t1_ien AND (NOT dw(6) OR dw(7))) OR
                           (dw(6) AND dw(7));
            via_t2_ien  <=(via_t1_ien AND (NOT dw(5) OR dw(7))) OR
                           (dw(5) AND dw(7));
            via_cb1_ien  <=(via_cb1_ien AND (NOT dw(4) OR dw(7))) OR
                            (dw(4) AND dw(7));
            via_cb2_ien  <=(via_cb2_ien AND (NOT dw(3) OR dw(7))) OR
                            (dw(3) AND dw(7));
            via_shift_ien<=(via_shift_ien AND (NOT dw(2) OR dw(7))) OR
                            (dw(2) AND dw(7));
            via_ca1_ien  <=(via_ca1_ien AND (NOT dw(1) OR dw(7))) OR
                            (dw(1) AND dw(7));
            via_ca2_ien  <=(via_ca1_ien AND (NOT dw(0) OR dw(7))) OR
                            (dw(0) AND dw(7));
          END IF;
          dr<='1' & via_t1_ien & via_t2_ien &
               via_cb1_ien & via_cb2_ien & via_shift_ien &
               via_ca1_ien & via_ca2_ien;
          
          
        WHEN x"F" => -- Port A data, no handshake

        WHEN OTHERS => NULL;
      END CASE;

      ----------------------------------------------
      -- VIA CA1/CA2/CB1/CB2
      
      IF via_ca1_ctrl='0' THEN -- Negative active edge
        
      ELSE -- Positive Active Edge
        
      END IF;
      
      CASE via_ca2_ctrl IS
        WHEN "000"  => -- Input negative active edge
        WHEN "001"  => -- Independant interrupt input negative edge
        WHEN "010"  => -- Input positive active edge
        WHEN "011"  => -- Independant interrupt input positive edge
        WHEN "100"  => -- Handshake output
        WHEN "101"  => -- Pulse output
        WHEN "110"  => -- Low output
          via_ca2_o<='0';
        WHEN OTHERS => -- High output
          via_ca2_o<='1';
      END CASE;
      
      IF via_cb1_ctrl='0' THEN -- Negative active edge
        
      ELSE -- Positive Active Edge
        
      END IF;
      
      CASE via_cb2_ctrl IS
        WHEN "000"  => -- Input negative active edge
        WHEN "001"  => -- Independant interrupt input negative edge
        WHEN "010"  => -- Input positive active edge
        WHEN "011"  => -- Independant interrupt input positive edge
        WHEN "100"  => -- Handshake output
        WHEN "101"  => -- Pulse output
        WHEN "110"  => -- Low output
        WHEN OTHERS => -- High output
      END CASE;
      
      ----------------------------------------------
      -- VIA TIMERS
      via_t1_int_set<='0';
      
      IF tick_cpu='1' THEN
        IF via_t1_en='1' THEN
          IF via_t1_ctrl(0)='0' THEN
            -- One shot mode
            IF via_t1_cnt/=x"0000" THEN
              via_t1_cnt<=via_t1_cnt-1;
            ELSE
              via_t1_en<='0';
              via_t1_int_set<='1';
            END IF;
          ELSE
            -- Continuous mode
            IF via_t1_cnt/=x"0000" THEN
              via_t1_cnt<=via_t1_cnt-1;
            ELSE
              via_t1_cnt<=via_t1_latch;
              via_t1_int_set<='1';
            END IF;
          END IF;
        END IF;
      END IF;

      via_t2_int_set<='0';
      IF tick_cpu='1' THEN
        IF via_t2_en='1' THEN
          IF via_t2_ctrl='0' THEN
            IF via_t2_cnt/=x"0000" THEN
              via_t2_cnt<=via_t2_cnt-1;
            ELSE
              via_t2_en<='0';
              via_t2_int_set<='1';
            END IF;
          ELSE
          -- <Count pulses : Not implemented>
          END IF;
        END IF;
        
      END IF;
      
      IF via_t1_ctrl(1)='1' THEN
        via_ob(7)<=NOT via_t1_en; -- Timer output on OB(7)
      END IF;
      
      via_t1_int<=(via_t1_int AND NOT via_t1_int_clr) OR via_t1_int_set;
      via_t2_int<=(via_t2_int AND NOT via_t2_int_clr) OR via_t2_int_set;
      ----------------------------------------------
      -- VIA Shift register

      via_shift_int_set<='0';
      
      IF tick_cpu='1' AND via_shift_en='1' THEN
        IF via_shift_ctrl="000" THEN
          via_shift_out<='0';
        ELSIF via_shift_ctrl="100" THEN -- Shift out free running at T2 rate
          IF via_t2_int_set='1' THEN
            via_shift<=via_shift(6 DOWNTO 0) & via_shift(7);
            via_shift_out<=via_shift(7);
            via_shift_count<=via_shift_count+1;
          END IF;
        ELSIF via_shift_ctrl="101" THEN -- Shift out under control of T2
          IF via_t2_int_set='1' THEN
            via_shift<=via_shift(6 DOWNTO 0) & via_shift(7);
            via_shift_out<=via_shift(7);
            via_shift_count<=via_shift_count+1;
          END IF;
        ELSIF via_shift_ctrl="110" THEN -- Shift out under control of Phy2 <!!>
          via_shift<=via_shift(6 DOWNTO 0) & via_shift(7);
          via_shift_out<=via_shift(7);
          via_shift_count<=via_shift_count+1;
        ELSIF via_shift_ctrl="111" THEN -- Shift out under control of EXTCLK
          NULL;
        END IF;

        IF via_shift_count="111" THEN
          via_shift_en<='0';
          via_shift_int_set<='1';
        END IF;
        
      END IF;
      
      IF via_shift_en='0' THEN
        via_shift_count<="000";
      END IF;
      
      IF via_shift_ctrl(2)='1' THEN
        via_cb2_o<=via_shift_out;
      END IF;

      via_shift_int<=(via_shift_int OR via_shift_int_set)
                      AND NOT via_shift_int_clr;
      
      ----------------------------------------------
      -- VIA IRQ
      via_irq_pre<=via_irq;

      irq<=via_irq AND NOT via_irq_pre; -- IRQ pulse
      ----------------------------------------------
    END IF;
  END PROCESS VIA;
  
  -- VIA_OA / IA : [7:0] : DATA to DAC & to/from sound controller
  
  -- VIA_OB [0] : OUT : Sample & Hold
  -- VIA_OB [1] : OUT : SEL0 analog mux
  -- VIA_OB [2] : OUT : SEL1 analog mux
  -- VIA_OB [3] : OUT : SOUND BC1
  -- VIA_OB [4] : OUT : Sound BDir
  -- VIA_IB [5] : IN  : COMPARE (joystick)
  -- VIA_   [6] : <unused>
  -- VIA_OB [7] : OUT : /RAMP : TIMER T1=
  
  -- VIA_CB1    : No connect
  -- VIA_CB2    : OUT : /BLANK : Relié au shift reg.
  
  -- VIA_CA1    : IN  : Left Controller RT button
  -- VIA_CA2    : OUT : /ZERO
  
  via_irq<=(via_t1_ien    AND via_t1_int)    OR (via_t2_ien AND via_t2_int) OR
           (via_cb1_ien   AND via_cb1_int)   OR (via_cb2_ien AND via_cb2_int) OR
           (via_shift_ien AND via_shift_int) OR
           (via_ca1_ien   AND via_ca1_int)   OR (via_ca2_ien AND via_ca2_int);
  
  ----------------------------------------------------------
  snd_bc1<=via_ob(3);
  snd_bdir<=via_ob(4);
  via_ia<=snd_dr;
  snd_dw<=via_oa;

  dac_mux<=via_ob(2 DOWNTO 1);
  dac_sh <=via_ob(0);
  dac_blank<=NOT via_cb2_o;
  dac_ramp <=NOT via_ob(7);
  dac_zero <=NOT via_ca2_o;
  
  dac_lev<=via_oa;
  
  ----------------------------------------------------------
  
  -- BDIR BC1  BC2=1
  -- 0    0  : Inactive
  -- 0    1  : Read from PSG
  -- 1    0  : Write to PSG
  -- 1    1  : Latch address
  snd_wreg<=snd_bdir AND NOT snd_bc1;
  
  SONO:PROCESS(clk,reset_na) IS
    ------------------------------------
    FUNCTION mixgen (gen      : std_logic;
                     noise    : std_logic;
                     amp      : uv4;       -- Fixed amplitude
                     env      : uv4;       -- Envelope amplitude
                     m        : std_logic; -- 0= Fix 1=Envelope
                     en_gen   : std_logic; 
                     en_noise : std_logic) RETURN signed IS
      VARIABLE ampli_v : uv4;
      VARIABLE sig_v   : std_logic;
      VARIABLE out_v : uv5;
    BEGIN
      sig_v:=(en_noise AND noise) XOR (en_gen AND gen);
      ampli_v:=mux(m,env,amp);
      
      IF sig_v='0' THEN
        out_v:='0' & ampli_v;
      ELSE
        out_v:= "00000" - ('0' & ampli_v);
      END IF;
      
      IF en_noise='0' AND en_gen='0' THEN
        out_v:="00000";
      END IF;
      
      RETURN signed(out_v);  
    END FUNCTION mixgen;
    
    ------------------------------------
    VARIABLE lev_v : signed(7 DOWNTO 0);
    
  BEGIN
    IF reset_na='0' THEN
      snd_tone_a_out<='0';
      snd_tone_b_out<='0';
      snd_tone_c_out<='0';

      snd_tone_a_per<=(OTHERS =>'0');
      snd_tone_b_per<=(OTHERS =>'0');
      snd_tone_c_per<=(OTHERS =>'0');
      snd_cpt8<=(OTHERS =>'0');
      snd_cpt128<=(OTHERS =>'0');

      snd_env_cpt<=(OTHERS =>'0');
      snd_env_per<=(OTHERS =>'0');
      snd_env_seq<="00000";
      
    ELSIF rising_edge(clk) THEN
      IF snd_bdir='1' AND snd_bc1='1' THEN -- Latch address
        snd_reg<=via_oa(3 DOWNTO 0);
      END IF;
      CASE snd_reg IS
        WHEN "0000" => -- Tone Generator. Channel A. Fine Tune Reg
          snd_dr<=snd_tone_a_per(7 DOWNTO 0);
          IF snd_wreg='1' THEN
            snd_tone_a_per(7 DOWNTO 0)<=snd_dw;
          END IF;
        WHEN "0001" => -- Tone Generator. Channel A. Coarse Tune Reg
          snd_dr<=x"0" & snd_tone_a_per(11 DOWNTO 8);
          IF snd_wreg='1' THEN
            snd_tone_a_per(11 DOWNTO 8)<=snd_dw(3 DOWNTO 0);
          END IF;

        WHEN "0010" => -- Tone Generator. Channel B. Fine Tune Reg
          snd_dr<=snd_tone_b_per(7 DOWNTO 0);
          IF snd_wreg='1' THEN
            snd_tone_b_per(7 DOWNTO 0)<=snd_dw;
          END IF;

        WHEN "0011" => -- Tone Generator. Channel B. Coarse Tune Reg
          snd_dr<=x"0" & snd_tone_b_per(11 DOWNTO 8);
          IF snd_wreg='1' THEN
            snd_tone_b_per(11 DOWNTO 8)<=snd_dw(3 DOWNTO 0);
          END IF;

        WHEN "0100" => -- Tone Generator. Channel C. Fine Tune Reg
          snd_dr<=snd_tone_c_per(7 DOWNTO 0);
          IF snd_wreg='1' THEN
            snd_tone_c_per(7 DOWNTO 0)<=snd_dw;
          END IF;

        WHEN "0101" => -- Tone Generator. Channel C. Coarse Tune Reg
          snd_dr<=x"0" & snd_tone_c_per(11 DOWNTO 8);
          IF snd_wreg='1' THEN
            snd_tone_c_per(11 DOWNTO 8)<=snd_dw(3 DOWNTO 0);
          END IF;

        WHEN "0110" => -- Noise Generator. Period
          snd_dr<="000" & snd_noise_per;
          IF snd_wreg='1' THEN
            snd_noise_per<=snd_dw(4 DOWNTO 0);
            snd_noise_lfsr<=x"12345678";
            snd_noise_cpt<="00000";
          END IF;
          
        WHEN "0111" => -- Mixer Control I/O Disable
          snd_dr<=NOT(snd_io_a_en & snd_io_a_en &
                       snd_noise_c_en & snd_noise_b_en & snd_noise_a_en &
                       snd_tone_c_en & snd_tone_b_en & snd_tone_a_en);
          IF snd_wreg='1' THEN
            snd_io_b_en<=NOT snd_dw(7);
            snd_io_a_en<=NOT snd_dw(6);
            snd_noise_c_en<=NOT snd_dw(5);
            snd_noise_b_en<=NOT snd_dw(4);
            snd_noise_a_en<=NOT snd_dw(3);
            snd_tone_c_en<=NOT snd_dw(2);
            snd_tone_b_en<=NOT snd_dw(1);
            snd_tone_a_en<=NOT snd_dw(0);
          END IF;

        WHEN "1000" => -- Amplitude Control A
          snd_dr<="000" & snd_tone_a_m & snd_tone_a_amp;
          IF snd_wreg='1' THEN
            snd_tone_a_amp<=snd_dw(3 DOWNTO 0);
            snd_tone_a_m  <=snd_dw(4);
          END IF;
        WHEN "1001" => -- Amplitude Control B
          snd_dr<="000" & snd_tone_b_m & snd_tone_b_amp;
          IF snd_wreg='1' THEN
            snd_tone_b_amp<=snd_dw(3 DOWNTO 0);
            snd_tone_b_m  <=snd_dw(4);
          END IF;

        WHEN "1010" => -- Amplitude Control C
          snd_dr<="000" & snd_tone_c_m & snd_tone_c_amp;
          IF snd_wreg='1' THEN
            snd_tone_c_amp<=snd_dw(3 DOWNTO 0);
            snd_tone_c_m  <=snd_dw(4);
          END IF;
          
        WHEN "1011" => -- Envelope Generator Control. Fine Tune
          snd_dr<=snd_env_per(7 DOWNTO 0);
          IF snd_wreg='1' THEN
            snd_env_per(7 DOWNTO 0)<=snd_dw;
          END IF;
          
        WHEN "1100" => -- Envelope Generator Control. Coarse Tune
          snd_dr<=snd_env_per(15 DOWNTO 8);
          IF snd_wreg='1' THEN
            snd_env_per(15 DOWNTO 8)<=snd_dw;
          END IF;
          
        WHEN "1101" => -- Envelope Shape / Cycle Control
          snd_dr<=x"0" & snd_env_shape;
          IF snd_wreg='1' THEN
            snd_env_shape<=snd_dw(3 DOWNTO 0);
            snd_env_sus<='0';
            snd_env_seq<="00000";
          END IF;
          
        WHEN "1110" => -- IO Port A data store
          snd_dr<=snd_io_ia;
          IF snd_wreg='1' THEN
            snd_io_oa<=snd_dw;
          END IF;
          
        WHEN "1111" => -- IO Port B data store
          snd_dr<=snd_io_ib;
          IF snd_wreg='1' THEN
            snd_io_ob<=snd_dw;
          END IF;

        WHEN OTHERS => NULL;
      END CASE;

      ------------------------------------------------------
      -- Tone generators
      IF tick_cpu='1' THEN
        snd_cpt8<=snd_cpt8+1;
        IF snd_cpt8="000" THEN
          IF snd_tone_a_cpt/=x"000" THEN
            snd_tone_a_cpt<=snd_tone_a_cpt-1;
          ELSE
            snd_tone_a_cpt<=snd_tone_a_per;
            snd_tone_a_out<=NOT snd_tone_a_out;
          END IF;
          
          IF snd_tone_b_cpt/=x"000" THEN
            snd_tone_b_cpt<=snd_tone_b_cpt-1;
          ELSE
            snd_tone_b_cpt<=snd_tone_b_per;
            snd_tone_b_out<=NOT snd_tone_b_out;
          END IF;
          
          IF snd_tone_c_cpt/=x"000" THEN
            snd_tone_c_cpt<=snd_tone_c_cpt-1;
          ELSE
            snd_tone_c_cpt<=snd_tone_c_per;
            snd_tone_c_out<=NOT snd_tone_c_out;
          END IF;
        END IF;
          
      END IF;

      IF snd_tone_a_en='0' THEN
        snd_tone_a_out<='0';
        snd_tone_a_cpt<=(OTHERS =>'0');
      END IF;

      IF snd_tone_b_en='0' THEN
        snd_tone_b_out<='0';
        snd_tone_b_cpt<=(OTHERS =>'0');
      END IF;

      IF snd_tone_c_en='0' THEN
        snd_tone_c_out<='0';
        snd_tone_c_cpt<=(OTHERS =>'0');
      END IF;
      
      ------------------------------------------------------
      -- Noise generators
      IF tick_cpu='1' THEN
        IF snd_cpt8="000" THEN
          IF snd_noise_cpt/="00000" THEN
            snd_noise_cpt<=snd_noise_cpt-1;
          ELSE
            snd_noise_cpt<=snd_noise_per;
            snd_noise_lfsr<=snd_noise_lfsr(30 DOWNTO 0) &
              (snd_noise_lfsr(31) XOR snd_noise_lfsr(30) XOR
               snd_noise_lfsr(29) XOR snd_noise_lfsr(9));
          END IF;
        END IF;
      END IF;

      snd_noise_out<=snd_noise_lfsr(0);

      ------------------------------------------------------
      -- Envelope
      IF tick_cpu='1' THEN
        IF snd_cpt8="000" THEN
          IF snd_env_cpt/=x"0000" THEN
            snd_env_cpt<=snd_env_cpt-1;
          ELSE
            snd_env_cpt<=snd_env_per;
            snd_env_seq<=snd_env_seq+1;
            IF snd_env_seq="01111" THEN
              snd_env_sus<='1';
            END IF;
          END IF;                
        END IF;
      END IF;

      IF snd_env_sus='0' THEN -- First period

        CASE snd_env_shape IS
          WHEN "0000" | "0001" | "0010" | "0011" | "1001" | -- \_______
               "1011" |  -- \"""""""
               "1000" |  -- \\\\\\\\
               "1010" => -- \/\/\/\/
            snd_env_amp<="1111" - snd_env_seq(3 DOWNTO 0);
            
          WHEN "0100" | "0101" | "0110" | "0111" | "1111" | -- /_______
               "1101" |  -- /"""""""
               "1100" |  -- ////////
               "1110" => -- /\/\/\/\
            snd_env_amp<=snd_env_seq(3 DOWNTO 0);
            
            IF snd_env_seq(4)=snd_env_shape(2) THEN
              snd_env_amp<="1111" - snd_env_seq(3 DOWNTO 0);
            ELSE
              snd_env_amp<=snd_env_seq(3 DOWNTO 0);
            END IF;
          WHEN OTHERS =>
            snd_env_amp<="0000";
        END CASE;
      ELSE -- Following periods

        CASE snd_env_shape IS
          WHEN "0000" | "0001" | "0010" | "0011" | "1001" |  -- \_______
               "0100" | "0101" | "0110" | "0111" | "1111" => -- /_______
            snd_env_amp<="0000";
            
          WHEN "1011" |  -- \"""""""
               "1101" => -- /"""""""
            snd_env_amp<="1111";
            
          WHEN "1000" => -- \\\\\\\\
            snd_env_amp<="1111" - snd_env_seq(3 DOWNTO 0);
            
          WHEN "1100" => -- ////////
            snd_env_amp<=snd_env_seq(3 DOWNTO 0);
            
          WHEN "1010" |  -- \/\/\/\/
               "1110" => -- /\/\/\/\
            IF snd_env_seq(4)=snd_env_shape(2) THEN
              snd_env_amp<="1111" - snd_env_seq(3 DOWNTO 0);
            ELSE
              snd_env_amp<=snd_env_seq(3 DOWNTO 0);
            END IF;
            
          WHEN OTHERS =>
            snd_env_amp<="0000";
        END CASE;
      END IF;
      
      ------------------------------------------------------
      lev_v:=x"00";
      lev_v:=lev_v+mixgen(snd_tone_a_out,snd_noise_out,
                          snd_tone_a_amp,snd_env_amp,
                          snd_tone_a_m,
                          snd_tone_a_en,snd_noise_a_en);
      xxx_lev_a<=mixgen(snd_tone_a_out,snd_noise_out,
                        snd_tone_a_amp,snd_env_amp,
                        snd_tone_a_m,
                        snd_tone_a_en,snd_noise_a_en);
      
      lev_v:=lev_v+mixgen(snd_tone_b_out,snd_noise_out,
                          snd_tone_b_amp,snd_env_amp,
                          snd_tone_b_m,
                          snd_tone_b_en,snd_noise_b_en);
      xxx_lev_b<=mixgen(snd_tone_b_out,snd_noise_out,
                        snd_tone_b_amp,snd_env_amp,
                        snd_tone_b_m,
                        snd_tone_b_en,snd_noise_b_en);
      
      lev_v:=lev_v+mixgen(snd_tone_c_out,snd_noise_out,
                          snd_tone_c_amp,snd_env_amp,
                          snd_tone_c_m,
                          snd_tone_c_en,snd_noise_c_en);
      xxx_lev_c<=mixgen(snd_tone_c_out,snd_noise_out,
                          snd_tone_c_amp,snd_env_amp,
                          snd_tone_c_m,
                          snd_tone_c_en,snd_noise_c_en);
      lev_v:=lev_v+signed(snd_dac);
      lev_v:=lev_v(6 DOWNTO 0) & '0';
      sound<=lev_v & lev_v;
      xxx_sono<=to_integer(lev_v & lev_v);
      
      ------------------------------------------------------
      -- FIRQ : Right / Right button
      right_right<=right_udlr(0);
      firq<=right_udlr(0) AND NOT right_right;
      
    END IF;
  END PROCESS SONO;
  
  ----------------------------------------------------------
  snd_io_ib<=x"00";
  snd_io_ia<= left_udlr(0) &  left_udlr(1) &  left_udlr(2) &  left_udlr(3) &
             right_udlr(0) & right_udlr(1) & right_udlr(2) & right_udlr(3);

  via_ib(5)<=to_std_logic(pot>=(dac_lev XOR x"80"));
  via_ib(6)<='0';
  via_ib(7)<='0';
  via_ib(4 DOWNTO 0)<="00000";
  
  ----------------------------------------------------------
  -- DAC and vector display.
  
  DAC:PROCESS (clk,reset_na) IS
    VARIABLE diff_v : signed(8 DOWNTO 0);
    VARIABLE mulo_v : signed(16 DOWNTO 0);
  BEGIN
    IF reset_na='0' THEN
      snd_dac<=(OTHERS =>'0');
    ELSIF rising_edge(clk) THEN
      IF dac_sh='0' THEN
        CASE dac_mux IS
          WHEN "00" => -- Y axis
            dac_sh_y<=dac_lev;
            pot<=right_x XOR x"80"; -- POT 0
            
          WHEN "01" => -- zero ref
            dac_lev_zero<=dac_lev;
            pot<=right_y XOR x"80"; -- POT 1
            
          WHEN "10" => -- Z axis
            dac_z   <=dac_lev;
            pot<=left_x XOR x"80"; -- POT 2
            
          WHEN OTHERS => -- Direct sound
            snd_dac <=dac_lev;
            pot<=left_y XOR x"80"; -- POT 3
            
        END CASE;
      END IF;
      dac_sh_x<=dac_lev;

      IF dac_zero='1' THEN
        -- Set outputs to Zero value
        diff_v:=signed(dac_lev_zero(7) & dac_lev_zero); -- 9 bits
        mulo_v:=diff_v * signed(scale);                 -- 17 bits
        
        dac_x24<=sext(unsigned(mulo_v(16 DOWNTO 0)),24);
        dac_y24<=sext(unsigned(mulo_v(16 DOWNTO 0)),24);
        
      ELSIF dac_ramp='1' THEN
        -- Integration
        diff_v:=-signed(dac_lev_zero(7) & dac_lev_zero)
                 + signed(dac_sh_x(7) & dac_sh_x);          -- 9 bits
        mulo_v:=diff_v * signed(scale);                     -- 17 bits
        
        dac_x24<=dac_x24+sext(unsigned(mulo_v(16 DOWNTO 0)),24);
        
        diff_v:=signed(dac_lev_zero(7) & dac_lev_zero)
                 - signed(dac_sh_y(7) & dac_sh_y);
        mulo_v:=diff_v * signed(scale);
        dac_y24<=dac_y24+sext(unsigned(mulo_v(16 DOWNTO 0)),24);
      ELSE
        -- ????
        
      END IF;
      
    END IF;
  END PROCESS DAC;
  
  Scalos:PROCESS(clk) IS
  BEGIN
    IF rising_edge(clk) THEN
      IF rotate='0' THEN
        xsca<=signed(dac_x24(23 DOWNTO 6)) * signed'(x"70");
        ysca<=signed(dac_y24(23 DOWNTO 6)) * signed'(x"70");
      ELSE
        xsca<=signed(    dac_y24(23 DOWNTO 6)) * signed'(x"7F");
        ysca<=signed(NOT dac_x24(23 DOWNTO 6)) * signed'(x"7F");
      END IF;
    END IF;
  END PROCESS Scalos;

  spot_x<=unsigned(xsca(24 DOWNTO 9)) + x"8000";
  spot_y<=unsigned(ysca(24 DOWNTO 9)) + x"6000";
  spot_z<=dac_z;

  spot_en<=NOT dac_blank AND tick_vec;

  --pragma synthesis_off
  Aff:PROCESS(clk) IS
    VARIABLE dis: arr_uv8(0 TO 131071) := (OTHERS =>x"00");
    VARIABLE x,y : uv(8 DOWNTO 0);
    VARIABLE xm : uv(7 DOWNTO 0);
  BEGIN
    IF rising_edge(clk) THEN
      x:=unsigned(dac_x24(23 DOWNTO 15)) + "100000000";
      y:=unsigned(dac_y24(23 DOWNTO 15)) + "011000000";

      xm:=x"00";
      xm(7-(to_integer(x) MOD 8)):='1';
      IF dac_blank='0' AND tick_vec='1' THEN
        dis(to_integer(x)/8+to_integer(y)*128):=
          dis(to_integer(x)/8+to_integer(y)*128) OR xm;
      END IF;
    END IF;

  END PROCESS Aff;
  --pragma synthesis_on

  -- 10k * 10nF
  -- I=U/R
  -- V=IT/C
  -- V=UT/RC


  -- 10V/ms -> 100µs = 150 cycles 1.5MHz

  dac_x<=dac_x24;
  dac_y<=dac_y24;
    
END ARCHITECTURE rtl;
