モノクロCRTC VHDLソース

戻る



VHDLソース

-- monochrome CRTC (c)2004 A.Hiramatsu
-- 128x96 dots 16 level gray scale

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity monocrt is
    Port ( clk : in std_logic;
           ca : in std_logic_vector(18 downto 0);
           cd : inout std_logic_vector(7 downto 0);
           ccs : in std_logic;
           crd : in std_logic;
           cwr : in std_logic;
           cwait : out std_logic;
           ma : out std_logic_vector(18 downto 0);
           md : inout std_logic_vector(7 downto 0);
           mrd : out std_logic;
           mwr : out std_logic;
           dclk : out std_logic;
           hgate : out std_logic;
           vgate : out std_logic;
           sync : out std_logic;
           do : out std_logic_vector(3 downto 0));
end monocrt;

architecture Behavioral of monocrt is

signal h:std_logic_vector(7 downto 0);
signal v:std_logic_vector(8 downto 0);
signal div:std_logic_vector(2 downto 0);
signal hff:std_logic;
signal vff:std_logic;
signal eff:std_logic;
signal s:std_logic_vector(1 downto 0);
signal ihgate:std_logic;
signal ivgate:std_logic;
signal iwait:std_logic;
signal imrd:std_logic;
signal msel:std_logic;

begin

-- NTSC timing

process(clk)
begin
  if clk'event and clk='1' then
    if (h="11000110") and (div="010") then -- 198.4 dots/line
      div <= "000";
      h <= "00000000";
      if v="100000110" then -- 263 lines
        v <= "000000000";
      else
        v <= v + 1;
      end if;
    else
      if div="111" then
        h <= h + 1;
      end if;
      div <= div + 1;
    end if;
  end if;
end process;

process(clk) -- hsync pulse
begin
  if clk'event and clk='1' then
    if (h="10010111") and (div="111") then
      hff <= '1';
    elsif (h="10100110") and (div="111") then
      hff <= '0';
    end if;
  end if;
end process;

process(clk) -- equalize pulse
begin
  if clk'event and clk='1' then
    if ((h="10010111") and (div="111")) or
       ((h="00110011") and (div="111")) then
      eff <= '1';
    elsif ((h="10011110") and (div="111")) or
          ((h="00111010") and (div="111")) then
      eff <= '0';
    end if;
  end if;
end process;

process(clk) -- vsync pulse
begin
  if clk'event and clk='1' then
    if ((h="10010111") and (div="111")) or
       ((h="00110011") and (div="111")) then
      vff <= '1';
    elsif ((h="10001000") and (div="111")) or
          ((h="00111100") and (div="111")) then
      vff <= '0';
    end if;
  end if;
end process;

process(clk) -- vertical state(00:hsync 01,11:equalize 10:vsync)
begin
  if clk'event and clk='1' then
    if (h="10010111") and (div="111") then
      if v="011100011" then
        s <= "00";
      else
        if(v="011011010") or (v="011011101") or (v="011100000") then
          s <= s + 1;
        end if;
      end if;
    end if;
  end if;
end process;

dclk <= div(2);

process(s,hff,vff,eff)
begin
  case s is
  when "00" => sync <= hff;
  when "10" => sync <= vff;
  when others => sync <= eff;
  end case;
end process;

-- video data out

process(clk) -- horizontal gate
begin
  if clk'event and clk='1' then
    if (h="11000110") and (div="010") then
      ihgate <= '1';
    elsif (h="01111111") and (div="111") then
      ihgate <= '0';
    end if;
  end if;
end process;

process(clk) -- vertical gate
begin
  if clk'event and clk='1' then
    if (h="10010111") and (div="111") then
      if v="100000101" then
        ivgate <= '1';
      elsif v="010111111" then
        ivgate <= '0';
      end if;
    end if;
  end if;
end process;

hgate <= ihgate;
vgate <= ivgate;

process(clk) -- output data latch
begin
  if clk'event and clk='1' then
    if div="111" then
      if (ihgate and ivgate)='1' then
        do <= md(3 downto 0);
      else
        do <= "0000";
      end if;
    end if;
  end if;
end process;

-- memory arbitoration

process(clk)
begin
  if clk'event and clk='1' then
    if div="011" then
      iwait <= ihgate and ivgate;
    elsif div="111" then
      iwait <= '0';
    end if;
  end if;
end process;

process(clk)
begin
  if clk'event and clk='1' then
    if div="101" then
      msel <= ihgate and ivgate;
    elsif div="111" then
      msel <= '0';
    end if;
  end if;
