@@ -96,7 +96,9 @@ void PPU::tick(uint8_t cycles) {
9696 current_ly++;
9797
9898 if (current_ly > 153 ) {
99- current_ly = 0 ; // Reset to start of next frame
99+ // Reset to start of next frame
100+ current_ly = 0 ;
101+ window_line_counter = 0 ;
100102 mode = 2 ;
101103 }
102104 }
@@ -142,6 +144,9 @@ void PPU::draw_scanline() {
142144 uint8_t bgp = mmu->read_byte (0xFF47 );
143145 uint32_t shades[] = { 0xFFFFFFFF , 0xFFAAAAAA , 0xFF555555 , 0xFF000000 };
144146
147+ // Sprite priority check
148+ uint8_t bg_color_ids[160 ] = {0 };
149+
145150 // Check master bg/window enable bit (LCDC bit 0)
146151 if (!(lcdc & 0x01 )) {
147152 // Fill scanline with white (color 0)
@@ -154,6 +159,7 @@ void PPU::draw_scanline() {
154159 uint8_t wy = mmu->read_byte (0xFF4A );
155160 uint8_t wx = mmu->read_byte (0xFF4B ) - 7 ;
156161 bool window_enabled = (lcdc & 0x20 ) && (ly >= wy);
162+ bool window_drawn = false ;
157163
158164 // Get scroll (x and y) pos
159165 uint8_t scy = mmu->read_byte (0xFF42 );
@@ -167,7 +173,8 @@ void PPU::draw_scanline() {
167173 if (window_enabled && px >= wx) {
168174 map_base = (lcdc & 0x40 ) ? 0x9C00 : 0x9800 ; // LCDC bit 6
169175 t_x = px - wx;
170- t_y = ly - wy;
176+ t_y = window_line_counter;
177+ window_drawn = true ;
171178 } else {
172179 map_base = (lcdc & 0x08 ) ? 0x9C00 : 0x9800 ; // LCDC bit 3
173180 t_x = px + scx;
@@ -181,6 +188,7 @@ void PPU::draw_scanline() {
181188 // Tile data addressing
182189 bool is_unsigned = (lcdc & 0x10 );
183190 uint16_t tile_data_addr;
191+
184192 if (is_unsigned) {
185193 tile_data_addr = 0x8000 + (tile_index * 16 );
186194 } else {
@@ -196,35 +204,69 @@ void PPU::draw_scanline() {
196204 int bit = 7 - (t_x % 8 );
197205 uint8_t color_id = ((b2 >> bit) & 0x01 ) << 1 | ((b1 >> bit) & 0x01 );
198206
207+ bg_color_ids[px] = color_id;
208+
199209 // Apply palette and write to framebuffer
200210 uint8_t palette_color = (bgp >> (color_id * 2 )) & 0x03 ;
201211 framebuffer[ly * 160 + px] = shades[palette_color];
202212 }
213+
214+ if (window_drawn) {
215+ window_line_counter++;
216+ }
203217 }
204218
205- // Draw sprite(s) on top of background
219+ // Draw sprite(s) (10 max per line) on top of background
206220 if (lcdc & 0x02 ) {
221+ int sprites_on_line = 0 ;
222+ uint8_t used_sprite_x_coords[160 ] = {};
207223 for (int i = 0 ; i < 40 ; i++) {
208224 uint16_t oam_addr = 0xFE00 + (i * 4 );
225+ if (sprites_on_line >= 10 ) break ;
209226
210227 // Get proper sprite attributes
211228 uint8_t sprite_y = mmu->read_byte (oam_addr) - 16 ;
212229 uint8_t sprite_x = mmu->read_byte (oam_addr + 1 ) - 8 ;
213230 uint8_t tile_index = mmu->read_byte (oam_addr + 2 );
214231 uint8_t attributes = mmu->read_byte (oam_addr + 3 );
232+ uint8_t sprite_height = (lcdc & 0x04 ) ? 16 : 8 ;
215233
216234 // Check if sprite is visible on this scanline (ly)
217- if (ly >= sprite_y && ly < sprite_y + 8 ) {
235+ if (ly >= sprite_y && ly < sprite_y + sprite_height) {
236+ sprites_on_line++;
237+
238+ // Check if sprite at this X coordinate has already been used (priority)
239+ if (sprite_x >= 0 && sprite_x < 160 ) {
240+ if (used_sprite_x_coords[sprite_x]) {
241+ continue ;
242+ } else {
243+ used_sprite_x_coords[sprite_x] = 1 ;
244+ }
245+ }
246+
218247 // Determine which palette to use (Bit 4: 0=OBP0, 1=OBP1)
219248 uint8_t obp = mmu->read_byte ((attributes & 0x10 ) ? 0xFF49 : 0xFF48 );
220249
221250 // Fetch tile data (Sprites always use 0x8000-0x8FFF unsigned mode)
222- uint16_t tile_addr = 0x8000 + (tile_index * 16 ) ;
251+ uint16_t tile_addr;
223252 uint8_t line = (ly - sprite_y);
224-
253+
225254 // Handle vertical flip (Bit 6)
226- if (attributes & 0x40 ) line = 7 - line;
227-
255+ if (attributes & 0x40 ) line = (sprite_height - 1 ) - line;
256+
257+ if (sprite_height == 16 ) {
258+ // For 8x16 sprites, bit 0 of the tile index is ignored for the base
259+ uint8_t base_tile = tile_index & 0xFE ;
260+ uint8_t actual_tile = (line < 8 ) ? base_tile : (base_tile | 0x01 );
261+
262+ tile_addr = 0x8000 + (actual_tile * 16 );
263+ // Reset line to 0-7 for the specific 8x8 tile selected
264+ line %= 8 ;
265+ } else {
266+ // Standard 8x8 mode
267+ tile_addr = 0x8000 + (tile_index * 16 );
268+ }
269+
228270 uint8_t b1 = mmu->read_byte (tile_addr + (line * 2 ));
229271 uint8_t b2 = mmu->read_byte (tile_addr + (line * 2 ) + 1 );
230272
@@ -238,8 +280,14 @@ void PPU::draw_scanline() {
238280
239281 // Don't draw transparent pixels (color 0)
240282 if (color_id != 0 ) {
241- uint8_t palette_color = (obp >> (color_id * 2 )) & 0x03 ;
242- framebuffer[ly * 160 + pixel_x] = shades[palette_color];
283+ // OBJ-to-BG priority (OAM bit 7)
284+ uint8_t bg_id = bg_color_ids[pixel_x];
285+ bool bg_over_obj = (attributes & 0x80 ) != 0 ;
286+
287+ if (!bg_over_obj || (bg_over_obj && bg_id == 0 )) {
288+ uint8_t palette_color = (obp >> (color_id * 2 )) & 0x03 ;
289+ framebuffer[ly * 160 + pixel_x] = shades[palette_color];
290+ }
243291 }
244292 }
245293 }
0 commit comments