1 -------------------------------------------------------------
\r
2 -------------------------------------------------------------
\r
3 ------------------- PPU VGA Output Control ------------------
\r
4 -------------------------------------------------------------
\r
5 -------------------------------------------------------------
\r
7 use ieee.std_logic_1164.all;
\r
8 use ieee.std_logic_unsigned.conv_integer;
\r
9 use ieee.std_logic_arith.conv_std_logic_vector;
\r
10 use ieee.std_logic_unsigned.all;
\r
14 pi_rst_n : in std_logic;
\r
15 pi_base_clk : in std_logic;
\r
16 pi_rnd_en : in std_logic_vector (3 downto 0);
\r
19 pi_ppu_ctrl : in std_logic_vector (7 downto 0);
\r
20 pi_ppu_mask : in std_logic_vector (7 downto 0);
\r
21 po_ppu_status : out std_logic_vector (7 downto 0);
\r
22 pi_ppu_scroll_x : in std_logic_vector (7 downto 0);
\r
23 pi_ppu_scroll_y : in std_logic_vector (7 downto 0);
\r
26 po_rd_n : out std_logic;
\r
27 po_wr_n : out std_logic;
\r
28 po_v_addr : out std_logic_vector (13 downto 0);
\r
29 pi_v_data : in std_logic_vector (7 downto 0);
\r
32 po_spr_ce_n : out std_logic;
\r
33 po_spr_rd_n : out std_logic;
\r
34 po_spr_wr_n : out std_logic;
\r
35 po_spr_addr : out std_logic_vector (7 downto 0);
\r
36 pi_spr_data : in std_logic_vector (7 downto 0);
\r
39 po_h_sync_n : out std_logic;
\r
40 po_v_sync_n : out std_logic;
\r
41 po_r : out std_logic_vector(3 downto 0);
\r
42 po_g : out std_logic_vector(3 downto 0);
\r
43 po_b : out std_logic_vector(3 downto 0)
\r
47 architecture rtl of render is
\r
50 --------- VGA screen constant -----------
\r
51 constant VGA_W : integer := 640;
\r
52 constant VGA_H : integer := 480;
\r
53 constant VGA_W_MAX : integer := 800;
\r
54 constant VGA_H_MAX : integer := 525;
\r
55 constant H_SP : integer := 95;
\r
56 constant H_BP : integer := 48;
\r
57 constant H_FP : integer := 15;
\r
58 constant V_SP : integer := 2;
\r
59 constant V_BP : integer := 33;
\r
60 constant V_FP : integer := 10;
\r
62 --nes screen size is emulated to align with the vga timing...
\r
63 constant HSCAN : integer := 256;
\r
64 constant VSCAN : integer := 240;
\r
65 constant HSCAN_NEXT_START : integer := 382;
\r
66 constant VSCAN_NEXT_START : integer := 262;
\r
67 constant HSCAN_SPR_MAX : integer := 321;
\r
68 constant HSCAN_OAM_EVA_START : integer := 64;
\r
70 constant PREFETCH_INT : integer := 16;
\r
72 constant PPUBNA : integer := 1; --base name address
\r
73 constant PPUVAI : integer := 2; --vram address increment
\r
74 constant PPUSPA : integer := 3; --sprite pattern table address
\r
75 constant PPUBPA : integer := 4; --background pattern table address
\r
76 constant PPUSPS : integer := 5; --sprite size
\r
77 constant PPUMS : integer := 6; --ppu master/slave
\r
78 constant PPUNEN : integer := 7; --nmi enable
\r
80 constant PPUGS : integer := 0; --grayscale
\r
81 constant PPUSBL : integer := 1; --show 8 left most bg pixel
\r
82 constant PPUSSL : integer := 2; --show 8 left most sprite pixel
\r
83 constant PPUSBG : integer := 3; --show bg
\r
84 constant PPUSSP : integer := 4; --show sprie
\r
85 constant PPUIR : integer := 5; --intensify red
\r
86 constant PPUIG : integer := 6; --intensify green
\r
87 constant PPUIB : integer := 7; --intensify blue
\r
89 constant SPRHFL : integer := 6; --flip sprigte horizontally
\r
90 constant SPRVFL : integer := 7; --flip sprigte vertically
\r
92 constant ST_BSY : integer := 4; --vram busy
\r
93 constant ST_SOF : integer := 5; --sprite overflow
\r
94 constant ST_SP0 : integer := 6; --sprite 0 hits
\r
95 constant ST_VBL : integer := 7; --vblank
\r
98 subtype nes_color_data is std_logic_vector (11 downto 0);
\r
99 type nes_color_array is array (0 to 63) of nes_color_data;
\r
100 --ref: http://hlc6502.web.fc2.com/NesPal2.htm
\r
101 constant nes_color_palette : nes_color_array := (
\r
102 conv_std_logic_vector(16#777#, 12),
\r
103 conv_std_logic_vector(16#20b#, 12),
\r
104 conv_std_logic_vector(16#20b#, 12),
\r
105 conv_std_logic_vector(16#61a#, 12),
\r
106 conv_std_logic_vector(16#927#, 12),
\r
107 conv_std_logic_vector(16#b13#, 12),
\r
108 conv_std_logic_vector(16#a30#, 12),
\r
109 conv_std_logic_vector(16#740#, 12),
\r
110 conv_std_logic_vector(16#450#, 12),
\r
111 conv_std_logic_vector(16#360#, 12),
\r
112 conv_std_logic_vector(16#360#, 12),
\r
113 conv_std_logic_vector(16#364#, 12),
\r
114 conv_std_logic_vector(16#358#, 12),
\r
115 conv_std_logic_vector(16#000#, 12),
\r
116 conv_std_logic_vector(16#000#, 12),
\r
117 conv_std_logic_vector(16#000#, 12),
\r
118 conv_std_logic_vector(16#bbb#, 12),
\r
119 conv_std_logic_vector(16#46f#, 12),
\r
120 conv_std_logic_vector(16#44f#, 12),
\r
121 conv_std_logic_vector(16#94f#, 12),
\r
122 conv_std_logic_vector(16#d4c#, 12),
\r
123 conv_std_logic_vector(16#d46#, 12),
\r
124 conv_std_logic_vector(16#e50#, 12),
\r
125 conv_std_logic_vector(16#c70#, 12),
\r
126 conv_std_logic_vector(16#880#, 12),
\r
127 conv_std_logic_vector(16#5a0#, 12),
\r
128 conv_std_logic_vector(16#4a1#, 12),
\r
129 conv_std_logic_vector(16#4a6#, 12),
\r
130 conv_std_logic_vector(16#49c#, 12),
\r
131 conv_std_logic_vector(16#000#, 12),
\r
132 conv_std_logic_vector(16#000#, 12),
\r
133 conv_std_logic_vector(16#000#, 12),
\r
134 conv_std_logic_vector(16#fff#, 12),
\r
135 conv_std_logic_vector(16#6af#, 12),
\r
136 conv_std_logic_vector(16#58f#, 12),
\r
137 conv_std_logic_vector(16#a7f#, 12),
\r
138 conv_std_logic_vector(16#f6f#, 12),
\r
139 conv_std_logic_vector(16#f6b#, 12),
\r
140 conv_std_logic_vector(16#f73#, 12),
\r
141 conv_std_logic_vector(16#fa0#, 12),
\r
142 conv_std_logic_vector(16#ed2#, 12),
\r
143 conv_std_logic_vector(16#9e0#, 12),
\r
144 conv_std_logic_vector(16#7f4#, 12),
\r
145 conv_std_logic_vector(16#7e9#, 12),
\r
146 conv_std_logic_vector(16#6de#, 12),
\r
147 conv_std_logic_vector(16#777#, 12),
\r
148 conv_std_logic_vector(16#000#, 12),
\r
149 conv_std_logic_vector(16#000#, 12),
\r
150 conv_std_logic_vector(16#fff#, 12),
\r
151 conv_std_logic_vector(16#9df#, 12),
\r
152 conv_std_logic_vector(16#abf#, 12),
\r
153 conv_std_logic_vector(16#cbf#, 12),
\r
154 conv_std_logic_vector(16#ebf#, 12),
\r
155 conv_std_logic_vector(16#fbe#, 12),
\r
156 conv_std_logic_vector(16#fcb#, 12),
\r
157 conv_std_logic_vector(16#fda#, 12),
\r
158 conv_std_logic_vector(16#ff9#, 12),
\r
159 conv_std_logic_vector(16#cf8#, 12),
\r
160 conv_std_logic_vector(16#afa#, 12),
\r
161 conv_std_logic_vector(16#afc#, 12),
\r
162 conv_std_logic_vector(16#aff#, 12),
\r
163 conv_std_logic_vector(16#aaa#, 12),
\r
164 conv_std_logic_vector(16#000#, 12),
\r
165 conv_std_logic_vector(16#000#, 12)
\r
168 signal reg_vga_x : integer range 0 to VGA_W_MAX - 1;
\r
169 signal reg_vga_y : integer range 0 to VGA_H_MAX - 1;
\r
171 signal reg_nes_x : integer range 0 to VGA_W_MAX / 2 - 1;
\r
172 signal reg_nes_y : integer range 0 to VGA_W_MAX / 2 - 1;
\r
173 --prefech is wider by scroll reg size.
\r
174 signal reg_prf_x : integer range 0 to VGA_W_MAX / 2 + 256 - 1;
\r
175 signal reg_prf_y : integer range 0 to VGA_W_MAX / 2 + 256 - 1;
\r
177 type vac_state is (
\r
189 signal reg_v_cur_state : vac_state;
\r
190 signal reg_v_next_state : vac_state;
\r
192 signal reg_v_rd_n : std_logic;
\r
193 signal reg_v_wr_n : std_logic;
\r
194 signal reg_v_addr : std_logic_vector (13 downto 0);
\r
195 signal reg_v_data : std_logic_vector (7 downto 0);
\r
197 signal reg_disp_nt : std_logic_vector (7 downto 0);
\r
198 signal reg_disp_attr : std_logic_vector (7 downto 0);
\r
199 signal reg_disp_ptn_l : std_logic_vector (15 downto 0);
\r
200 signal reg_disp_ptn_h : std_logic_vector (15 downto 0);
\r
204 --position and sync signal generate.
\r
205 pos_p : process (pi_rst_n, pi_base_clk)
\r
207 if (pi_rst_n = '0') then
\r
214 elsif (rising_edge(pi_base_clk)) then
\r
215 if ((pi_rnd_en(0) or pi_rnd_en(2))= '1') then
\r
216 if (reg_vga_x = VGA_W_MAX - 1) then
\r
218 if (reg_vga_x = VGA_H_MAX - 1) then
\r
221 reg_vga_y <= reg_vga_y + 1;
\r
224 reg_vga_x <= reg_vga_x + 1;
\r
227 --sync signal assert.
\r
228 if (reg_vga_x >= VGA_W + H_FP and reg_vga_x < VGA_W + H_FP + H_SP) then
\r
229 po_h_sync_n <= '0';
\r
231 po_h_sync_n <= '1';
\r
234 if (reg_vga_y >= VGA_H + V_FP and reg_vga_y < VGA_H + V_FP + V_SP) then
\r
235 po_v_sync_n <= '0';
\r
237 po_v_sync_n <= '1';
\r
239 end if;--if (pi_rnd_en(1) = '1' or pi_rnd_en(3) = '1' ) then
\r
241 --nes x/y position...
\r
242 reg_nes_x <= reg_vga_x / 2;
\r
243 reg_nes_y <= reg_vga_y / 2;
\r
245 --pre-fetch x/y position...
\r
246 if (reg_vga_x < HSCAN_NEXT_START * 2) then
\r
247 reg_prf_x <= reg_vga_x / 2 + conv_integer(pi_ppu_scroll_x) + PREFETCH_INT;
\r
249 reg_prf_x <= reg_vga_x / 2 + conv_integer(pi_ppu_scroll_x)
\r
250 - HSCAN_NEXT_START + PREFETCH_INT;
\r
253 if (reg_vga_y < VSCAN * 2) then
\r
254 if (reg_vga_x < HSCAN_NEXT_START * 2) then
\r
255 reg_prf_y <= reg_vga_y / 2 + conv_integer(pi_ppu_scroll_y);
\r
257 reg_prf_y <= (reg_vga_y + 1) / 2 + conv_integer(pi_ppu_scroll_y);
\r
262 end if;--if (pi_rst_n = '0') then
\r
265 --vram access state machine (state transition)...
\r
266 vac_set_stat_p : process (pi_rst_n, pi_base_clk)
\r
268 if (pi_rst_n = '0') then
\r
269 reg_v_cur_state <= IDLE;
\r
270 elsif (rising_edge(pi_base_clk)) then
\r
271 reg_v_cur_state <= reg_v_next_state;
\r
272 end if;--if (pi_rst_n = '0') then
\r
275 --state change to next.
\r
276 vac_next_stat_p : process (reg_v_cur_state, pi_rnd_en, pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y)
\r
277 function bg_process (
\r
278 pm_sbg : in std_logic;
\r
279 pm_nes_x : in integer range 0 to VGA_W_MAX - 1;
\r
280 pm_nes_y : in integer range 0 to VGA_H_MAX - 1
\r
283 if (pm_sbg = '1'and
\r
284 (pm_nes_x <= HSCAN or pm_nes_x >= HSCAN_NEXT_START) and
\r
285 (pm_nes_y < VSCAN or pm_nes_y = VSCAN_NEXT_START)) then
\r
293 pm_sbg : in std_logic;
\r
294 pm_nes_x : in integer range 0 to VGA_W_MAX - 1;
\r
295 pm_nes_y : in integer range 0 to VGA_H_MAX - 1
\r
298 if (pm_sbg = '0' or
\r
299 (pm_nes_x > HSCAN and pm_nes_x < HSCAN_NEXT_START) or
\r
300 (pm_nes_y >= VSCAN and pm_nes_y < VSCAN_NEXT_START)) then
\r
307 case reg_v_cur_state is
\r
309 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
310 pi_rnd_en(3) = '1' and
\r
311 reg_nes_x mod 8 = 0) then
\r
312 --start vram access process.
\r
313 reg_v_next_state <= AD_SET0;
\r
315 reg_v_next_state <= reg_v_cur_state;
\r
318 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
321 reg_v_next_state <= AD_SET1;
\r
322 elsif (is_idle(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1) then
\r
323 ---when nes_x=257, fall to idle
\r
324 reg_v_next_state <= IDLE;
\r
326 reg_v_next_state <= reg_v_cur_state;
\r
329 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
332 reg_v_next_state <= AD_SET2;
\r
334 reg_v_next_state <= reg_v_cur_state;
\r
337 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
340 reg_v_next_state <= AD_SET3;
\r
342 reg_v_next_state <= reg_v_cur_state;
\r
345 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
348 reg_v_next_state <= REG_SET0;
\r
350 reg_v_next_state <= reg_v_cur_state;
\r
353 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
356 reg_v_next_state <= REG_SET1;
\r
358 reg_v_next_state <= reg_v_cur_state;
\r
361 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
364 reg_v_next_state <= REG_SET2;
\r
366 reg_v_next_state <= reg_v_cur_state;
\r
369 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
372 reg_v_next_state <= REG_SET3;
\r
374 reg_v_next_state <= reg_v_cur_state;
\r
377 if (bg_process(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1 and
\r
380 reg_v_next_state <= AD_SET0;
\r
382 reg_v_next_state <= reg_v_cur_state;
\r
387 po_rd_n <= reg_v_rd_n;
\r
388 po_wr_n <= reg_v_wr_n;
\r
389 po_v_addr <= reg_v_addr;
\r
391 --vram r/w selector state machine...
\r
392 vac_main_stat_p : process (reg_v_cur_state)
\r
394 case reg_v_cur_state is
\r
398 when AD_SET0 | AD_SET1 | REG_SET2 | REG_SET3 =>
\r
401 when AD_SET2 | AD_SET3 | REG_SET0 | REG_SET1 =>
\r
407 --vram address state machine...
\r
408 vaddr_stat_p : process (pi_rst_n, pi_base_clk)
\r
410 pm_sbg : in std_logic;
\r
411 pm_nes_x : in integer range 0 to VGA_W_MAX - 1;
\r
412 pm_nes_y : in integer range 0 to VGA_H_MAX - 1
\r
415 if (pm_sbg = '1'and
\r
416 (pm_nes_x <= HSCAN or pm_nes_x >= HSCAN_NEXT_START) and
\r
417 (pm_nes_y < VSCAN or pm_nes_y = VSCAN_NEXT_START)) then
\r
424 if (pi_rst_n = '0') then
\r
425 reg_v_addr <= (others => 'Z');
\r
426 reg_v_data <= (others => 'Z');
\r
427 elsif (rising_edge(pi_base_clk)) then
\r
428 reg_v_data <= pi_v_data;
\r
430 if (is_bg(pi_ppu_mask(PPUSBG), reg_nes_x, reg_nes_y) = 1) then
\r
431 ----fetch next tile byte.
\r
432 if (reg_prf_x mod 8 = 1) then
\r
433 --vram addr is incremented every 8 cycle.
\r
434 --name table at 0x2000
\r
435 reg_v_addr(9 downto 0)
\r
436 <= conv_std_logic_vector(reg_prf_y, 9)(7 downto 3)
\r
437 & conv_std_logic_vector(reg_prf_x, 9)(7 downto 3);
\r
438 reg_v_addr(13 downto 10) <= "10" & pi_ppu_ctrl(PPUBNA downto 0)
\r
439 + ("000" & conv_std_logic_vector(reg_prf_x, 9)(8));
\r
440 ----fetch attr table byte.
\r
441 elsif (reg_prf_x mod 8 = 3) then
\r
442 --attr table at 0x23c0
\r
443 reg_v_addr(7 downto 0) <= "11000000" +
\r
444 ("00" & conv_std_logic_vector(reg_prf_y, 9)(7 downto 5)
\r
445 & conv_std_logic_vector(reg_prf_x, 9)(7 downto 5));
\r
446 reg_v_addr(13 downto 8) <= "10" &
\r
447 pi_ppu_ctrl(PPUBNA downto 0) & "11"
\r
448 + ("000" & conv_std_logic_vector(reg_prf_x, 9)(8) & "00");
\r
449 ----fetch pattern table low byte.
\r
450 elsif (reg_prf_x mod 8 = 5) then
\r
451 --vram addr is incremented every 8 cycle.
\r
452 reg_v_addr <= "0" & pi_ppu_ctrl(PPUBPA) &
\r
453 reg_disp_nt(7 downto 0)
\r
454 & "0" & conv_std_logic_vector(reg_prf_y, 9)(2 downto 0);
\r
455 ----fetch pattern table high byte.
\r
456 elsif (reg_prf_x mod 8 = 7) then
\r
457 --vram addr is incremented every 8 cycle.
\r
458 reg_v_addr <= "0" & pi_ppu_ctrl(PPUBPA) &
\r
459 reg_disp_nt(7 downto 0)
\r
460 & "0" & conv_std_logic_vector(reg_prf_y, 9)(2 downto 0)
\r
461 + "00000000001000";
\r
464 end if;--if (pi_rst_n = '0') then
\r
467 --vram r/w selector state machine...
\r
468 bg_main_stat_p : process (reg_v_cur_state, reg_v_data)
\r
470 case reg_v_cur_state is
\r
472 reg_disp_nt <= (others => 'Z');
\r
473 reg_disp_attr <= (others => 'Z');
\r
474 reg_disp_ptn_l <= (others => 'Z');
\r
475 reg_disp_ptn_h <= (others => 'Z');
\r
476 when AD_SET0 | AD_SET1 | REG_SET2 | AD_SET2 | AD_SET3 | REG_SET0 | REG_SET1 =>
\r
477 -- reg_disp_nt <= (others => 'Z');
\r
478 -- reg_disp_attr <= (others => 'Z');
\r
479 -- reg_disp_ptn_l <= (others => 'Z');
\r
480 -- reg_disp_ptn_h <= (others => 'Z');
\r
482 reg_disp_nt <= reg_v_data;
\r
483 reg_disp_attr <= reg_v_data;
\r
484 reg_disp_ptn_l <= reg_v_data & "00000000";
\r
485 reg_disp_ptn_h <= reg_v_data & "00000000";
\r
490 po_ppu_status <= (others => '0');
\r
492 po_spr_ce_n <= 'Z';
\r
493 po_spr_rd_n <= 'Z';
\r
494 po_spr_wr_n <= 'Z';
\r
495 po_spr_addr <= (others => 'Z');
\r
497 po_r <= (others => 'Z');
\r
498 po_g <= (others => 'Z');
\r
499 po_b <= (others => 'Z');
\r