Path: blob/master/libgambatte/src/video/sprite_mapper.cpp
2 views
/***************************************************************************1* Copyright (C) 2007 by Sindre AamÄs *2* [email protected] *3* *4* This program is free software; you can redistribute it and/or modify *5* it under the terms of the GNU General Public License version 2 as *6* published by the Free Software Foundation. *7* *8* This program is distributed in the hope that it will be useful, *9* but WITHOUT ANY WARRANTY; without even the implied warranty of *10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *11* GNU General Public License version 2 for more details. *12* *13* You should have received a copy of the GNU General Public License *14* version 2 along with this program; if not, write to the *15* Free Software Foundation, Inc., *16* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *17***************************************************************************/18#include "sprite_mapper.h"19#include "counterdef.h"20#include "next_m0_time.h"21#include "../insertion_sort.h"22#include <cstring>23#include <algorithm>2425namespace gambatte {2627SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)28: lyCounter(lyCounter), oamram(oamram), cgb_(false) {29reset(oamram, false);30}3132void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const bool cgb) {33this->oamram = oamram;34this->cgb_ = cgb;35setLargeSpritesSrc(false);36lu = 0;37lastChange = 0xFF;38std::fill_n(szbuf, 40, largeSpritesSrc);3940unsigned pos = 0;41unsigned distance = 80;4243while (distance--) {44buf[pos] = oamram[((pos * 2) & ~3) | (pos & 1)];45++pos;46}47}4849static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {50unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;5152if (lc >= 456)53lc -= 456;5455return lc;56}5758void SpriteMapper::OamReader::update(const unsigned long cc) {59if (cc > lu) {60if (changed()) {61const unsigned lulc = toPosCycles(lu, lyCounter);6263unsigned pos = std::min(lulc, 80u);64unsigned distance = 80;6566if ((cc - lu) >> lyCounter.isDoubleSpeed() < 456) {67const unsigned cclc = toPosCycles(cc, lyCounter);6869distance = std::min(cclc, 80u) - pos + (cclc < lulc ? 80 : 0);70}7172{73const unsigned targetDistance = lastChange - pos + (lastChange <= pos ? 80 : 0);7475if (targetDistance <= distance) {76distance = targetDistance;77lastChange = 0xFF;78}79}8081while (distance--) {82if (!(pos & 1)) {83if (pos == 80)84pos = 0;8586if (cgb_)87szbuf[pos >> 1] = largeSpritesSrc;8889buf[pos ] = oamram[pos * 2 ];90buf[pos + 1] = oamram[pos * 2 + 1];91} else92szbuf[pos >> 1] = (szbuf[pos >> 1] & cgb_) | largeSpritesSrc;9394++pos;95}96}9798lu = cc;99}100}101102void SpriteMapper::OamReader::change(const unsigned long cc) {103update(cc);104lastChange = std::min(toPosCycles(lu, lyCounter), 80u);105}106107void SpriteMapper::OamReader::setStatePtrs(SaveState &state) {108state.ppu.oamReaderBuf.set(buf, sizeof buf);109state.ppu.oamReaderSzbuf.set(szbuf, sizeof(szbuf) / sizeof(bool));110}111112void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char *const oamram) {113this->oamram = oamram;114largeSpritesSrc = ss.mem.ioamhram.get()[0x140] >> 2 & 1;115lu = ss.ppu.enableDisplayM0Time;116change(lu);117}118119SYNCFUNC(SpriteMapper::OamReader)120{121NSS(buf);122NSS(szbuf);123124NSS(lu);125NSS(lastChange);126NSS(largeSpritesSrc);127NSS(cgb_);128}129130void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {131std::memset(buf, 0x00, sizeof(buf));132std::fill(szbuf, szbuf + 40, false);133lu = cc + (80 << lyCounter.isDoubleSpeed());134lastChange = 80;135}136137SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,138const LyCounter &lyCounter,139const unsigned char *const oamram) :140nextM0Time_(nextM0Time),141oamReader(lyCounter, oamram)142{143clearMap();144}145146void SpriteMapper::reset(const unsigned char *const oamram, const bool cgb) {147oamReader.reset(oamram, cgb);148clearMap();149}150151void SpriteMapper::clearMap() {152std::memset(num, NEED_SORTING_MASK, sizeof num);153}154155void SpriteMapper::mapSprites() {156clearMap();157158for (unsigned i = 0x00; i < 0x50; i += 2) {159const int spriteHeight = 8 << largeSprites(i >> 1);160const unsigned bottom_pos = posbuf()[i] - (17u - spriteHeight);161162if (bottom_pos < 143u + spriteHeight) {163const unsigned startly = static_cast<int>(bottom_pos) + 1 - spriteHeight >= 0164? static_cast<int>(bottom_pos) + 1 - spriteHeight : 0;165unsigned char *map = spritemap + startly * 10;166unsigned char *n = num + startly;167unsigned char *const nend = num + (bottom_pos < 143 ? bottom_pos : 143) + 1;168169do {170if (*n < NEED_SORTING_MASK + 10)171map[(*n)++ - NEED_SORTING_MASK] = i;172173map += 10;174} while (++n != nend);175}176}177178nextM0Time_.invalidatePredictedNextM0Time();179}180181void SpriteMapper::sortLine(const unsigned ly) const {182num[ly] &= ~NEED_SORTING_MASK;183insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));184}185186unsigned long SpriteMapper::doEvent(const unsigned long time) {187oamReader.update(time);188mapSprites();189return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);190}191192SYNCFUNC(SpriteMapper)193{194NSS(spritemap);195NSS(num);196197SSS(nextM0Time_);198SSS(oamReader);199}200201}202203204