mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-11 17:29:57 +00:00
First stab at sprites. Lots of bugs!
Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
parent
2060989ac7
commit
f6181400c4
@ -3,7 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "Bus.h"
|
||||
#include <Bus.h>
|
||||
|
||||
namespace EightBit {
|
||||
namespace GameBoy {
|
||||
|
@ -30,6 +30,18 @@ namespace EightBit {
|
||||
std::vector<uint32_t> m_pixels;
|
||||
Bus& m_bus;
|
||||
const AbstractColourPalette* m_colours;
|
||||
|
||||
std::array<int, 4> createPalette(int address);
|
||||
|
||||
void renderBackground();
|
||||
void renderBackground(
|
||||
int bgArea, int bgCharacters,
|
||||
int offsetX, int offsetY,
|
||||
int scrollX, int scrollY,
|
||||
const std::array<int, 4>& palette);
|
||||
|
||||
void renderObjects();
|
||||
void renderObjects(int objBlockHeight);
|
||||
};
|
||||
}
|
||||
}
|
42
LR35902/inc/ObjectAttribute.h
Normal file
42
LR35902/inc/ObjectAttribute.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <Bus.h>
|
||||
#include <Processor.h>
|
||||
|
||||
namespace EightBit {
|
||||
namespace GameBoy {
|
||||
class ObjectAttribute {
|
||||
public:
|
||||
ObjectAttribute() {}
|
||||
ObjectAttribute(Bus& bus, uint16_t address, int height) {
|
||||
m_positionY = bus.peek(address);
|
||||
m_positionX = bus.peek(address + 1);
|
||||
m_pattern = bus.peek(address + 2);
|
||||
if (height == 16)
|
||||
m_pattern >>= 1;
|
||||
m_flags = bus.peek(address + 3);
|
||||
}
|
||||
|
||||
uint8_t positionY() const { return m_positionY; }
|
||||
uint8_t positionX() const { return m_positionX; }
|
||||
uint8_t pattern() const { return m_pattern; }
|
||||
uint8_t flags() const { return m_flags; }
|
||||
|
||||
uint8_t priority() const { return flags() & Processor::Bit7; }
|
||||
|
||||
bool highPriority() const { return priority() != 0; }
|
||||
bool lowPriority() const { return priority() == 0; }
|
||||
bool flipY() const { return (flags() & Processor::Bit6) != 0; }
|
||||
bool flipX() const { return (flags() & Processor::Bit5) != 0; }
|
||||
int palette() const { return (flags() & Processor::Bit4) >> 3; }
|
||||
|
||||
private:
|
||||
uint8_t m_positionY;
|
||||
uint8_t m_positionX;
|
||||
uint8_t m_pattern;
|
||||
uint8_t m_flags;
|
||||
};
|
||||
}
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
#include "Display.h"
|
||||
#include "Processor.h"
|
||||
#include "CharacterDefinition.h"
|
||||
#include "ObjectAttribute.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
EightBit::GameBoy::Display::Display(const AbstractColourPalette* colours, Bus& bus)
|
||||
: m_bus(bus),
|
||||
@ -22,65 +25,131 @@ void EightBit::GameBoy::Display::render() {
|
||||
auto on = control & Bus::LcdEnable;
|
||||
if (on) {
|
||||
|
||||
auto windowArea = (control & Bus::WindowCodeAreaSelection) ? 0x9c00 : 0x9800;
|
||||
auto window = (control & Bus::WindowEnable) != 0;
|
||||
auto bgCharacters = (control & Bus::BackgroundCharacterDataSelection) ? 0x8000 : 0x8800;
|
||||
auto bgArea = (control & Bus::BackgroundCodeAreaSelection) ? 0x9c00 : 0x9800;
|
||||
auto objBlockHeight = (control & Bus::ObjectBlockCompositionSelection) ? 16 : 8;
|
||||
auto objEnable = (control & Bus::ObjectEnable) != 0;
|
||||
auto bgDisplay = (control & Bus::DisplayBackground) != 0;
|
||||
const auto bgDisplay = (control & Bus::DisplayBackground) != 0;
|
||||
if (bgDisplay)
|
||||
renderBackground();
|
||||
|
||||
auto scrollX = m_bus.peekRegister(Bus::SCX);
|
||||
auto scrollY = m_bus.peekRegister(Bus::SCY);
|
||||
const auto objEnable = (control & Bus::ObjectEnable) != 0;
|
||||
if (objEnable)
|
||||
renderObjects();
|
||||
}
|
||||
}
|
||||
|
||||
auto paletteRaw = m_bus.peekRegister(Bus::BGP);
|
||||
std::array<int, 4> palette;
|
||||
palette[0] = paletteRaw & 0b11;
|
||||
palette[1] = (paletteRaw & 0b1100) >> 2;
|
||||
palette[2] = (paletteRaw & 0b110000) >> 4;
|
||||
palette[3] = (paletteRaw & 0b11000000) >> 6;
|
||||
std::array<int, 4> EightBit::GameBoy::Display::createPalette(const int address) {
|
||||
const auto raw = m_bus.peekRegister(address);
|
||||
std::array<int, 4> palette;
|
||||
palette[0] = raw & 0b11;
|
||||
palette[1] = (raw & 0b1100) >> 2;
|
||||
palette[2] = (raw & 0b110000) >> 4;
|
||||
palette[3] = (raw & 0b11000000) >> 6;
|
||||
return palette;
|
||||
}
|
||||
|
||||
auto wx = m_bus.peekRegister(Bus::WX);
|
||||
auto wy = m_bus.peekRegister(Bus::WY);
|
||||
void EightBit::GameBoy::Display::renderObjects() {
|
||||
const auto control = m_bus.peekRegister(Bus::LCDC);
|
||||
const auto objBlockHeight = (control & Bus::ObjectBlockCompositionSelection) ? 16 : 8;
|
||||
renderObjects(objBlockHeight);
|
||||
}
|
||||
|
||||
auto offsetX = window ? wx - 7 : 0;
|
||||
auto offsetY = window ? wy : 0;
|
||||
void EightBit::GameBoy::Display::renderObjects(int objBlockHeight) {
|
||||
|
||||
std::vector<std::array<int, 4>> palettes(2);
|
||||
palettes[0] = createPalette(Bus::OBP0);
|
||||
palettes[1] = createPalette(Bus::OBP1);
|
||||
|
||||
std::map<int, CharacterDefinition> definitions;
|
||||
auto objDefinitionAddress = 0x8000;
|
||||
auto oamAddress = 0xfe00;
|
||||
|
||||
for (int row = 0; row < BufferCharacterHeight; ++row) {
|
||||
for (int column = 0; column < BufferCharacterWidth; ++column) {
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
const auto current = ObjectAttribute(m_bus, oamAddress + 4 * i, objBlockHeight);
|
||||
const auto sprite = current.pattern();
|
||||
const auto definition = CharacterDefinition(m_bus, objDefinitionAddress + 16 * sprite);
|
||||
const auto spriteX = current.positionX();
|
||||
const auto spriteY = current.positionY();
|
||||
const auto& palette = palettes[current.palette()];
|
||||
|
||||
auto address = bgArea + row * BufferCharacterWidth + column;
|
||||
auto character = m_bus.peek(address);
|
||||
for (int cy = 0; cy < 8; ++cy) {
|
||||
for (int cx = 0; cx < 8; ++cx) {
|
||||
|
||||
auto definitionPair = definitions.find(character);
|
||||
uint8_t x = spriteX + cx;
|
||||
if (x >= RasterWidth)
|
||||
break;
|
||||
|
||||
if (definitionPair == definitions.end()) {
|
||||
definitions[character] = CharacterDefinition(m_bus, bgCharacters + 16 * character);
|
||||
definitionPair = definitions.find(character);
|
||||
}
|
||||
uint8_t y = spriteX + cy;
|
||||
if (y >= RasterHeight)
|
||||
break;
|
||||
|
||||
auto definition = definitionPair->second;
|
||||
auto outputPixel = y * RasterWidth + x;
|
||||
|
||||
for (int cy = 0; cy < 8; ++cy) {
|
||||
for (int cx = 0; cx < 8; ++cx) {
|
||||
|
||||
uint8_t x = column * 8 + cx + offsetX - scrollX;
|
||||
if (x >= RasterWidth)
|
||||
break;
|
||||
|
||||
uint8_t y = row * 8 + cy + offsetY - scrollY;
|
||||
if (y >= RasterHeight)
|
||||
break;
|
||||
|
||||
auto outputPixel = y * RasterWidth + x;
|
||||
|
||||
auto colour = palette[definition.get()[cy * 8 + cx]];
|
||||
m_pixels[outputPixel] = m_colours->getColour(colour);
|
||||
}
|
||||
}
|
||||
auto colour = palette[definition.get()[cy * 8 + cx]];
|
||||
m_pixels[outputPixel] = m_colours->getColour(colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Display::renderBackground() {
|
||||
|
||||
const auto control = m_bus.peekRegister(Bus::LCDC);
|
||||
|
||||
auto palette = createPalette(Bus::BGP);
|
||||
|
||||
const auto window = (control & Bus::WindowEnable) != 0;
|
||||
const auto bgArea = (control & Bus::BackgroundCodeAreaSelection) ? 0x9c00 : 0x9800;
|
||||
const auto bgCharacters = (control & Bus::BackgroundCharacterDataSelection) ? 0x8000 : 0x8800;
|
||||
|
||||
const auto wx = m_bus.peekRegister(Bus::WX);
|
||||
const auto wy = m_bus.peekRegister(Bus::WY);
|
||||
|
||||
const auto offsetX = window ? wx - 7 : 0;
|
||||
const auto offsetY = window ? wy : 0;
|
||||
|
||||
const auto scrollX = m_bus.peekRegister(Bus::SCX);
|
||||
const auto scrollY = m_bus.peekRegister(Bus::SCY);
|
||||
|
||||
renderBackground(bgArea, bgCharacters, offsetX, offsetY, scrollX, scrollY, palette);
|
||||
}
|
||||
|
||||
void EightBit::GameBoy::Display::renderBackground(
|
||||
int bgArea, int bgCharacters,
|
||||
int offsetX, int offsetY,
|
||||
int scrollX, int scrollY,
|
||||
const std::array<int, 4>& palette) {
|
||||
|
||||
std::map<int, CharacterDefinition> definitions;
|
||||
|
||||
for (int row = 0; row < BufferCharacterHeight; ++row) {
|
||||
for (int column = 0; column < BufferCharacterWidth; ++column) {
|
||||
|
||||
auto address = bgArea + row * BufferCharacterWidth + column;
|
||||
auto character = m_bus.peek(address);
|
||||
|
||||
auto definitionPair = definitions.find(character);
|
||||
|
||||
if (definitionPair == definitions.end()) {
|
||||
definitions[character] = CharacterDefinition(m_bus, bgCharacters + 16 * character);
|
||||
definitionPair = definitions.find(character);
|
||||
}
|
||||
|
||||
auto definition = definitionPair->second;
|
||||
|
||||
for (int cy = 0; cy < 8; ++cy) {
|
||||
for (int cx = 0; cx < 8; ++cx) {
|
||||
|
||||
uint8_t x = column * 8 + cx + offsetX - scrollX;
|
||||
if (x >= RasterWidth)
|
||||
break;
|
||||
|
||||
uint8_t y = row * 8 + cy + offsetY - scrollY;
|
||||
if (y >= RasterHeight)
|
||||
break;
|
||||
|
||||
auto outputPixel = y * RasterWidth + x;
|
||||
|
||||
auto colour = palette[definition.get()[cy * 8 + cx]];
|
||||
m_pixels[outputPixel] = m_colours->getColour(colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -145,6 +145,7 @@
|
||||
<ClInclude Include="..\inc\Disassembler.h" />
|
||||
<ClInclude Include="..\inc\Display.h" />
|
||||
<ClInclude Include="..\inc\LR35902.h" />
|
||||
<ClInclude Include="..\inc\ObjectAttribute.h" />
|
||||
<ClInclude Include="..\inc\Profiler.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
</ItemGroup>
|
||||
|
@ -35,6 +35,9 @@
|
||||
<ClInclude Include="..\inc\GameBoyBus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\ObjectAttribute.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
Loading…
x
Reference in New Issue
Block a user