BizHawk/libmeteor/source/lcd.cpp

152 lines
4.2 KiB
C++

// 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 <http://www.gnu.org/licenses/>.
#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;
}
}