<commit_before>use memory::MemSegment;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AddrByte {
    High,
    Low,
}

pub struct PPUCtrl {
    bits: u8,
}
impl PPUCtrl {
    pub fn empty() -> PPUCtrl {
        PPUCtrl { bits: 0 }
    }

    pub fn new(bits: u8) -> PPUCtrl {
        PPUCtrl { bits: bits }
    }

    pub fn nametable_num(&self) -> u16 {
        (self.bits & 0b0000_0011) as u16
    }

    pub fn vram_addr_step(&self) -> u16 {
        if self.bits & 0b0000_0100 != 0 {
            32
        } else {
            1
        }
    }

    pub fn background_table(&self) -> u16 {
        if self.bits & 0b0001_0000 != 0 {
            0x1000
        } else {
            0x0000
        }
    }

    pub fn sprite_table(&self) -> u16 {
        if self.bits & 0b0010_0000 != 0 {
            0x1000
        } else {
            0x0000
        }
    }

    pub fn generate_vblank_nmi(&self) -> bool {
        self.bits & 0b1000_0000 != 0
    }
}

bitflags! {
    flags PPUMask : u8 {
        const GREY =    0b0000_0001, //Greyscale
        const S_BCK_L = 0b0000_0010, //Show background in the leftmost 8 pixels
        const S_SPR_L = 0b0000_0100, //Show sprites in the leftmost 8 pixels
        const S_BCK =   0b0000_1000, //Show background
        const S_SPR =   0b0001_0000, //Show sprites
        const EM_R =    0b0010_0000, //Emphasize Red
        const EM_G =    0b0100_0000, //Emphasize Green
        const EM_B =    0b1000_0000, //Emphasize Blue
    }
}

bitflags! {
    flags PPUStat : u8 {
        const VBLANK =          0b1000_0000, //Currently in the vertical blank interval
        const SPRITE_0 =        0b0100_0000, //Sprite 0 hit
        const SPRITE_OVERFLOW = 0b0010_0000, //Greater than 8 sprites on current scanline
    }
}

pub struct PPUReg {
    pub ppuctrl: PPUCtrl,
    pub ppumask: PPUMask,
    pub ppustat: PPUStat,
    pub oamaddr: u8,
    ppuscroll: u16,
    pub ppuaddr: u16,

    ///A fake dynamic latch representing the capacitance of the wires in the
    ///PPU that we have to emulate.
    dyn_latch: u8,

    ///The address registers are two bytes but we can only write one at a time.
    address_latch: AddrByte,
}

impl PPUReg {
    pub fn scroll_x(&self) -> u16 {
        (self.ppuscroll & 0xFF00) >> 8
    }

    pub fn scroll_x_fine(&self) -> u16 {
        self.scroll_x() & 0x07
    }

    pub fn scroll_x_coarse(&self) -> u16 {
        self.scroll_x() >> 3
    }

    pub fn scroll_y(&self) -> u16 {
        (self.ppuscroll & 0x00FF) >> 0
    }

    pub fn scroll_y_coarse(&self) -> u16 {
        self.scroll_y() >> 3
    }

    pub fn incr_ppuaddr(&mut self) {
        let incr_size = self.ppuctrl.vram_addr_step();
        self.ppuaddr = self.ppuaddr.wrapping_add(incr_size);
    }

    pub fn incr_oamaddr(&mut self) {
        self.oamaddr = self.oamaddr.wrapping_add(1);
    }
}

fn write_addr_byte(latch: &mut AddrByte, target: &mut u16, val: u8) {
    match *latch {
        AddrByte::High => {
            *target = (*target & 0x00FF) | ((val as u16) << 8);
            *latch = AddrByte::Low;
        }
        AddrByte::Low => {
            *target = (*target & 0xFF00) | ((val as u16) << 0);
            *latch = AddrByte::High;
        }
    }
}

impl Default for PPUReg {
    fn default() -> PPUReg {
        PPUReg {
            ppuctrl: PPUCtrl::empty(),
            ppumask: PPUMask::empty(),
            ppustat: PPUStat::empty(),
            oamaddr: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            dyn_latch: 0,
            address_latch: AddrByte::High,
        }
    }
}

impl MemSegment for PPUReg {
    fn read(&mut self, idx: u16) -> u8 {
        match idx % 8 {
            0x0000 => self.dyn_latch,
            0x0001 => self.dyn_latch,
            0x0002 => {
                self.address_latch = AddrByte::High;
                let res = self.ppustat.bits | (self.dyn_latch & 0b0001_1111);
                self.ppustat.remove(VBLANK);
                res
            }
            0x0003 => self.dyn_latch,
            0x0005 => self.dyn_latch,
            0x0006 => self.dyn_latch,
            _ => invalid_address!(idx),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        self.dyn_latch = val;
        match idx % 8 {
            0x0000 => self.ppuctrl = PPUCtrl::new(val),
            0x0001 => self.ppumask = PPUMask::from_bits_truncate(val),
            0x0002 => (),
            0x0003 => self.oamaddr = val,
            0x0005 => write_addr_byte(&mut self.address_latch, &mut self.ppuscroll, val),
            0x0006 => write_addr_byte(&mut self.address_latch, &mut self.ppuaddr, val),
            _ => invalid_address!(idx),
        }
    }
}<commit_msg>Hacky and broken fix to SMB lockup.<commit_after>use memory::MemSegment;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AddrByte {
    High,
    Low,
}

pub struct PPUCtrl {
    bits: u8,
}
impl PPUCtrl {
    pub fn empty() -> PPUCtrl {
        PPUCtrl { bits: 0 }
    }

