// Meteor - A Nintendo Gameboy Advance emulator // Copyright (C) 2009-2011 Philippe Daouadi // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "ameteor/lcd.hpp" #include "ameteor/io.hpp" #include "globals.hpp" #include "ameteor.hpp" #include "debug.hpp" namespace AMeteor { Lcd::Lcd () : m_screen(MEM, IO) { Reset(); } void Lcd::Reset () { CLOCK.AddLcd(960); // call me at first H-Blank } void Lcd::TimeEvent () { uint16_t& dispstat = IO.GetRef16(Io::DISPSTAT); uint16_t& vcount = IO.GetRef16(Io::VCOUNT); if (dispstat & 0x2) // if we were H-Blanking { //debug("hblank end"); // we are not anymore, we're on next line dispstat ^= 0x2; // call me when we are H-Blanking again CLOCK.AddLcd(960); // we have finished drawing a line, do our stuff... if (vcount == 227) // this was the last line { vcount = 0; // we're now at first // we reload the reference points m_screen.UpdateBg2RefX(IO.DRead32(Io::BG2X_L)); m_screen.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L)); m_screen.UpdateBg3RefX(IO.DRead32(Io::BG3X_L)); m_screen.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L)); // and we draw the line 0 m_screen.DrawLine(0); // FIXME see below, vblank finished dispstat ^= 0x1; } else { ++vcount; // we're on next line if (vcount < 160) // we draw normally m_screen.DrawLine(vcount); else if (vcount == 160) // We enter V-Blank { dispstat |= 0x1; if (dispstat & (0x1 << 3)) // if V-Blank irq is enabled CPU.SendInterrupt(0x1); DMA.CheckAll(Dma::VBlank); KEYPAD.VBlank(); // we send the vblank signal //sig_vblank.emit(); } // NOTE : v-blank finishes on line 227, not 0 // FIXME on vba, it finishes on 0 //if (vcount == 227) // V-Blank finished //dispstat ^= 0x1; } // check for vcount match if (vcount == ((dispstat >> 8) & 0xFF)) // vcount match { dispstat |= 0x4; // enable vcount match bit if (dispstat & (0x1 << 5)) // if V-Counter irq is enabled CPU.SendInterrupt(0x4); } else // no vcount match dispstat &= ~(uint16_t)0x4; // scanline callback for frontend if (slcallbackline == vcount) scanlinecallback_bizhawk(); } else // if we were not H-Blanking { //debug("hblank vcount : " << std::dec << vcount); // now, we are dispstat |= 0x2; // call me when we are not H-Blanking anymore CLOCK.AddLcd(272); // NOTE : H-Blank interrupts are not generated during V-Blank // FIXME vba generates hblank interrupts even during vblank // if H-Blank irq is enabled //and we're not in V-Blank if ((dispstat & 0x10) == 0x10) CPU.SendInterrupt(0x2); // NOTE : hblank DMAs are not triggered during vblank // (seen on vba) if (!(dispstat & 0x1)) // if we're not vblanking DMA.CheckAll(Dma::HBlank); } } bool Lcd::SaveState (std::ostream& stream) { if (!m_screen.SaveState(stream)) return false; return true; } bool Lcd::LoadState (std::istream& stream) { if (!m_screen.LoadState(stream)) return false; UpdateDispCnt (IO.DRead16(Io::DISPCNT)); UpdateBg0Cnt (IO.DRead16(Io::BG0CNT)); UpdateBg1Cnt (IO.DRead16(Io::BG1CNT)); UpdateBg2Cnt (IO.DRead16(Io::BG2CNT)); UpdateBg3Cnt (IO.DRead16(Io::BG3CNT)); UpdateBg0XOff (IO.DRead16(Io::BG0HOFS)); UpdateBg0YOff (IO.DRead16(Io::BG0VOFS)); UpdateBg1XOff (IO.DRead16(Io::BG1HOFS)); UpdateBg1YOff (IO.DRead16(Io::BG1VOFS)); UpdateBg2XOff (IO.DRead16(Io::BG2HOFS)); UpdateBg2YOff (IO.DRead16(Io::BG2VOFS)); UpdateBg3XOff (IO.DRead16(Io::BG3HOFS)); UpdateBg3YOff (IO.DRead16(Io::BG3VOFS)); OamWrite (0x07000000, 0x07000400); return true; } }