end process;

process(msel,v,h,ca)
begin
  if msel='1' then
    ma <= "00000" & v(7 downto 1) & h(6 downto 0);
  else
    ma <= ca;
  end if;
end process;

process(ccs,crd,md)
begin
  if (ccs='0') and (crd='0') then
    cd <= md;
  else
    cd <= "ZZZZZZZZ";
  end if;
end process;

process(msel,crd,cd)
begin
  if msel='1' then
    md <= "ZZZZZZZZ";
    mrd <= '0';
  else
    if crd='0' then
      md <= "ZZZZZZZZ";
      mrd <= '0';
    else
      md <= cd;
      mrd <= '1';
    end if;
  end if;
end process;

process(msel,ccs,cwr,iwait)
begin
  if msel='1' then
    mwr <= '1';
  else
    mwr <= ccs or cwr or iwait;
  end if;
end process;

cwait <= ccs or (not iwait);

end Behavioral;

解説

・ピン

 ピン配置は、G2436コントローラのものを、以下の信号名を変えるだけで、あとは同じです。

cp2 → dclk
cp1 → hgate
m → sync
frm → vgate


 コンポジット同期信号は、いままで交流化信号 m だった所から出しました。cp2、cp1、frm の信号は出さなくてもいいのですが、いままで出力だったところをオープンにするのはなんか落ち着かないので、それぞれドットクロック(dclk)、水平表示期間(hgate)、垂直表示期間(vgate)にしました。hgate、vgate はCPUの割り込み要求端子につなげば水平周期割り込みや垂直周期割り込みをかけることができるようになると思います。ドットクロックは気休めですが、ドットを出力するタイミングを外部で知ることができたらなんかの役にたつことがあるかもしれないと思い、出しました。

・主な信号名の意味








ca,cd,crd,cwr,ccs,cwait

CPUバスに接続します

ma,md,mwr,mrd

メモリ(628512)に接続します

dclk

ドットクロックです。約3.125MHzです

hgate

水平表示期間のとき"H"になります

hgate="H" かつ vgate="H" のとき、CPUとCRTCのバス競合が起こり得るので、内部信号 iwait が競合を防ぎます。また、hgate="L" または vgate="L" のとき、ビデオ出力に余計な信号を出さないよう do3 〜 do0 を全部"L"にします。

vgate

垂直表示期間のとき"H"になります

sync

反転したコンポジット同期信号です
(簡易D/Aコンバータで再度反転されます)

do

4ビットの輝度出力です





div

システムクロック(25MHz)を8分周します

h

水平位置です。0〜127のときhgateが"H"になります。151あたりで水平同期パルスをだします。198(DIV=2)でリセットされます。

v

垂直位置です。h が 198→0 にリセットされるたびに1づつ増えます。0〜191のときvgateが"H"になります。252でリセットされます。

hff

水平同期パルスです

これらの信号は v の値により適宜切り換えられて最終的にコンポジット同期信号になります。
(等化パルスは、インターレスをしないなら不要なのですが、将来容易にインターレスに対応できるように入れておきました)

eff

等化パルスです

vff

垂直同期パルスです。

msel

これが"H"のときはCRTCがメモリをアクセスしており、"L"のときにはCPUがメモリをアクセスできます。

iwait

msel より2クロック早く立ちあがり、msel と同時に立ち下がります。CRTCがメモリをアクセスする際、CPUを待たせるのに使います。


・VRAM構成

 $200000〜$202FFF までの12KバイトがVRAMになります。単純に1バイト=1ドットです。画面左上角の番地が $200000 で、1ライン128バイトで、途中に番地の飛びなどはなく、リニアな構造になっています。

 下位4ビットの値はそのままドットの輝度になります(0:真っ黒〜15:真っ白)。
 上位4ビットは表示には全く影響を与えませんがメモリは存在しており、ユーザーは自由に使うことができます。たとえばシューティングゲームなどなら当り判定データを置いてもいいし、マルチウインドウのGUIシステムなんかを作りたい場合はウインドウの前後関係を表わす簡易Zデプスバッファにしてもいいでしょう。

 1バイトに2ドット詰め込む「パックトピクセル」にしなかったのは、H8は4ビット単位のデータの扱いが苦手だからです。たとえば「画面全体を1ドット右にずらす」なんて処理は、1ドット=1バイトなら単なるデータ転送で済みますが、もしパックトピクセルだったら恐ろしく面倒くさくなります。