#include "ameteor/graphics/screen.hpp"
#include "../globals.hpp"
#include "../debug.hpp"
#include "ameteor.hpp"
#include <cstring>
namespace AMeteor
{
namespace Graphics
{
BgLayer Screen::* const Screen::BgLayers [4] =
{ &Screen::m_bgLayer0, &Screen::m_bgLayer1,
&Screen::m_bgLayer2, &Screen::m_bgLayer3 };
Screen::Screen (Memory& memory, Io& io) :
m_io(io),
m_surface(new uint16_t[WIDTH*HEIGHT]),
m_renderer(m_surface),
m_frameskip(0),
m_curframe(0),
m_dispcnt(0),
m_refX2(0), m_refY2(0), m_refX3(0), m_refY3(0),
m_pPalette((uint16_t*)memory.GetRealAddress(0x05000000)),
m_bgLayer0(0, memory, io, m_pPalette),
m_bgLayer1(1, memory, io, m_pPalette),
m_bgLayer2(2, memory, io, m_pPalette),
m_bgLayer3(3, memory, io, m_pPalette),
m_objs(memory, io, m_pPalette + 256)
{
}
Screen::~Screen ()
{
delete [] m_surface;
}
void Screen::DrawLine (uint8_t line)
{
if (m_curframe < m_frameskip)
{
if (line == 159)
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
return;
}
uint16_t* lineBg = new uint16_t[4*WIDTH];
memset(lineBg, 0, 4*WIDTH*sizeof(uint16_t));
uint32_t* lineObj = new uint32_t[WIDTH];
for (uint32_t* p = lineObj + WIDTH - 1; p >= lineObj; --p)
*p = 0x00030000;
uint8_t prio[4];
prio[0] = m_bgLayer0.GetPriority();
prio[1] = m_bgLayer1.GetPriority();
prio[2] = m_bgLayer2.GetPriority();
prio[3] = m_bgLayer3.GetPriority();
uint8_t layersOn = (m_dispcnt >> 8) & 0x1F;
switch (m_dispcnt & 0x7)
{
case 0:
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2)) m_bgLayer2.DrawLine0(line, lineBg+2*WIDTH);
if (layersOn & (0x1 << 3)) m_bgLayer3.DrawLine0(line, lineBg+3*WIDTH);
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 1:
layersOn &= 0xF7;
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 2:
layersOn &= 0xFC;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 3))
m_bgLayer3.DrawLine2(lineBg+3*WIDTH,
m_refX3, m_refY3,
m_io.DRead16(Io::BG3PA),
m_io.DRead16(Io::BG3PC));
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 3:
layersOn &= 0xF4;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine3(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 4))
m_objs.DrawLineHighOnly(line, lineObj);
break;
case 4:
layersOn &= 0xF4;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine4(
line,
lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PB),
m_io.DRead16(Io::BG2PC),
m_io.DRead16(Io::BG2PD),
m_dispcnt & (0x1 << 4));
if (layersOn & (0x1 << 4))
m_objs.DrawLineHighOnly(line, lineObj);
break;
case 5:
layersOn &= 0xF4;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine5(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC),
m_dispcnt & (0x1 << 4));
if (layersOn & (0x1 << 4))
m_objs.DrawLineHighOnly(line, lineObj);
break;
default :
met_abort("not supported : " << (m_dispcnt & 0x7));
break;
}
uint8_t* window = NULL;
if (m_dispcnt >> 13)
{
window = new uint8_t[WIDTH];
memset(window, m_io.DRead16(Io::WINOUT) & 0x3F, WIDTH*sizeof(uint8_t));
if (m_dispcnt & (0x1 << 15))
m_objs.DrawWindow(line, window);
if (m_dispcnt & (0x1 << 14))
DrawWindow(line, window,
m_io.DRead16(Io::WIN1V), m_io.DRead16(Io::WIN1H),
(m_io.DRead16(Io::WININ) >> 8) & 0x3F);
if (m_dispcnt & (0x1 << 13))
DrawWindow(line, window,
m_io.DRead16(Io::WIN0V), m_io.DRead16(Io::WIN0H),
m_io.DRead16(Io::WININ) & 0x3F);
}
uint16_t bldcnt = m_io.DRead16(Io::BLDCNT);
uint8_t colorEffect = (bldcnt >> 6) & 0x3;
uint8_t eva = std::min(m_io.DRead8(Io::BLDALPHA) & 0x1F, 16);
uint8_t evb = std::min(m_io.DRead8(Io::BLDALPHA+1) & 0x1F, 16);
uint8_t evy = std::min(m_io.DRead8(Io::BLDY) & 0x1F, 16);
uint16_t* surface = m_surface + line*WIDTH;
uint16_t out, bout;
uint8_t top, back;
uint8_t curprio;
uint32_t* pObj = lineObj;
uint16_t* pBg = lineBg;
uint8_t* pWin = window;
uint8_t winmask;
if (!window)
winmask = 0xFF;
for (uint8_t x = 0; x < WIDTH; ++x, ++pBg, ++pObj, ++pWin)
{
if (window)
winmask = *pWin;
bout = out = m_pPalette[0];
back = top = 0xF5;
for (uint8_t l = 0; l < 4; ++l)
if ((layersOn & (0x1 << l)) &&
(pBg[l*WIDTH] & 0x8000))
{
curprio = ((prio[l] << 4) | l);
if (curprio < back && curprio > top)
{
bout = pBg[l*WIDTH];
back = curprio;
}
else if (
curprio < top &&
(winmask & (0x1 << l)))
{
bout = out;
out = pBg[l*WIDTH];
back = top;
top = curprio;
}
}
if ((layersOn & (0x1 << 4)) &&
(*pObj & 0x8000))
{
curprio = ((*pObj >> (16 - 4)) & (0x3 << 4));
if (curprio <= (back & 0xF0) && curprio > (top & 0xF0))
{
bout = *pObj;
back = curprio | 4;
}
else if (
curprio <= (top & 0xF0) &&
(winmask & (0x1 << 4)))
{
bout = out;
out = *pObj;
back = top;
top = curprio | 4;
}
}
if ((top & 0xF) == 4 && (*pObj & (0x1 << 18)))
{
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
out =
std::min(((bout & 0x001F) * evb +
(out & 0x001F) * eva) / 16, 0x001F) |
std::min((((bout & 0x03E0) * evb +
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
std::min((((bout & 0x7C00) * evb +
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
}
else if ((!window || (*pWin & (0x1 << 5)))
&& (bldcnt & (0x1 << (top & 0xF))))
switch (colorEffect)
{
case 1:
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
out =
std::min(((bout & 0x001F) * evb +
(out & 0x001F) * eva) / 16, 0x001F) |
std::min((((bout & 0x03E0) * evb +
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
std::min((((bout & 0x7C00) * evb +
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
break;
case 2:
out =
(((out & 0x001F) +
((0x001F - (out & 0x001F)) * evy) / 16) & 0x001F) |
(((out & 0x03E0) +
((0x03E0 - (out & 0x03E0)) * evy) / 16) & 0x03E0) |
(((out & 0x7C00) +
((0x7C00 - (out & 0x7C00)) * evy) / 16) & 0x7C00);
break;
case 3:
out =
((((out & 0x001F) * (16-evy)) / 16) & 0x001F) |
((((out & 0x03E0) * (16-evy)) / 16) & 0x03E0) |
((((out & 0x7C00) * (16-evy)) / 16) & 0x7C00);
break;
}
*surface = out;
++surface;
}
m_refX2 += (int16_t)m_io.DRead16(Io::BG2PB);
m_refY2 += (int16_t)m_io.DRead16(Io::BG2PD);
m_refX3 += (int16_t)m_io.DRead16(Io::BG3PB);
m_refY3 += (int16_t)m_io.DRead16(Io::BG3PD);
if (window)
delete [] window;
delete [] lineBg;
delete [] lineObj;
if (line == 159)
{
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
m_renderer.VBlank();
}
}
void Screen::DrawWindow (uint8_t line, uint8_t* surface,
uint16_t win0v, uint16_t win0h, uint8_t mask)
{
uint8_t win0t = win0v >> 8, win0b = win0v & 0xFF;
if (win0t >= 227)
return;
else if (win0b > win0t && line >= win0t && line < win0b
|| win0b < win0t && (line >= win0t || line < win0b)
)
{
uint8_t win0l, win0r;
uint8_t* ptr;
win0l = win0h >> 8;
win0r = win0h & 0xFF;
if (win0l <= win0r)
{
ptr = surface + win0l;
for (uint8_t i = win0l; i < win0r && i < 240; ++i, ++ptr)
*ptr = mask;
}
else
{
ptr = surface;
for (uint8_t i = 0; i < win0r && i < 240; ++i, ++ptr)
*ptr = mask;
ptr = surface + win0l;
for (uint8_t i = win0l; i < 240; ++i, ++ptr)
*ptr = mask;
}
}
}
bool Screen::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_refX2);
SS_WRITE_VAR(m_refY2);
SS_WRITE_VAR(m_refX3);
SS_WRITE_VAR(m_refY3);
return true;
}
bool Screen::LoadState (std::istream& stream)
{
SS_READ_VAR(m_refX2);
SS_READ_VAR(m_refY2);
SS_READ_VAR(m_refX3);
SS_READ_VAR(m_refY3);
return true;
}
}
}