    pub fn new(bits: u8) -> PPUCtrl {
        PPUCtrl { bits: bits }
    }

    pub fn nametable_num(&self) -> u16 {
        (self.bits & 0b0000_0011) as u16
    }

    pub fn vram_addr_step(&self) -> u16 {
        if self.bits & 0b0000_0100 != 0 {
            32
        } else {
            1
        }
    }

    pub fn background_table(&self) -> u16 {
        if self.bits & 0b0001_0000 != 0 {
            0x1000
        } else {
            0x0000
        }
    }

    pub fn sprite_table(&self) -> u16 {
        if self.bits & 0b0010_0000 != 0 {
            0x1000
        } else {
            0x0000
        }
    }

    pub fn generate_vblank_nmi(&self) -> bool {
        self.bits & 0b1000_0000 != 0
    }
}

bitflags! {
    flags PPUMask : u8 {
        const GREY =    0b0000_0001, //Greyscale
        const S_BCK_L = 0b0000_0010, //Show background in the leftmost 8 pixels
        const S_SPR_L = 0b0000_0100, //Show sprites in the leftmost 8 pixels
        const S_BCK =   0b0000_1000, //Show background
        const S_SPR =   0b0001_0000, //Show sprites
        const EM_R =    0b0010_0000, //Emphasize Red
        const EM_G =    0b0100_0000, //Emphasize Green
        const EM_B =    0b1000_0000, //Emphasize Blue
    }
}

bitflags! {
    flags PPUStat : u8 {
        const VBLANK =          0b1000_0000, //Currently in the vertical blank interval
        const SPRITE_0 =        0b0100_0000, //Sprite 0 hit
        const SPRITE_OVERFLOW = 0b0010_0000, //Greater than 8 sprites on current scanline
    }
}

pub struct PPUReg {
    pub ppuctrl: PPUCtrl,
    pub ppumask: PPUMask,
    pub ppustat: PPUStat,
    pub oamaddr: u8,
    ppuscroll: u16,
    pub ppuaddr: u16,

    ///A fake dynamic latch representing the capacitance of the wires in the
    ///PPU that we have to emulate.
    dyn_latch: u8,

    ///The address registers are two bytes but we can only write one at a time.
    address_latch: AddrByte,
}

impl PPUReg {
    pub fn scroll_x(&self) -> u16 {
        (self.ppuscroll & 0xFF00) >> 8
    }

    pub fn scroll_x_fine(&self) -> u16 {
        self.scroll_x() & 0x07
    }

    pub fn scroll_x_coarse(&self) -> u16 {
        self.scroll_x() >> 3
    }

    pub fn scroll_y(&self) -> u16 {
        (self.ppuscroll & 0x00FF) >> 0
    }

    pub fn scroll_y_coarse(&self) -> u16 {
        self.scroll_y() >> 3
    }

    pub fn incr_ppuaddr(&mut self) {
        let incr_size = self.ppuctrl.vram_addr_step();
        self.ppuaddr = self.ppuaddr.wrapping_add(incr_size);
    }

    pub fn incr_oamaddr(&mut self) {
        self.oamaddr = self.oamaddr.wrapping_add(1);
    }
}

fn write_addr_byte(latch: &mut AddrByte, target: &mut u16, val: u8) {
    match *latch {
        AddrByte::High => {
            *target = (*target & 0x00FF) | ((val as u16) << 8);
            *latch = AddrByte::Low;
        }
        AddrByte::Low => {
            *target = (*target & 0xFF00) | ((val as u16) << 0);
            *latch = AddrByte::High;
        }
    }
}

impl Default for PPUReg {
    fn default() -> PPUReg {
        PPUReg {
            ppuctrl: PPUCtrl::empty(),
            ppumask: PPUMask::empty(),
            ppustat: PPUStat::empty(),
            oamaddr: 0,
            ppuscroll: 0,
            ppuaddr: 0,
            dyn_latch: 0,
            address_latch: AddrByte::High,
        }
    }
}

impl MemSegment for PPUReg {
    fn read(&mut self, idx: u16) -> u8 {
        match idx % 8 {
            0x0000 => self.dyn_latch,
            0x0001 => self.dyn_latch,
            0x0002 => {
                self.address_latch = AddrByte::High;
                let res = self.ppustat.bits | (self.dyn_latch & 0b0001_1111);
                self.ppustat.remove(VBLANK);
                res
            }
            0x0003 => self.dyn_latch,
            0x0005 => self.dyn_latch,
            0x0006 => self.dyn_latch,
            _ => invalid_address!(idx),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        self.dyn_latch = val;
        match idx % 8 {
            0x0000 => self.ppuctrl = PPUCtrl::new(val),
            0x0001 => self.ppumask = PPUMask::from_bits_truncate(val),
            0x0002 => (),
            0x0003 => self.oamaddr = val,
            0x0005 => write_addr_byte(&mut self.address_latch, &mut self.ppuscroll, val),
            0x0006 => {
                if self.address_latch == AddrByte::High {
                    self.ppuctrl = PPUCtrl::new(self.ppuctrl.bits & 0b1111_1110);
                }
                write_addr_byte(&mut self.address_latch, &mut self.ppuaddr, val)
            }
            _ => invalid_address!(idx),
        }
    }
}
{'fraction_non_alphanumeric': 0.11173708920187793, 'fraction_numerical': 0.06708398539384455, 'mean_word_length': 1.6964838255977497, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2532946', 'n_tokens_mistral': 3829, 'n_tokens_neox': 3340, 'n_words': 798}