aboutsummaryrefslogblamecommitdiff
path: root/src/ppu.rs
blob: fc1c09ab48071117c49f928656dc1bb7e593cf77 (plain) (tree)














































                                                   

                          




































































































                                                                                
                                                           



                           
                                                           













                                                                          
                                     


                                                            


                                                


























































                                                                                         
                                


                                                                               

                          
                                                                 
                                                                                  
                          

                                                                       

                                                                           

                                   








                                                       









                                                                                  













                                              

                                                         


































































                                                                                                 
use memory::VMem;
use core::intrinsics::transmute;

pub trait Screen {
    fn put(&mut self, x: u8, y: u8, color: u8);
    fn render(&self);
}

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct Sprite {
    y: u8,      /* is the (actualy y) - 1 */
    tile: u8,
    attr: u8,
    x: u8
}

struct PPU<'a, 'b> {
    /* internal srams */
    nametable_ram: [u8; 2048],
    palette_ram: [u8; 32],
    scanline: u16,
    /* registers */
    ppuctl: u8,
    ppumask: u8,
    ppustatus: u8,
    
    x: u8, /* fine x scroll */
    v: u16, /* current vram addr */
    t: u16, /* temporary vram addr */
    w: bool, /* first/second write toggle */
    vblank: bool,
    cycle: u16, /* cycle in the current scanline */
    /* rendering regs & latches */
        /* background registers */
    bg_bitmap: [u16; 2],
    bg_palette: [u8; 2],
        /* background latches */
    bg_nt: u8,
    bg_attr: u8,
    bg_bit_low: u8,
    bg_bit_high: u8,
    /* sprites */
    oam: [Sprite; 64],
    oam2: [Sprite; 8],
    sp_bitmap: [[u8; 2]; 8],
    sp_cnt: [u8; 8],
    sp_zero_insight: bool,
    /* IO */
    mem: &'a mut VMem,
    scr: &'b mut Screen
}

impl<'a, 'b> PPU<'a, 'b> {
    #[inline(always)] fn get_spritesize(&self) -> u8 {(self.ppuctl >> 5) & 1}

    #[inline(always)]
    fn fetch_nametable_byte(&mut self) {
        self.bg_nt = self.mem.read(0x2000 | (self.v & 0x0fff));
    }

    #[inline(always)]
    fn fetch_attrtable_byte(&mut self) {
        let v = self.v;
        /* the byte representing 4x4 tiles */
        let b = self.mem.read(0x23c0 | (v & 0x0c00) |
                            ((v >> 4) & 0x38) | ((v >> 2) & 0x07));
        self.bg_attr = (b >> ((v & 2) | ((v & 0x40) >> 4))) & 3;
    }

    #[inline(always)]
    fn fetch_low_bgtile_byte(&mut self) {
                                        /* 0x?000 */
        self.bg_bit_low = self.mem.read(((self.ppuctl as u16 & 0x10) << 8) |
                                        /* 0x-??0 */
                                        ((self.bg_nt as u16) << 4) |
                                        /* 0x---? (0 - 7) */
                                        (self.v >> 12) | 0x0);
    }

    #[inline(always)]
    fn fetch_high_bgtile_byte(&mut self) {
                                        /* 0x?000 */
        self.bg_bit_high = self.mem.read(((self.ppuctl as u16 & 0x10) << 8) |
                                        /* 0x-??0 */
                                        ((self.bg_nt as u16) << 4) |
                                        /* 0x---? (8 - f) */
                                        (self.v >> 12) | 0x8);
    }

    fn load_bgtile(&mut self) {
        /* load the tile bitmap to high 8 bits of bitmap,
         * assume the high 8 bits are zeros */
        assert!(self.bg_bitmap[0] >> 8 == 0 &&
                self.bg_bitmap[1] >> 8 == 0);
        self.bg_bitmap[0] |= (self.bg_bit_low as u16) << 8;
        self.bg_bitmap[1] |= (self.bg_bit_high as u16) << 8;
        self.bg_palette[0] |= (self.bg_attr & 1) * 0xff;
        self.bg_palette[1] |= ((self.bg_attr >> 1) & 1) * 0xff;
    }

    #[inline(always)]
    fn shift_sprites(&mut self) {
        for (i, c) in self.sp_cnt.iter_mut().enumerate() {
            let c0 = *c;
            match c0 {
                0 => {
                    self.sp_bitmap[i][0] >>= 1;
                    self.sp_bitmap[i][1] >>= 1;
                },
                _ => *c = c0 - 1
            }
        }
    }

    #[inline(always)]
    fn shift_bgtile(&mut self, d: u8) {
        self.bg_bitmap[0] >>= d;
        self.bg_bitmap[1] >>= d;
        self.bg_palette[0] >>= d;
        self.bg_palette[1] >>= d;
    }

    fn wrapping_inc_cx(&mut self) {
        match self.v & 0x001f {
            31 => {
                self.v &= !0x001fu16; /* reset coarse x */
                self.v ^= 0x0400;     /* switch horizontal nametable */
            }
            _ => self.v += 1
        }
    }

    fn wrapping_inc_y(&mut self) {
        match (self.v & 0x7000) == 0x7000 {
            false => self.v += 0x1000, /* fine y < 7 */
            true => {
                self.v &= !0x7000u16;  /* fine y <- 0 */
                self.v = (self.v & !0x03e0u16) |
                    (match (self.v & 0x03e0) >> 5 {
                        29 => {self.v ^= 0x0800; 0}, /* at bottom of scanline */
                        31 => 0,                     /* do not switch nt */
                        y => y + 1
                    }) << 5;
            }
        }
    }

    #[inline(always)]
    fn reset_cx(&mut self) {
        self.v = (self.v & !0x041fu16) | (self.t & 0x041f);
    }

    #[inline(always)]
    fn reset_y(&mut self) {
        self.v = (self.v & !0x7be0u16) | (self.t & 0x7be0);
    }

    fn clear_sprite(&mut self) {
        self.oam2 = [Sprite{y: 0xff, tile: 0xff, attr: 0xff, x: 0xff}; 8];
    }

    fn eval_sprite(&mut self) {
        /* we use scanline here because s.y is the (actual y) - 1 */
        let mut nidx = 0;
        let mut n = 0;
        let h =