···11+-- ========================================================================
22+-- Physics Engine
33+-- Runs once per frame on vert_sync rising edge (~60Hz).
44+-- Handles gravity, keyboard input, friction, bouncing off walls/floor/
55+-- ceiling/obstacles, and squish animation.
66+--
77+-- Outputs character position and animated dimensions for the renderer.
88+-- All velocity math is 10-bit 2's complement (bit 9 = sign).
99+-- ========================================================================
1010+1111+library IEEE;
1212+use IEEE.STD_LOGIC_1164.all;
1313+use IEEE.STD_LOGIC_ARITH.all;
1414+use IEEE.STD_LOGIC_UNSIGNED.all;
1515+1616+entity physics_engine is
1717+ port(
1818+ vert_sync : in std_logic; -- frame clock (~60Hz)
1919+ key_w : in std_logic; -- from ps2_decoder
2020+ key_a : in std_logic;
2121+ key_s : in std_logic;
2222+ key_d : in std_logic;
2323+ char_x : out std_logic_vector(9 downto 0); -- character center X
2424+ char_y : out std_logic_vector(9 downto 0); -- character center Y
2525+ char_width : out std_logic_vector(9 downto 0); -- animated half-width
2626+ char_height : out std_logic_vector(9 downto 0) -- animated half-height
2727+ );
2828+end physics_engine;
2929+3030+architecture behavior of physics_engine is
3131+3232+ -- Character state
3333+ signal pos_x : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(100, 10);
3434+ signal pos_y : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10);
3535+ signal vel_x : std_logic_vector(9 downto 0) := (others => '0');
3636+ signal vel_y : std_logic_vector(9 downto 0) := (others => '0');
3737+3838+ constant SIZE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(7, 10);
3939+4040+ -- Tuning constants
4141+ constant GRAVITY : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(1, 10);
4242+ constant IMPULSE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(3, 10);
4343+ constant JUMP_FORCE : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(13, 10);
4444+ constant MAX_VEL_X : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(32, 10);
4545+4646+ -- Screen bounds
4747+ constant GROUND : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(440, 10);
4848+ constant CEILING : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(16, 10);
4949+ constant LEFT_WALL : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(8, 10);
5050+ constant RIGHT_WALL: std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(631, 10);
5151+5252+ -- Obstacle positions (must match renderer)
5353+ constant O1_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(60, 10);
5454+ constant O1_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(370, 10);
5555+ constant O1_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(180, 10);
5656+ constant O1_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(386, 10);
5757+5858+ constant O2_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(250, 10);
5959+ constant O2_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(300, 10);
6060+ constant O2_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(390, 10);
6161+ constant O2_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(316, 10);
6262+6363+ constant O3_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(440, 10);
6464+ constant O3_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10);
6565+ constant O3_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(580, 10);
6666+ constant O3_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(216, 10);
6767+6868+ constant O4_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(140, 10);
6969+ constant O4_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(120, 10);
7070+ constant O4_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10);
7171+ constant O4_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(150, 10);
7272+7373+ -- Animation state
7474+ signal squish : std_logic_vector(3 downto 0) := (others => '0');
7575+ signal squish_h : std_logic := '0'; -- '0'=vertical squish, '1'=horizontal
7676+ signal on_ground : std_logic := '0';
7777+ signal jump_pressed : std_logic := '0';
7878+7979+begin
8080+8181+ -- Output character position
8282+ char_x <= pos_x;
8383+ char_y <= pos_y;
8484+8585+ -- Squish deforms the character: vertical hit = wider+shorter, wall hit = taller+narrower
8686+ char_width <= SIZE + ("000000" & squish) when squish_h = '0'
8787+ else SIZE - ("000000" & squish(3 downto 1));
8888+ char_height <= SIZE - ("000000" & squish(3 downto 1)) when squish_h = '0'
8989+ else SIZE + ("000000" & squish);
9090+9191+ -- Main physics process: one tick per frame
9292+ physics : process
9393+ variable vx, vy : std_logic_vector(9 downto 0);
9494+ variable px, py : std_logic_vector(9 downto 0);
9595+ variable bounced : std_logic;
9696+ variable bounce_wall : std_logic;
9797+ variable bounce_speed : std_logic_vector(9 downto 0);
9898+ variable grounded : std_logic;
9999+ variable c_left, c_right, c_top, c_bot : std_logic_vector(9 downto 0);
100100+ variable overlap_x, overlap_y : std_logic_vector(9 downto 0);
101101+ begin
102102+ wait until vert_sync'event and vert_sync = '1';
103103+104104+ vx := vel_x;
105105+ vy := vel_y;
106106+ bounced := '0';
107107+ bounce_wall := '0';
108108+ bounce_speed := (others => '0');
109109+ grounded := '0';
110110+111111+ -- == INPUT ==
112112+113113+ if key_w = '0' then
114114+ jump_pressed <= '0';
115115+ end if;
116116+117117+ -- Jump on ground (single impulse)
118118+ if key_w = '1' and jump_pressed = '0' and on_ground = '1' then
119119+ vy := (others => '0');
120120+ vy := vy - JUMP_FORCE;
121121+ jump_pressed <= '1';
122122+ end if;
123123+124124+ -- Bounce boost: holding W adds energy each ground contact
125125+ if key_w = '1' and jump_pressed = '1' and on_ground = '1' then
126126+ vy := vy - 4;
127127+ end if;
128128+129129+ -- Slam down (air only)
130130+ if key_s = '1' and on_ground = '0' then
131131+ vy := vy + IMPULSE;
132132+ end if;
133133+134134+ -- Horizontal: full on ground, reduced in air
135135+ if key_a = '1' then
136136+ if on_ground = '1' then vx := vx - IMPULSE;
137137+ else vx := vx - 2; end if;
138138+ end if;
139139+ if key_d = '1' then
140140+ if on_ground = '1' then vx := vx + IMPULSE;
141141+ else vx := vx + 2; end if;
142142+ end if;
143143+144144+ -- == GRAVITY ==
145145+ vy := vy + GRAVITY;
146146+147147+ -- == FRICTION: vel -= vel/4, min 1 ==
148148+ if vx(9) = '0' then
149149+ if vx > 0 then
150150+ if vx(9 downto 2) = "00000000" then vx := vx - 1;
151151+ else vx := vx - ("000" & vx(9 downto 3)); end if;
152152+ end if;
153153+ else
154154+ if vx /= "0000000000" then
155155+ if vx(9 downto 2) = "11111111" then vx := vx + 1;
156156+ else vx := vx - ("11" & vx(9 downto 2)); end if;
157157+ end if;
158158+ end if;
159159+160160+ -- == CLAMP ==
161161+ if vx(9) = '0' and vx > MAX_VEL_X then vx := MAX_VEL_X; end if;
162162+ if vx(9) = '1' and vx < (not MAX_VEL_X) + 1 then vx := (not MAX_VEL_X) + 1; end if;
163163+ if vy(9) = '0' and vy > 63 then vy := CONV_STD_LOGIC_VECTOR(63, 10); end if;
164164+ if vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(960, 10) then vy := CONV_STD_LOGIC_VECTOR(960, 10); end if;
165165+166166+ -- == MOVE ==
167167+ px := pos_x + vx;
168168+ py := pos_y + vy;
169169+170170+ -- == GROUND ==
171171+ if py >= GROUND then
172172+ py := GROUND;
173173+ bounced := '1'; grounded := '1';
174174+ bounce_speed := vy;
175175+ vy := (not vy) + 1;
176176+ if vy(9) = '1' then
177177+ vy := vy + ("000" & ((not vy(9 downto 3)) + 1));
178178+ end if;
179179+ if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0');
180180+ elsif vy(9) = '0' then vy := (others => '0'); end if;
181181+ end if;
182182+183183+ -- == CEILING ==
184184+ if py(9) = '1' or py <= CEILING then
185185+ py := CEILING;
186186+ bounced := '1';
187187+ bounce_speed := (not vy) + 1;
188188+ vy := (not vy) + 1;
189189+ if vy(9) = '0' and vy > 1 then
190190+ vy := vy - ("000" & vy(9 downto 3));
191191+ end if;
192192+ end if;
193193+194194+ -- == LEFT WALL ==
195195+ if px(9) = '1' or px <= LEFT_WALL then
196196+ px := LEFT_WALL;
197197+ bounced := '1'; bounce_wall := '1';
198198+ bounce_speed := (not vx) + 1;
199199+ vx := (not vx) + 1;
200200+ if vx(9) = '0' and vx > 1 then
201201+ vx := vx - ("000" & vx(9 downto 3));
202202+ end if;
203203+ end if;
204204+205205+ -- == RIGHT WALL ==
206206+ if px >= RIGHT_WALL then
207207+ px := RIGHT_WALL;
208208+ bounced := '1'; bounce_wall := '1';
209209+ bounce_speed := vx;
210210+ vx := (not vx) + 1;
211211+ -- vx is now negative: reduce magnitude toward zero
212212+ if vx(9) = '1' then
213213+ vx := vx + ("000" & ((not vx(9 downto 3)) + 1));
214214+ end if;
215215+ -- Kill tiny bounces
216216+ if vx(9) = '1' and vx >= CONV_STD_LOGIC_VECTOR(1022, 10) then vx := (others => '0'); end if;
217217+ end if;
218218+219219+ -- == OBSTACLE COLLISIONS ==
220220+ -- Pattern: AABB overlap -> resolve on shallower axis -> bounce
221221+222222+ -- Obstacle 1
223223+ c_left := px - SIZE; c_right := px + SIZE;
224224+ c_top := py - SIZE; c_bot := py + SIZE;
225225+ if (c_right >= O1_L) and (c_left <= O1_R) and
226226+ (c_bot >= O1_T) and (c_top <= O1_B) then
227227+ if vy(9) = '0' then overlap_y := c_bot - O1_T;
228228+ else overlap_y := O1_B - c_top; end if;
229229+ if vx(9) = '0' then overlap_x := c_right - O1_L;
230230+ else overlap_x := O1_R - c_left; end if;
231231+ if overlap_y <= overlap_x then
232232+ if vy(9) = '0' then py := O1_T - SIZE; grounded := '1';
233233+ else py := O1_B + SIZE; end if;
234234+ bounced := '1';
235235+ vy := (not vy) + 1;
236236+ if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3));
237237+ elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then
238238+ vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if;
239239+ if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if;
240240+ if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if;
241241+ else
242242+ if vx(9) = '0' then px := O1_L - SIZE;
243243+ else px := O1_R + SIZE; end if;
244244+ bounced := '1'; bounce_wall := '1';
245245+ vx := (not vx) + 1;
246246+ if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3));
247247+ elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then
248248+ vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if;
249249+ end if;
250250+ end if;
251251+252252+ -- Obstacle 2
253253+ c_left := px - SIZE; c_right := px + SIZE;
254254+ c_top := py - SIZE; c_bot := py + SIZE;
255255+ if (c_right >= O2_L) and (c_left <= O2_R) and
256256+ (c_bot >= O2_T) and (c_top <= O2_B) then
257257+ if vy(9) = '0' then overlap_y := c_bot - O2_T;
258258+ else overlap_y := O2_B - c_top; end if;
259259+ if vx(9) = '0' then overlap_x := c_right - O2_L;
260260+ else overlap_x := O2_R - c_left; end if;
261261+ if overlap_y <= overlap_x then
262262+ if vy(9) = '0' then py := O2_T - SIZE; grounded := '1';
263263+ else py := O2_B + SIZE; end if;
264264+ bounced := '1';
265265+ vy := (not vy) + 1;
266266+ if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3));
267267+ elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then
268268+ vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if;
269269+ if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if;
270270+ if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if;
271271+ else
272272+ if vx(9) = '0' then px := O2_L - SIZE;
273273+ else px := O2_R + SIZE; end if;
274274+ bounced := '1'; bounce_wall := '1';
275275+ vx := (not vx) + 1;
276276+ if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3));
277277+ elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then
278278+ vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if;
279279+ end if;
280280+ end if;
281281+282282+ -- Obstacle 3
283283+ c_left := px - SIZE; c_right := px + SIZE;
284284+ c_top := py - SIZE; c_bot := py + SIZE;
285285+ if (c_right >= O3_L) and (c_left <= O3_R) and
286286+ (c_bot >= O3_T) and (c_top <= O3_B) then
287287+ if vy(9) = '0' then overlap_y := c_bot - O3_T;
288288+ else overlap_y := O3_B - c_top; end if;
289289+ if vx(9) = '0' then overlap_x := c_right - O3_L;
290290+ else overlap_x := O3_R - c_left; end if;
291291+ if overlap_y <= overlap_x then
292292+ if vy(9) = '0' then py := O3_T - SIZE; grounded := '1';
293293+ else py := O3_B + SIZE; end if;
294294+ bounced := '1';
295295+ vy := (not vy) + 1;
296296+ if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3));
297297+ elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then
298298+ vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if;
299299+ if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if;
300300+ if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if;
301301+ else
302302+ if vx(9) = '0' then px := O3_L - SIZE;
303303+ else px := O3_R + SIZE; end if;
304304+ bounced := '1'; bounce_wall := '1';
305305+ vx := (not vx) + 1;
306306+ if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3));
307307+ elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then
308308+ vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if;
309309+ end if;
310310+ end if;
311311+312312+ -- Obstacle 4
313313+ c_left := px - SIZE; c_right := px + SIZE;
314314+ c_top := py - SIZE; c_bot := py + SIZE;
315315+ if (c_right >= O4_L) and (c_left <= O4_R) and
316316+ (c_bot >= O4_T) and (c_top <= O4_B) then
317317+ if vy(9) = '0' then overlap_y := c_bot - O4_T;
318318+ else overlap_y := O4_B - c_top; end if;
319319+ if vx(9) = '0' then overlap_x := c_right - O4_L;
320320+ else overlap_x := O4_R - c_left; end if;
321321+ if overlap_y <= overlap_x then
322322+ if vy(9) = '0' then py := O4_T - SIZE; grounded := '1';
323323+ else py := O4_B + SIZE; end if;
324324+ bounced := '1';
325325+ vy := (not vy) + 1;
326326+ if vy(9) = '0' and vy > 1 then vy := vy - ("000" & vy(9 downto 3));
327327+ elsif vy(9) = '1' and vy < CONV_STD_LOGIC_VECTOR(1022, 10) then
328328+ vy := vy + ("000" & ((not vy(9 downto 3)) + 1)); end if;
329329+ if vy(9) = '0' and vy < 2 then vy := (others => '0'); end if;
330330+ if vy(9) = '1' and vy >= CONV_STD_LOGIC_VECTOR(1022, 10) then vy := (others => '0'); end if;
331331+ else
332332+ if vx(9) = '0' then px := O4_L - SIZE;
333333+ else px := O4_R + SIZE; end if;
334334+ bounced := '1'; bounce_wall := '1';
335335+ vx := (not vx) + 1;
336336+ if vx(9) = '0' and vx > 1 then vx := vx - ("000" & vx(9 downto 3));
337337+ elsif vx(9) = '1' and vx < CONV_STD_LOGIC_VECTOR(1022, 10) then
338338+ vx := vx + ("000" & ((not vx(9 downto 3)) + 1)); end if;
339339+ end if;
340340+ end if;
341341+342342+ -- == COMMIT ==
343343+ vel_x <= vx;
344344+ vel_y <= vy;
345345+ pos_x <= px;
346346+ pos_y <= py;
347347+348348+ -- Squish on meaningful impacts only
349349+ if bounced = '1' and bounce_speed > 3 then
350350+ if bounce_speed >= 8 then squish <= "1000";
351351+ else squish <= bounce_speed(3 downto 0); end if;
352352+ squish_h <= bounce_wall;
353353+ elsif squish > 0 then
354354+ squish <= squish - 1;
355355+ end if;
356356+357357+ if grounded = '1' then on_ground <= '1';
358358+ elsif py < GROUND - 1 then on_ground <= '0'; end if;
359359+360360+ end process physics;
361361+362362+end behavior;
+80
ps2_decoder.vhd
···11+-- ========================================================================
22+-- PS2 Keyboard Decoder
33+-- Receives serial data from a PS2 keyboard and outputs which
44+-- WASD keys are currently held down.
55+--
66+-- PS2 protocol: 11 bits per key event (start, 8 data LSB first, parity, stop)
77+-- Make code = key pressed, F0 + code = key released
88+-- Scan codes: W=1D, A=1C, S=1B, D=23
99+-- ========================================================================
1010+1111+library IEEE;
1212+use IEEE.STD_LOGIC_1164.all;
1313+use IEEE.STD_LOGIC_UNSIGNED.all;
1414+1515+entity ps2_decoder is
1616+ port(
1717+ ps2_clk : in std_logic; -- clock from keyboard
1818+ ps2_data : in std_logic; -- serial data from keyboard
1919+ key_w : out std_logic; -- '1' when W is held
2020+ key_a : out std_logic; -- '1' when A is held
2121+ key_s : out std_logic; -- '1' when S is held
2222+ key_d : out std_logic -- '1' when D is held
2323+ );
2424+end ps2_decoder;
2525+2626+architecture behavior of ps2_decoder is
2727+ signal shift_reg : std_logic_vector(10 downto 0);
2828+ signal bit_count : std_logic_vector(3 downto 0) := (others => '0');
2929+ signal break_flag : std_logic := '0';
3030+ signal scan_code : std_logic_vector(7 downto 0);
3131+begin
3232+3333+ -- Single process: shift in bits, decode when frame complete
3434+ ps2_receive : process(ps2_clk)
3535+ begin
3636+ if ps2_clk'event and ps2_clk = '0' then
3737+ -- Shift in new bit (MSB first into shift register)
3838+ shift_reg <= ps2_data & shift_reg(10 downto 1);
3939+4040+ if bit_count = "1010" then
4141+ -- 11th bit received (stop bit), frame complete
4242+ -- Data byte is in shift_reg(9 downto 2) at this point:
4343+ -- shift_reg(10) = parity (from previous edge)
4444+ -- shift_reg(9) = data bit 7
4545+ -- shift_reg(2) = data bit 0
4646+ -- shift_reg(1) = start bit
4747+ scan_code <= shift_reg(9 downto 2);
4848+4949+ -- Decode: F0 means next byte is a key release
5050+ if shift_reg(9 downto 2) = X"F0" then
5151+ break_flag <= '1';
5252+ elsif break_flag = '1' then
5353+ -- Key released
5454+ break_flag <= '0';
5555+ case shift_reg(9 downto 2) is
5656+ when X"1D" => key_w <= '0';
5757+ when X"1C" => key_a <= '0';
5858+ when X"1B" => key_s <= '0';
5959+ when X"23" => key_d <= '0';
6060+ when others => null;
6161+ end case;
6262+ else
6363+ -- Key pressed
6464+ case shift_reg(9 downto 2) is
6565+ when X"1D" => key_w <= '1';
6666+ when X"1C" => key_a <= '1';
6767+ when X"1B" => key_s <= '1';
6868+ when X"23" => key_d <= '1';
6969+ when others => null;
7070+ end case;
7171+ end if;
7272+7373+ bit_count <= (others => '0');
7474+ else
7575+ bit_count <= bit_count + 1;
7676+ end if;
7777+ end if;
7878+ end process ps2_receive;
7979+8080+end behavior;
+120
renderer.vhd
···11+-- ========================================================================
22+-- Renderer
33+-- Combinational logic: given the current pixel position and character
44+-- state, outputs the RGB color for that pixel.
55+--
66+-- Draws: character (red), 4 obstacles (yellow), ground/ceiling (green),
77+-- walls (cyan), sky (black)
88+-- ========================================================================
99+1010+library IEEE;
1111+use IEEE.STD_LOGIC_1164.all;
1212+use IEEE.STD_LOGIC_ARITH.all;
1313+use IEEE.STD_LOGIC_UNSIGNED.all;
1414+1515+entity renderer is
1616+ port(
1717+ pixel_row : in std_logic_vector(9 downto 0); -- from vga_sync
1818+ pixel_column : in std_logic_vector(9 downto 0); -- from vga_sync
1919+ char_x : in std_logic_vector(9 downto 0); -- from physics
2020+ char_y : in std_logic_vector(9 downto 0); -- from physics
2121+ char_width : in std_logic_vector(9 downto 0); -- from physics (animated)
2222+ char_height : in std_logic_vector(9 downto 0); -- from physics (animated)
2323+ red : out std_logic;
2424+ green : out std_logic;
2525+ blue : out std_logic
2626+ );
2727+end renderer;
2828+2929+architecture behavior of renderer is
3030+3131+ -- Screen boundaries
3232+ constant GROUND_TOP : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(448, 10);
3333+ constant CEIL_BOT : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(8, 10);
3434+ constant LEFT_WALL : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(8, 10);
3535+ constant RIGHT_WALL : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(631, 10);
3636+3737+ -- Obstacle positions (must match physics_engine)
3838+ constant O1_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(60, 10);
3939+ constant O1_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(370, 10);
4040+ constant O1_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(180, 10);
4141+ constant O1_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(386, 10);
4242+4343+ constant O2_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(250, 10);
4444+ constant O2_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(300, 10);
4545+ constant O2_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(390, 10);
4646+ constant O2_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(316, 10);
4747+4848+ constant O3_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(440, 10);
4949+ constant O3_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10);
5050+ constant O3_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(580, 10);
5151+ constant O3_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(216, 10);
5252+5353+ constant O4_L : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(140, 10);
5454+ constant O4_T : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(120, 10);
5555+ constant O4_R : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(200, 10);
5656+ constant O4_B : std_logic_vector(9 downto 0) := CONV_STD_LOGIC_VECTOR(150, 10);
5757+5858+ signal char_on, ground_on, wall_on, ceiling_on, obs_on : std_logic;
5959+6060+begin
6161+6262+ -- Check what the current pixel overlaps
6363+ render : process(pixel_row, pixel_column, char_x, char_y,
6464+ char_width, char_height)
6565+ begin
6666+ char_on <= '0';
6767+ ground_on <= '0';
6868+ wall_on <= '0';
6969+ ceiling_on <= '0';
7070+ obs_on <= '0';
7171+7272+ -- Character
7373+ if (pixel_column >= char_x - char_width) and
7474+ (pixel_column <= char_x + char_width) and
7575+ (pixel_row >= char_y - char_height) and
7676+ (pixel_row <= char_y + char_height) then
7777+ char_on <= '1';
7878+ end if;
7979+8080+ -- Ground
8181+ if pixel_row >= GROUND_TOP then ground_on <= '1'; end if;
8282+8383+ -- Ceiling
8484+ if pixel_row <= CEIL_BOT then ceiling_on <= '1'; end if;
8585+8686+ -- Walls
8787+ if (pixel_column < LEFT_WALL) or (pixel_column > RIGHT_WALL) then
8888+ wall_on <= '1';
8989+ end if;
9090+9191+ -- Obstacles
9292+ if (pixel_column >= O1_L) and (pixel_column <= O1_R) and
9393+ (pixel_row >= O1_T) and (pixel_row <= O1_B) then obs_on <= '1'; end if;
9494+ if (pixel_column >= O2_L) and (pixel_column <= O2_R) and
9595+ (pixel_row >= O2_T) and (pixel_row <= O2_B) then obs_on <= '1'; end if;
9696+ if (pixel_column >= O3_L) and (pixel_column <= O3_R) and
9797+ (pixel_row >= O3_T) and (pixel_row <= O3_B) then obs_on <= '1'; end if;
9898+ if (pixel_column >= O4_L) and (pixel_column <= O4_R) and
9999+ (pixel_row >= O4_T) and (pixel_row <= O4_B) then obs_on <= '1'; end if;
100100+ end process render;
101101+102102+ -- Priority color encoder
103103+ -- Character=Red(100), Obstacles=Yellow(110), Ground/Ceil=Green(010),
104104+ -- Walls=Green(010), Sky=Black(000)
105105+ color : process(char_on, ground_on, wall_on, ceiling_on, obs_on)
106106+ begin
107107+ if char_on = '1' then
108108+ red <= '1'; green <= '0'; blue <= '0';
109109+ elsif obs_on = '1' then
110110+ red <= '1'; green <= '1'; blue <= '0';
111111+ elsif ground_on = '1' or ceiling_on = '1' then
112112+ red <= '0'; green <= '1'; blue <= '0';
113113+ elsif wall_on = '1' then
114114+ red <= '0'; green <= '1'; blue <= '0';
115115+ else
116116+ red <= '0'; green <= '0'; blue <= '0';
117117+ end if;
118118+ end process color;
119119+120120+end behavior;