476 lines
10 KiB
C++
476 lines
10 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/graphics/bglayer.hpp"
|
|
#include "ameteor/graphics/screen.hpp"
|
|
#include "../debug.hpp"
|
|
|
|
namespace AMeteor
|
|
{
|
|
namespace Graphics
|
|
{
|
|
BgLayer::BgLayer (int8_t num, Memory& memory, Io& io, uint16_t* pPalette) :
|
|
m_memory(memory),
|
|
m_io(io),
|
|
m_num(num),
|
|
m_priority(0),
|
|
m_cnt(0),
|
|
m_xoff(0),
|
|
m_yoff(0),
|
|
m_tWidth(32),
|
|
m_tHeight(32),
|
|
m_rWidth(16),
|
|
m_rHeight(16),
|
|
m_mapAdd(0x06000000),
|
|
m_charAdd(0x06000000),
|
|
m_pPalette(pPalette)
|
|
{
|
|
}
|
|
|
|
BgLayer::~BgLayer ()
|
|
{
|
|
}
|
|
|
|
void BgLayer::DrawLine0 (uint8_t line, uint16_t* ptr)
|
|
{
|
|
uint8_t mosH;
|
|
if (m_cnt & (0x1 << 6))
|
|
{
|
|
mosH = m_io.DRead8(Io::MOSAIC);
|
|
uint8_t mosV = mosH >> 4;
|
|
mosH &= 0x0F;
|
|
|
|
++mosV;
|
|
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
|
|
++mosH;
|
|
|
|
line /= mosV;
|
|
line *= mosV;
|
|
}
|
|
else
|
|
mosH = 0;
|
|
|
|
uint16_t* pMap = (uint16_t*)m_memory.GetRealAddress(m_mapAdd), *pTile;
|
|
uint8_t* pChar = m_memory.GetRealAddress(m_charAdd), *tpChar;
|
|
uint8_t i = 0;
|
|
|
|
// theses are the coordinates on the layer from which we will draw a line
|
|
uint16_t xoff = m_xoff % (m_tWidth * 8);
|
|
uint16_t yoff = (m_yoff + line) % (m_tHeight * 8);
|
|
|
|
// theses are the coordinates on the tile of the pixels we are drawing
|
|
// NOTE : if the tile is horizontally flipped tileX = 8 - tileX, thus
|
|
// when we draw it is ALWAYS incremented no matter how the tile is
|
|
// flipped
|
|
// NOTE : tileY is NOT redefined if the tile is flipped vertically
|
|
int8_t tileX = xoff % 8, tileY;
|
|
bool flipH;
|
|
|
|
// sets pTile to the tile we must draw first
|
|
// +---+---+
|
|
// | 1 | 2 |
|
|
// +---+---+
|
|
// | 3 | 4 |
|
|
// +---+---+
|
|
if (yoff >= 256)
|
|
if (m_tWidth == 64)
|
|
if (xoff >= 256)
|
|
// zone 4
|
|
pTile = pMap + 32*32 + 32*32 + 32*32
|
|
+ 32 * ((yoff-256)/8) + (xoff-256)/8;
|
|
else
|
|
// zone 3 (large map)
|
|
pTile = pMap + 32*32 + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
|
|
else
|
|
// zone 3 (no large map)
|
|
pTile = pMap + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
|
|
else
|
|
if (xoff >= 256)
|
|
// zone 2
|
|
pTile = pMap + 32*32 + 32 * (yoff/8) + (xoff-256)/8;
|
|
else
|
|
// zone 1
|
|
pTile = pMap + 32 * (yoff/8) + xoff/8;
|
|
|
|
if (m_hicolor)
|
|
{
|
|
while (i < 240)
|
|
{
|
|
flipH = (bool)(*pTile & (0x1 << 10));
|
|
|
|
// if the tile is vertically fliped
|
|
if (*pTile & (0x1 << 11))
|
|
tileY = 7 - (yoff % 8);
|
|
else
|
|
tileY = yoff % 8;
|
|
|
|
if (flipH)
|
|
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX;
|
|
else
|
|
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + tileX;
|
|
|
|
while (tileX < 8)
|
|
{
|
|
if (mosH && i % mosH)
|
|
*ptr = ptr[-1];
|
|
else
|
|
{
|
|
if (*tpChar)
|
|
*ptr = m_pPalette[*tpChar] | 0x8000;
|
|
else
|
|
*ptr = 0x0;
|
|
}
|
|
if (flipH)
|
|
--tpChar;
|
|
else
|
|
++tpChar;
|
|
|
|
++ptr;
|
|
++tileX;
|
|
++i;
|
|
|
|
if (i == 240)
|
|
return;
|
|
}
|
|
tileX = 0;
|
|
++pTile;
|
|
|
|
// support for big maps and wrap around
|
|
if (!((pTile - pMap) % 32))
|
|
if (m_tWidth == 32)
|
|
pTile -= 32;
|
|
else
|
|
if (yoff >= 256 && pTile - pMap > 32*32 * 3
|
|
|| yoff < 256 && pTile - pMap > 32*32)
|
|
pTile -= 32*33;
|
|
else
|
|
pTile += 32*31;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint16_t* pPalette;
|
|
uint8_t colorInd;
|
|
|
|
// for each pixel of the line we are drawing
|
|
while (i < 240)
|
|
{
|
|
pPalette = m_pPalette + ((*pTile >> 12) & 0xF) * 16;
|
|
flipH = (bool)(*pTile & (0x1 << 10));
|
|
|
|
// if the tile is vertically fliped
|
|
if (*pTile & (0x1 << 11))
|
|
tileY = 7 - (yoff % 8);
|
|
else
|
|
tileY = yoff % 8;
|
|
|
|
if (flipH)
|
|
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX)/2;
|
|
else
|
|
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + tileX)/2;
|
|
|
|
// we draw until the end of the tile or the line
|
|
while (tileX < 8)
|
|
{
|
|
if (flipH)
|
|
if (tileX % 2)
|
|
{
|
|
colorInd = *tpChar & 0xF;
|
|
--tpChar;
|
|
}
|
|
else
|
|
colorInd = *tpChar >> 4;
|
|
else
|
|
if (tileX % 2)
|
|
{
|
|
colorInd = *tpChar >> 4;
|
|
++tpChar;
|
|
}
|
|
else
|
|
colorInd = *tpChar & 0xF;
|
|
|
|
if (mosH && i % mosH)
|
|
*ptr = ptr[-1];
|
|
else
|
|
{
|
|
if (colorInd)
|
|
*ptr = pPalette[colorInd] | 0x8000;
|
|
else
|
|
*ptr = 0x0;
|
|
}
|
|
|
|
++ptr;
|
|
++tileX;
|
|
++i;
|
|
|
|
// if this was the last pixel of the line
|
|
if (i == 240)
|
|
return;
|
|
}
|
|
|
|
// we have finished drawing a tile, so we go to the next tile
|
|
tileX = 0;
|
|
++pTile;
|
|
|
|
// support for big maps and wrap around
|
|
if (!((pTile - pMap) % 32))
|
|
if (m_tWidth == 32)
|
|
pTile -= 32;
|
|
else
|
|
if (yoff >= 256 && pTile - pMap > 32*32 * 3
|
|
|| yoff < 256 && pTile - pMap > 32*32)
|
|
pTile -= 32*33;
|
|
else
|
|
pTile += 32*31;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BgLayer::DrawLine2 (uint16_t* ptr,
|
|
int32_t curX, int32_t curY,
|
|
int16_t dx, int16_t dy)
|
|
{
|
|
if (!m_hicolor)
|
|
{
|
|
//FIXME is this possible ??
|
|
//this seems to be impossible, but we should not crash since some games
|
|
//do it
|
|
//puts("rotated layer with 16 colors");
|
|
//abort();
|
|
// XXX
|
|
//it seems now that we should not care about this flag, we draw in 256
|
|
//colors, that's all
|
|
//return;
|
|
}
|
|
int32_t intX, intY;
|
|
|
|
uint16_t colorInd;
|
|
|
|
uint8_t* pMap = (uint8_t*)m_memory.GetRealAddress(m_mapAdd);
|
|
uint8_t* pChar = m_memory.GetRealAddress(m_charAdd);
|
|
|
|
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
|
|
{
|
|
intX = curX >> 8;
|
|
intY = curY >> 8;
|
|
|
|
// if we are off layer
|
|
if (intX < 0 || intX >= m_rWidth*8)
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
// NOTE : in C++, the modulus can be negative, this is because in
|
|
// x86, the IDIV instruction gives a remainder of the same sign of
|
|
// the dividend
|
|
curX %= m_rWidth*8 << 8;
|
|
if (curX < 0)
|
|
curX += m_rWidth*8 << 8;
|
|
intX = curX >> 8;
|
|
}
|
|
else
|
|
continue;
|
|
if (intY < 0 || intY >= m_rHeight*8)
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
curY %= m_rHeight*8 << 8;
|
|
if (curY < 0)
|
|
curY += m_rHeight*8 << 8;
|
|
intY = curY >> 8;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
colorInd = pChar[pMap[intY / 8 * m_rWidth + intX / 8] * 8 * 8
|
|
+ (intY % 8) * 8 + intX % 8];
|
|
|
|
if (colorInd)
|
|
*ptr = m_pPalette[colorInd] | 0x8000;
|
|
else
|
|
*ptr = 0x0;
|
|
}
|
|
}
|
|
|
|
void BgLayer::DrawLine3 (uint16_t* ptr,
|
|
int32_t curX, int32_t curY,
|
|
int16_t dx, int16_t dy)
|
|
{
|
|
int32_t intX, intY;
|
|
|
|
uint16_t* pChar = (uint16_t*) m_memory.GetRealAddress(0x06000000);
|
|
|
|
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
|
|
{
|
|
intX = curX >> 8;
|
|
intY = curY >> 8;
|
|
|
|
// if we are off layer
|
|
if (intX < 0 || intX >= 240)
|
|
/*
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
// NOTE : in C++, the modulus can be negative
|
|
intX %= 240;
|
|
if (intX < 0)
|
|
intX += 240;
|
|
}
|
|
else*/
|
|
continue;
|
|
if (intY < 0 || intY >= 160)
|
|
/*
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
intY %= 160;
|
|
if (intY < 0)
|
|
intY += 160;
|
|
}
|
|
else*/
|
|
continue;
|
|
|
|
*ptr = pChar[intY * 240 + intX] | 0x8000;
|
|
}
|
|
}
|
|
|
|
void BgLayer::DrawLine4 (uint8_t line, uint16_t* ptr,
|
|
int32_t curX, int32_t curY,
|
|
int16_t dx, int16_t dmx, int16_t dy, int16_t dmy, bool frame1)
|
|
{
|
|
uint8_t mosH;
|
|
if (m_cnt & (0x1 << 6))
|
|
{
|
|
// TODO horizontal mosaic not implemented
|
|
mosH = m_io.DRead8(Io::MOSAIC);
|
|
uint8_t mosV = mosH >> 4;
|
|
mosH &= 0x0F;
|
|
|
|
++mosV;
|
|
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
|
|
++mosH;
|
|
|
|
uint8_t back = line % mosV;
|
|
|
|
curX -= back*dmx;
|
|
curY -= back*dmy;
|
|
}
|
|
else
|
|
mosH = 0;
|
|
|
|
int32_t intX, intY;
|
|
|
|
uint16_t colorInd;
|
|
|
|
uint8_t* pChar =
|
|
m_memory.GetRealAddress(frame1 ? 0x0600A000 : 0x06000000);
|
|
|
|
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
|
|
{
|
|
if (mosH && x % mosH)
|
|
{
|
|
*ptr = ptr[-1];
|
|
continue;
|
|
}
|
|
|
|
intX = curX >> 8;
|
|
intY = curY >> 8;
|
|
|
|
// if we are off layer
|
|
if (intX < 0 || intX >= 240)
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
// NOTE : in C++, the modulus can be negative
|
|
intX %= 240;
|
|
if (intX < 0)
|
|
intX += 240;
|
|
}
|
|
else
|
|
continue;
|
|
if (intY < 0 || intY >= 160)
|
|
if (m_cnt & (0x1 << 13))
|
|
{
|
|
intY %= 160;
|
|
if (intY < 0)
|
|
intY += 160;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
colorInd = pChar[intY * 240 + intX];
|
|
|
|
if (colorInd)
|
|
*ptr = m_pPalette[colorInd] | 0x8000;
|
|
else
|
|
*ptr = 0x0;
|
|
}
|
|
}
|
|
|
|
void BgLayer::DrawLine5 (uint16_t* ptr,
|
|
int32_t curX, int32_t curY,
|
|
int16_t dx, int16_t dy, bool frame1)
|
|
{
|
|
int32_t intX, intY;
|
|
|
|
uint16_t* pChar = (uint16_t*) m_memory.GetRealAddress(frame1 ? 0x0600A000 : 0x06000000);
|
|
|
|
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
|
|
{
|
|
intX = curX >> 8;
|
|
intY = curY >> 8;
|
|
|
|
// if we are off layer
|
|
if (intX < 0 || intX >= 160)
|
|
continue;
|
|
if (intY < 0 || intY >= 128)
|
|
continue;
|
|
|
|
*ptr = pChar[intY * 160 + intX] | 0x8000;
|
|
}
|
|
}
|
|
|
|
void BgLayer::UpdateCnt (uint16_t cnt)
|
|
{
|
|
if (m_cnt == cnt)
|
|
return;
|
|
|
|
switch (cnt >> 14)
|
|
{
|
|
case 0:
|
|
m_tWidth = m_tHeight = 32;
|
|
m_rWidth = m_rHeight = 16;
|
|
break;
|
|
case 1:
|
|
m_tWidth = 64;
|
|
m_tHeight = 32;
|
|
m_rWidth = m_rHeight = 32;
|
|
break;
|
|
case 2:
|
|
m_tWidth = 32;
|
|
m_tHeight = 64;
|
|
m_rWidth = m_rHeight = 64;
|
|
break;
|
|
case 3:
|
|
m_tWidth = m_tHeight = 64;
|
|
m_rWidth = m_rHeight = 128;
|
|
break;
|
|
}
|
|
|
|
m_priority = (cnt & 0x3);
|
|
m_charAdd = 0x06000000 + ((cnt >> 2) & 0x3) * 0x4000;
|
|
m_hicolor = cnt & (0x1 << 7);
|
|
m_mapAdd = 0x06000000 + ((cnt >> 8) & 0x1F) * 0x0800;
|
|
|
|
m_cnt = cnt;
|
|
}
|
|
}
|
|
}
|