2017-08-29 21:23:32 +00:00
|
|
|
#include "stdafx.h"
|
2021-05-29 11:18:13 +00:00
|
|
|
#include "../inc/Display.h"
|
|
|
|
#include "../inc/CharacterDefinition.h"
|
|
|
|
#include "../inc/ObjectAttribute.h"
|
|
|
|
#include "../inc/GameBoyBus.h"
|
|
|
|
#include "../inc/AbstractColourPalette.h"
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2017-10-19 21:43:09 +00:00
|
|
|
#include <Processor.h>
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2017-11-20 19:17:49 +00:00
|
|
|
EightBit::GameBoy::Display::Display(const AbstractColourPalette* colours, Bus& bus, Ram& oam, Ram& vram)
|
2017-08-29 21:23:32 +00:00
|
|
|
: m_bus(bus),
|
2017-10-04 17:00:53 +00:00
|
|
|
m_oam(oam),
|
|
|
|
m_vram(vram),
|
2017-11-11 11:12:09 +00:00
|
|
|
m_colours(colours) {
|
2017-08-29 21:23:32 +00:00
|
|
|
}
|
|
|
|
|
2020-11-07 09:41:12 +00:00
|
|
|
void EightBit::GameBoy::Display::renderCurrentScanline() {
|
2017-10-05 10:24:36 +00:00
|
|
|
m_scanLine = m_bus.IO().peek(IoRegisters::LY);
|
2017-10-04 00:28:33 +00:00
|
|
|
if (m_scanLine < RasterHeight) {
|
2017-10-05 10:24:36 +00:00
|
|
|
m_control = m_bus.IO().peek(IoRegisters::LCDC);
|
2020-11-07 09:41:12 +00:00
|
|
|
assert(m_control & IoRegisters::LCD_EN);
|
|
|
|
if (m_control & IoRegisters::BG_EN)
|
|
|
|
renderBackground();
|
|
|
|
if (m_control & IoRegisters::OBJ_EN)
|
|
|
|
renderObjects();
|
2017-09-15 16:25:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2017-09-15 16:25:55 +00:00
|
|
|
std::array<int, 4> EightBit::GameBoy::Display::createPalette(const int address) {
|
2017-10-05 10:24:36 +00:00
|
|
|
const auto raw = m_bus.IO().peek(address);
|
2018-05-02 01:47:47 +00:00
|
|
|
const std::array<int, 4> palette = {
|
|
|
|
raw & 0b11,
|
|
|
|
(raw & 0b1100) >> 2,
|
|
|
|
(raw & 0b110000) >> 4,
|
|
|
|
(raw & 0b11000000) >> 6,
|
|
|
|
};
|
2017-09-15 16:25:55 +00:00
|
|
|
return palette;
|
|
|
|
}
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2017-10-01 10:32:41 +00:00
|
|
|
void EightBit::GameBoy::Display::loadObjectAttributes() {
|
2017-10-22 20:25:06 +00:00
|
|
|
for (int i = 0; i < 40; ++i)
|
|
|
|
m_objectAttributes[i] = ObjectAttribute(m_oam, 4 * i);
|
2017-10-01 10:32:41 +00:00
|
|
|
}
|
|
|
|
|
2017-10-22 20:25:06 +00:00
|
|
|
void EightBit::GameBoy::Display::renderObjects() {
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2020-10-28 14:02:42 +00:00
|
|
|
const auto objBlockHeight = (m_control & IoRegisters::OBJ_SIZE) ? 16 : 8;
|
2017-10-22 20:25:06 +00:00
|
|
|
|
2018-05-02 01:47:47 +00:00
|
|
|
const std::array<std::array<int, 4>, 2> palettes = {
|
|
|
|
createPalette(IoRegisters::OBP0),
|
|
|
|
createPalette(IoRegisters::OBP1)
|
|
|
|
};
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2018-05-04 02:38:39 +00:00
|
|
|
const auto characterAddressMultiplier = objBlockHeight == 8 ? 16 : 8;
|
|
|
|
|
2017-09-15 16:25:55 +00:00
|
|
|
for (int i = 0; i < 40; ++i) {
|
2017-09-15 17:23:02 +00:00
|
|
|
|
2017-10-01 10:32:41 +00:00
|
|
|
const auto& current = m_objectAttributes[i];
|
|
|
|
|
2017-09-15 16:25:55 +00:00
|
|
|
const auto spriteY = current.positionY();
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto drawY = spriteY - 16;
|
2017-10-01 10:32:41 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
if ((m_scanLine >= drawY) && (m_scanLine < (drawY + objBlockHeight))) {
|
2017-10-01 10:32:41 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto spriteX = current.positionX();
|
|
|
|
const auto drawX = spriteX - 8;
|
2017-10-01 10:32:41 +00:00
|
|
|
|
2017-10-04 14:37:11 +00:00
|
|
|
const auto sprite = current.pattern();
|
2020-11-07 09:41:12 +00:00
|
|
|
const auto definition = CharacterDefinition(m_vram, characterAddressMultiplier * sprite);
|
2017-10-01 10:32:41 +00:00
|
|
|
const auto& palette = palettes[current.palette()];
|
|
|
|
const auto flipX = current.flipX();
|
|
|
|
const auto flipY = current.flipY();
|
|
|
|
|
2020-11-07 09:41:12 +00:00
|
|
|
renderSpriteTile(
|
2017-10-01 10:32:41 +00:00
|
|
|
objBlockHeight,
|
2017-10-04 00:28:33 +00:00
|
|
|
drawX, drawY,
|
2020-11-07 09:41:12 +00:00
|
|
|
flipX, flipY,
|
2017-10-01 10:32:41 +00:00
|
|
|
palette,
|
|
|
|
definition);
|
|
|
|
}
|
2017-09-15 16:25:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EightBit::GameBoy::Display::renderBackground() {
|
|
|
|
|
2018-05-04 02:38:39 +00:00
|
|
|
const auto palette = createPalette(IoRegisters::BGP);
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2020-10-28 14:02:42 +00:00
|
|
|
const auto window = !!(m_control & IoRegisters::WIN_EN);
|
|
|
|
const auto bgArea = (m_control & IoRegisters::BG_MAP) ? 0x1c00 : 0x1800;
|
|
|
|
const auto bgCharacters = (m_control & IoRegisters::TILE_SEL) ? 0 : 0x1000;
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2017-10-05 10:24:36 +00:00
|
|
|
const auto wx = m_bus.IO().peek(IoRegisters::WX);
|
|
|
|
const auto wy = m_bus.IO().peek(IoRegisters::WY);
|
2017-08-29 21:23:32 +00:00
|
|
|
|
2017-09-15 16:25:55 +00:00
|
|
|
const auto offsetX = window ? wx - 7 : 0;
|
|
|
|
const auto offsetY = window ? wy : 0;
|
|
|
|
|
2017-10-05 10:24:36 +00:00
|
|
|
const auto scrollX = m_bus.IO().peek(IoRegisters::SCX);
|
|
|
|
const auto scrollY = m_bus.IO().peek(IoRegisters::SCY);
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2020-10-28 14:02:42 +00:00
|
|
|
const auto offsetType = bgCharacters == 0 ? tile_offset_t::Unsigned : tile_offset_t::Signed;
|
|
|
|
renderBackground(bgArea, bgCharacters, offsetType, offsetX - scrollX, offsetY - scrollY, palette);
|
2017-09-15 16:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void EightBit::GameBoy::Display::renderBackground(
|
2020-10-28 14:02:42 +00:00
|
|
|
int bgArea, int bgCharacters, tile_offset_t offsetType,
|
2017-09-15 16:25:55 +00:00
|
|
|
int offsetX, int offsetY,
|
|
|
|
const std::array<int, 4>& palette) {
|
|
|
|
|
2017-10-03 23:30:59 +00:00
|
|
|
const int row = (m_scanLine - offsetY) / 8;
|
2017-10-22 20:25:06 +00:00
|
|
|
auto address = bgArea + row * BufferCharacterWidth;
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2017-10-03 22:54:39 +00:00
|
|
|
for (int column = 0; column < BufferCharacterWidth; ++column) {
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2017-10-22 20:25:06 +00:00
|
|
|
const auto character = m_vram.peek(address++);
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2020-10-28 14:02:42 +00:00
|
|
|
const auto definitionOffset = offsetType == tile_offset_t::Signed ? 16 * (int8_t)character : 16 * character;
|
2020-11-07 09:41:12 +00:00
|
|
|
const auto definition = CharacterDefinition(m_vram, bgCharacters + definitionOffset);
|
|
|
|
renderBackgroundTile(
|
2017-10-03 22:54:39 +00:00
|
|
|
column * 8 + offsetX, row * 8 + offsetY,
|
|
|
|
palette,
|
|
|
|
definition);
|
2017-09-15 17:23:02 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2020-11-07 09:41:12 +00:00
|
|
|
void EightBit::GameBoy::Display::renderSpriteTile(
|
|
|
|
const int height,
|
|
|
|
const int drawX, const int drawY,
|
|
|
|
const bool flipX, const bool flipY,
|
|
|
|
const std::array<int, 4>& palette,
|
|
|
|
const CharacterDefinition& definition) {
|
|
|
|
renderTile(
|
|
|
|
height,
|
|
|
|
drawX, drawY,
|
|
|
|
flipX, flipY, true,
|
|
|
|
palette,
|
|
|
|
definition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EightBit::GameBoy::Display::renderBackgroundTile(
|
|
|
|
const int drawX, const int drawY,
|
|
|
|
const std::array<int, 4>& palette,
|
|
|
|
const CharacterDefinition& definition) {
|
|
|
|
renderTile(
|
|
|
|
8,
|
|
|
|
drawX, drawY,
|
|
|
|
false, false, false,
|
|
|
|
palette,
|
|
|
|
definition);
|
|
|
|
}
|
|
|
|
|
2017-09-15 17:23:02 +00:00
|
|
|
void EightBit::GameBoy::Display::renderTile(
|
2017-10-04 00:28:33 +00:00
|
|
|
const int height,
|
|
|
|
const int drawX, const int drawY,
|
|
|
|
const bool flipX, const bool flipY, const bool allowTransparencies,
|
2017-09-15 17:23:02 +00:00
|
|
|
const std::array<int, 4>& palette,
|
2020-11-07 09:41:12 +00:00
|
|
|
const CharacterDefinition& definition) {
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2017-09-17 08:46:28 +00:00
|
|
|
const auto width = 8;
|
2017-09-15 16:25:55 +00:00
|
|
|
|
2017-10-02 14:28:41 +00:00
|
|
|
const auto flipMaskX = width - 1;
|
|
|
|
const auto flipMaskY = height - 1;
|
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto y = m_scanLine;
|
2017-09-17 08:46:28 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
auto cy = y - drawY;
|
|
|
|
if (flipY)
|
|
|
|
cy = ~cy & flipMaskY;
|
2017-10-03 22:54:39 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto rowDefinition = definition.get(cy);
|
2017-10-02 09:12:25 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto lineAddress = y * RasterWidth;
|
|
|
|
for (int cx = 0; cx < width; ++cx) {
|
2017-09-15 17:23:02 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const uint8_t x = drawX + (flipX ? ~cx & flipMaskX : cx);
|
|
|
|
if (x >= RasterWidth)
|
|
|
|
break;
|
2017-09-15 17:23:02 +00:00
|
|
|
|
2017-10-04 00:28:33 +00:00
|
|
|
const auto colour = rowDefinition[cx];
|
|
|
|
if (!allowTransparencies || (allowTransparencies && (colour > 0))) {
|
|
|
|
const auto outputPixel = lineAddress + x;
|
2020-11-07 09:41:12 +00:00
|
|
|
m_pixels[outputPixel] = m_colours->colour(palette[colour]);
|
2017-08-29 21:23:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-15 17:23:02 +00:00
|
|
|
}
|