From e8223c761516faf767079fab2aabbfd2e01a7901 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Fri, 1 Jul 2022 11:14:39 +0100
Subject: [PATCH] feat: initial implementation of draw line

---
 src/ppu.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 61 insertions(+), 6 deletions(-)

diff --git a/src/ppu.rs b/src/ppu.rs
index f50b080d..5c3193c1 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -17,7 +17,7 @@ pub const SCREEN_HEIGHT: usize = 154;
 /// all of the logic behind the graphics processing and presentation.
 /// Should store both the VRAM and HRAM together with the internal
 /// graphic related registers.
-/// Outputs the screen as a monochromatic 8 bit frame buffer.
+/// Outputs the screen as a RGBA 8 bit frame buffer.
 ///
 /// # Basic usage
 /// ```rust
@@ -25,9 +25,9 @@ pub const SCREEN_HEIGHT: usize = 154;
 /// ppu.tick();
 /// ```
 pub struct Ppu {
-    /// The 8 bit based monochromatic frame buffer with the
+    /// The 8 bit based RGBA frame buffer with the
     /// processed set of pixels ready to be displayed on screen.
-    pub frame_buffer: [u8; SCREEN_WIDTH * SCREEN_HEIGHT],
+    pub frame_buffer: [u8; SCREEN_WIDTH * SCREEN_HEIGHT * RGBA_SIZE],
     /// Video dedicated memory (VRAM) where both the tiles and
     /// the sprites are going to be stored.
     pub vram: [u8; VRAM_SIZE],
@@ -69,7 +69,7 @@ pub enum PpuMode {
 impl Ppu {
     pub fn new() -> Ppu {
         Ppu {
-            frame_buffer: [0u8; SCREEN_WIDTH * SCREEN_HEIGHT],
+            frame_buffer: [0u8; SCREEN_WIDTH * SCREEN_HEIGHT * RGBA_SIZE],
             vram: [0u8; VRAM_SIZE],
             hram: [0u8; HRAM_SIZE],
             tiles: [[[0u8; 8]; 8]; TILE_COUNT],
@@ -203,7 +203,62 @@ impl Ppu {
         }
     }
 
-    fn render_line(&self) {
-        //@todo implement the rendering of a line
+    fn render_line(&mut self) {
+        let mut map_offset: usize = if self.bg_map { 0x1c00 } else { 0x1800 };
+        map_offset += (((self.line + self.scy) & 0xff) >> 3) as usize;
+
+        // calculates the sprite line offset by using the SCX register
+        // shifted by 3 meaning as the tiles are 8x8
+        let mut line_offset: usize = (self.scx >> 3) as usize;
+
+        let y = ((self.scy + self.line) & 0x07) as usize;
+        let mut x = (self.scx & 0x07) as usize;
+
+        let mut frame_offset = self.line as usize * SCREEN_WIDTH * RGBA_SIZE;
+
+        let mut tile_index = self.vram[map_offset + line_offset] as usize;
+
+        // if the tile data set in use is #1, the
+        // indices are signed, calculates a real tile offset
+        if self.bg_tile && tile_index < 128 {
+            tile_index += 256;
+        }
+
+        for _index in 0..SCREEN_WIDTH {
+            // in case the end of tile width has been reached then
+            // a new tile must be retrieved for plotting
+            if x == 8 {
+                // resets the tile X position to the base value
+                // as a new tile is going to be drawn
+                x = 0;
+
+                // calculates the new line tile offset making sure that
+                // the maximum of 32 is not overflown
+                line_offset = (line_offset + 1) & 31;
+
+                // calculates the tile index nad makes sure the value
+                // takes into consideration the bg tile value
+                tile_index = self.vram[map_offset + line_offset] as usize;
+                if self.bg_tile && tile_index < 128 {
+                    tile_index += 256;
+                }
+            }
+
+            // obtains the current pixel data from the tile and
+            // re-maps it according to the current palette
+            let pixel = self.tiles[tile_index][y][x];
+            let color = self.palette[pixel as usize];
+
+            // set the color pixel in the frame buffer
+            self.frame_buffer[frame_offset] = color[0];
+            self.frame_buffer[frame_offset + 1] = color[1];
+            self.frame_buffer[frame_offset + 2] = color[2];
+            self.frame_buffer[frame_offset + 3] = color[3];
+
+            frame_offset += RGBA_SIZE;
+
+            // increments the current tile X position in drawing
+            x += 1;
+        }
     }
 }
-- 
GitLab