-- 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 |
外 |
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 |
反転したコンポジット同期信号です | ||
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を待たせるのに使います。 |
$200000〜$202FFF までの12KバイトがVRAMになります。単純に1バイト=1ドットです。画面左上角の番地が $200000 で、1ライン128バイトで、途中に番地の飛びなどはなく、リニアな構造になっています。
下位4ビットの値はそのままドットの輝度になります(0:真っ黒〜15:真っ白)。
上位4ビットは表示には全く影響を与えませんがメモリは存在しており、ユーザーは自由に使うことができます。たとえばシューティングゲームなどなら当り判定データを置いてもいいし、マルチウインドウのGUIシステムなんかを作りたい場合はウインドウの前後関係を表わす簡易Zデプスバッファにしてもいいでしょう。
1バイトに2ドット詰め込む「パックトピクセル」にしなかったのは、H8は4ビット単位のデータの扱いが苦手だからです。たとえば「画面全体を1ドット右にずらす」なんて処理は、1ドット=1バイトなら単なるデータ転送で済みますが、もしパックトピクセルだったら恐ろしく面倒くさくなります。