sprites working, dmg_acid2 passed
This commit is contained in:
@@ -46,6 +46,15 @@ void AddressSpace::loadGame(const std::string& filename) {
|
|||||||
memoryLayout.romBankSwitch = game.data() + ROM_BANK_SIZE;
|
memoryLayout.romBankSwitch = game.data() + ROM_BANK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddressSpace::dmaTransfer() {
|
||||||
|
dmaTransferRequested = false;
|
||||||
|
const Word addr = memoryLayout.DMA << 8;
|
||||||
|
for (int i = 0; i < 0xA0; i++) {
|
||||||
|
const Byte data = (*this)[addr + i];;
|
||||||
|
(*this)[0xFE00 + i] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AddressSpace::setTesting(const bool state) {
|
void AddressSpace::setTesting(const bool state) {
|
||||||
testing = state;
|
testing = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ public:
|
|||||||
uint32_t externalRamSize = 0;
|
uint32_t externalRamSize = 0;
|
||||||
uint32_t externalRamBanks = 0;
|
uint32_t externalRamBanks = 0;
|
||||||
|
|
||||||
|
bool dmaTransferRequested = false;
|
||||||
|
void dmaTransfer();
|
||||||
|
|
||||||
//Selected ROM Bank = (Secondary Bank << 5) + ROM Bank
|
//Selected ROM Bank = (Secondary Bank << 5) + ROM Bank
|
||||||
Byte selectedRomBank = 0;
|
Byte selectedRomBank = 0;
|
||||||
Byte romBankRegister = 0x00;
|
Byte romBankRegister = 0x00;
|
||||||
@@ -348,6 +351,7 @@ public:
|
|||||||
case 0xFF45:
|
case 0xFF45:
|
||||||
return memoryLayout.LYC;
|
return memoryLayout.LYC;
|
||||||
case 0xFF46:
|
case 0xFF46:
|
||||||
|
dmaTransferRequested = true;
|
||||||
return memoryLayout.DMA;
|
return memoryLayout.DMA;
|
||||||
case 0xFF47:
|
case 0xFF47:
|
||||||
return memoryLayout.BGP;
|
return memoryLayout.BGP;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#define RESOLUTION_Y 144
|
#define RESOLUTION_Y 144
|
||||||
#define SCREEN_BPP 3
|
#define SCREEN_BPP 3
|
||||||
|
|
||||||
|
//lcdc
|
||||||
#define BG_WINDOW_ENABLE 0
|
#define BG_WINDOW_ENABLE 0
|
||||||
#define OBJ_ENABLE 1
|
#define OBJ_ENABLE 1
|
||||||
#define OBJ_SIZE 2
|
#define OBJ_SIZE 2
|
||||||
@@ -40,6 +41,13 @@
|
|||||||
#define WINDOW_TILE_MAP_AREA 6
|
#define WINDOW_TILE_MAP_AREA 6
|
||||||
#define LCD_ENABLE 7
|
#define LCD_ENABLE 7
|
||||||
|
|
||||||
|
//oam
|
||||||
|
#define PRIORITY 7
|
||||||
|
#define Y_FLIP 6
|
||||||
|
#define X_FLIP 5
|
||||||
|
#define OBJ_PALETTE 4
|
||||||
|
|
||||||
|
|
||||||
#define ROM_BANK_SIZE 0x4000
|
#define ROM_BANK_SIZE 0x4000
|
||||||
#define RAM_BANK_SIZE 0x2000
|
#define RAM_BANK_SIZE 0x2000
|
||||||
|
|
||||||
@@ -56,13 +64,10 @@
|
|||||||
#define SCANLINE_OAM_FREQ 80 //PPU_MODE 2
|
#define SCANLINE_OAM_FREQ 80 //PPU_MODE 2
|
||||||
#define SCANLINE_VRAM_FREQ 80 //PPU_MODE 3
|
#define SCANLINE_VRAM_FREQ 80 //PPU_MODE 3
|
||||||
|
|
||||||
//two bits per colour
|
#define WHITE 0xFFFFFFFF;
|
||||||
enum Colour {
|
#define LIGHT_GRAY 0xFFAAAAAA;
|
||||||
black = 0b11,
|
#define DARK_GRAY 0xFF555555;
|
||||||
darkGray = 0b10,
|
#define BLACK 0xFF000000
|
||||||
lightGray = 0b01,
|
|
||||||
white = 0b00
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Input {
|
struct Input {
|
||||||
bool UP = false;
|
bool UP = false;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ GameboyTestState GameBoy::runTest(GameboyTestState initial) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void GameBoy::start(std::string bootrom, std::string game) {
|
void GameBoy::start(const std::string& bootrom, const std::string& game) {
|
||||||
addressSpace.loadBootrom(bootrom);
|
addressSpace.loadBootrom(bootrom);
|
||||||
addressSpace.loadGame(game);
|
addressSpace.loadGame(game);
|
||||||
addressSpace.determineMBCInfo();
|
addressSpace.determineMBCInfo();
|
||||||
@@ -94,8 +94,6 @@ void GameBoy::start(std::string bootrom, std::string game) {
|
|||||||
// printf("LCDC:%.2x STAT:0x%.2x LY:%d LYC:%d\n", (*LCDC), (*STAT), (*LY), (*LYC));
|
// printf("LCDC:%.2x STAT:0x%.2x LY:%d LYC:%d\n", (*LCDC), (*STAT), (*LY), (*LYC));
|
||||||
// printf("\n");
|
// printf("\n");
|
||||||
}
|
}
|
||||||
// if (PC >= 0xf000)
|
|
||||||
// exit(1);
|
|
||||||
|
|
||||||
|
|
||||||
if (!halted) {
|
if (!halted) {
|
||||||
@@ -125,6 +123,13 @@ void GameBoy::start(std::string bootrom, std::string game) {
|
|||||||
setIME = true;
|
setIME = true;
|
||||||
IME_togge = false;
|
IME_togge = false;
|
||||||
}
|
}
|
||||||
|
if (addressSpace.dmaTransferRequested) {
|
||||||
|
cyclesUntilDMATransfer -= lastOpTicks;
|
||||||
|
if (cyclesUntilDMATransfer <= 0) {
|
||||||
|
cyclesUntilDMATransfer = 160;
|
||||||
|
addressSpace.dmaTransfer();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rendered = false;
|
rendered = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class GameBoy {
|
|||||||
//Start at 2 T-cycles https://github.com/Gekkio/mooneye-test-suite/blob/main/acceptance/ppu/lcdon_timing-GS.s
|
//Start at 2 T-cycles https://github.com/Gekkio/mooneye-test-suite/blob/main/acceptance/ppu/lcdon_timing-GS.s
|
||||||
uint64_t ppuCycles = 2;
|
uint64_t ppuCycles = 2;
|
||||||
bool ppuEnabled = false;
|
bool ppuEnabled = false;
|
||||||
uint64_t lastOpTicks = 0;
|
uint16_t lastOpTicks = 0;
|
||||||
uint64_t lastRefresh = 0;
|
uint64_t lastRefresh = 0;
|
||||||
uint64_t lastScanline = 0;
|
uint64_t lastScanline = 0;
|
||||||
uint64_t cyclesToStayInHblank = -1;
|
uint64_t cyclesToStayInHblank = -1;
|
||||||
@@ -51,6 +51,7 @@ class GameBoy {
|
|||||||
|
|
||||||
PPUMode currentMode = PPUMode::mode0;
|
PPUMode currentMode = PPUMode::mode0;
|
||||||
Byte windowLineCounter = 0;
|
Byte windowLineCounter = 0;
|
||||||
|
int16_t cyclesUntilDMATransfer = 160;
|
||||||
|
|
||||||
Byte prevTMA = 0;
|
Byte prevTMA = 0;
|
||||||
uint64_t lastTIMAUpdate = 0;
|
uint64_t lastTIMAUpdate = 0;
|
||||||
@@ -73,10 +74,12 @@ class GameBoy {
|
|||||||
void opcodeResolver();
|
void opcodeResolver();
|
||||||
|
|
||||||
bool statInteruptLine = false;
|
bool statInteruptLine = false;
|
||||||
bool testLCDCBitEnabled(Byte bit) const;
|
bool LCDCBitEnabled(Byte bit) const;
|
||||||
void incLY();
|
void incLY();
|
||||||
void ppuUpdate();
|
void ppuUpdate();
|
||||||
void drawLine();
|
void drawLine();
|
||||||
|
static bool oamBitEnabled(Byte oamAttributeByte, Byte bit);
|
||||||
|
static unsigned int getColourFromPalette(Byte palette);
|
||||||
void SDL2present();
|
void SDL2present();
|
||||||
|
|
||||||
void checkPPUMode();
|
void checkPPUMode();
|
||||||
@@ -172,7 +175,7 @@ class GameBoy {
|
|||||||
void swap(Byte& value);
|
void swap(Byte& value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void start(std::string bootrom, std::string game);
|
void start(const std::string& bootrom, const std::string& game);
|
||||||
void SDL2setup();
|
void SDL2setup();
|
||||||
void SDL2destroy() const;
|
void SDL2destroy() const;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ int main(int argc, char** argv) {
|
|||||||
auto* gb = new GameBoy();
|
auto* gb = new GameBoy();
|
||||||
gb->SDL2setup();
|
gb->SDL2setup();
|
||||||
//runJSONTests(gb);
|
//runJSONTests(gb);
|
||||||
gb->start("../dmg_boot.bin", "../roms/dmg-acid2.gb");
|
gb->start("../dmg_boot.bin", "../roms/DrMario.gb");
|
||||||
gb->SDL2destroy();
|
gb->SDL2destroy();
|
||||||
delete gb;
|
delete gb;
|
||||||
|
|
||||||
|
|||||||
@@ -115,3 +115,4 @@ void AddressSpace::loadRamBank() {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
195
src/ppu.cpp
195
src/ppu.cpp
@@ -3,11 +3,31 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
bool GameBoy::testLCDCBitEnabled(const Byte bit) const {
|
bool GameBoy::LCDCBitEnabled(const Byte bit) const {
|
||||||
return readOnlyAddressSpace.memoryLayout.LCDC & static_cast<Byte>(1 << bit);
|
return readOnlyAddressSpace.memoryLayout.LCDC & static_cast<Byte>(1 << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameBoy::oamBitEnabled(const Byte oamAttributeByte, const Byte bit) {
|
||||||
|
return oamAttributeByte & static_cast<Byte>(1 << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int GameBoy::getColourFromPalette(const Byte palette) {
|
||||||
|
switch (palette & 0x3) {
|
||||||
|
case 0:
|
||||||
|
return WHITE;
|
||||||
|
case 1:
|
||||||
|
return LIGHT_GRAY;
|
||||||
|
case 2:
|
||||||
|
return DARK_GRAY;
|
||||||
|
case 3:
|
||||||
|
return BLACK;
|
||||||
|
default:
|
||||||
|
std::unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GameBoy::ppuUpdate() {
|
void GameBoy::ppuUpdate() {
|
||||||
//test for HBlank
|
//test for HBlank
|
||||||
checkPPUMode();
|
checkPPUMode();
|
||||||
@@ -99,24 +119,24 @@ uint64_t GameBoy::cyclesSinceLastRefresh() const {
|
|||||||
|
|
||||||
void GameBoy::setPPUMode(const PPUMode mode) {
|
void GameBoy::setPPUMode(const PPUMode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case PPUMode::mode0:
|
case mode0:
|
||||||
addressSpace.memoryLayout.STAT &= ~0x03;
|
addressSpace.memoryLayout.STAT &= ~0x03;
|
||||||
currentMode = PPUMode::mode0;
|
currentMode = mode0;
|
||||||
break;
|
break;
|
||||||
case PPUMode::mode1:
|
case mode1:
|
||||||
addressSpace.memoryLayout.STAT &= ~0x03;
|
addressSpace.memoryLayout.STAT &= ~0x03;
|
||||||
addressSpace.memoryLayout.STAT |= 0x01;
|
addressSpace.memoryLayout.STAT |= 0x01;
|
||||||
currentMode = PPUMode::mode1;
|
currentMode = mode1;
|
||||||
break;
|
break;
|
||||||
case PPUMode::mode2:
|
case mode2:
|
||||||
addressSpace.memoryLayout.STAT &= ~0x03;
|
addressSpace.memoryLayout.STAT &= ~0x03;
|
||||||
addressSpace.memoryLayout.STAT |= 0x02;
|
addressSpace.memoryLayout.STAT |= 0x02;
|
||||||
currentMode = PPUMode::mode2;
|
currentMode = mode2;
|
||||||
break;
|
break;
|
||||||
case PPUMode::mode3:
|
case mode3:
|
||||||
addressSpace.memoryLayout.STAT &= ~0x03;
|
addressSpace.memoryLayout.STAT &= ~0x03;
|
||||||
addressSpace.memoryLayout.STAT |= 0x03;
|
addressSpace.memoryLayout.STAT |= 0x03;
|
||||||
currentMode = PPUMode::mode3;
|
currentMode = mode3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//7th bit is unused but always set
|
//7th bit is unused but always set
|
||||||
@@ -134,13 +154,13 @@ void GameBoy::drawLine() {
|
|||||||
std::fill_n(currentLinePixels, RESOLUTION_X, 0xFFFFFFFF);
|
std::fill_n(currentLinePixels, RESOLUTION_X, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
|
||||||
const uint16_t backgroundMapAddr = testLCDCBitEnabled(BG_TILE_MAP_AREA) ? 0x9C00 : 0x9800;
|
const uint16_t backgroundMapAddr = LCDCBitEnabled(BG_TILE_MAP_AREA) ? 0x9C00 : 0x9800;
|
||||||
const uint16_t windowMapAddr = testLCDCBitEnabled(WINDOW_TILE_MAP_AREA) ? 0x9C00 : 0x9800;
|
const uint16_t windowMapAddr = LCDCBitEnabled(WINDOW_TILE_MAP_AREA) ? 0x9C00 : 0x9800;
|
||||||
const uint16_t tileDataTableAddr = testLCDCBitEnabled(BG_WINDOW_TILE_DATA_AREA) ? 0x8000 : 0x8800;
|
const uint16_t tileDataTableAddr = LCDCBitEnabled(BG_WINDOW_TILE_DATA_AREA) ? 0x8000 : 0x8800;
|
||||||
const bool signedIndex = !testLCDCBitEnabled(BG_WINDOW_TILE_DATA_AREA);
|
const bool signedIndex = !LCDCBitEnabled(BG_WINDOW_TILE_DATA_AREA);
|
||||||
|
|
||||||
//BG
|
//BG
|
||||||
if (testLCDCBitEnabled(BG_WINDOW_ENABLE)) {
|
if (LCDCBitEnabled(BG_WINDOW_ENABLE)) {
|
||||||
for (int pixel = 0; pixel < RESOLUTION_X; pixel++) {
|
for (int pixel = 0; pixel < RESOLUTION_X; pixel++) {
|
||||||
const uint16_t xIndex = (pixel + readOnlyAddressSpace.memoryLayout.SCX) % 256;
|
const uint16_t xIndex = (pixel + readOnlyAddressSpace.memoryLayout.SCX) % 256;
|
||||||
// 256 pixels in total BG width
|
// 256 pixels in total BG width
|
||||||
@@ -174,22 +194,7 @@ void GameBoy::drawLine() {
|
|||||||
// Apply the BGP register for palette mapping
|
// Apply the BGP register for palette mapping
|
||||||
const uint8_t palette = (readOnlyAddressSpace.memoryLayout.BGP >> (colourNum * 2)) & 0x3;
|
const uint8_t palette = (readOnlyAddressSpace.memoryLayout.BGP >> (colourNum * 2)) & 0x3;
|
||||||
|
|
||||||
switch (palette) {
|
currentLinePixels[pixel] = getColourFromPalette(palette);
|
||||||
case 0:
|
|
||||||
currentLinePixels[pixel] = 0xFFFFFFFF;
|
|
||||||
break; // White
|
|
||||||
case 1:
|
|
||||||
currentLinePixels[pixel] = 0xFFAAAAAA;
|
|
||||||
break; // Light gray
|
|
||||||
case 2:
|
|
||||||
currentLinePixels[pixel] = 0xFF555555;
|
|
||||||
break; // Dark gray
|
|
||||||
case 3:
|
|
||||||
currentLinePixels[pixel] = 0xFF000000;
|
|
||||||
break; // Black
|
|
||||||
default:
|
|
||||||
break; // Default case for safety, should not be reached
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the window to be displayed on a scanline, the following conditions must be met:
|
// For the window to be displayed on a scanline, the following conditions must be met:
|
||||||
@@ -198,10 +203,10 @@ void GameBoy::drawLine() {
|
|||||||
// Window enable bit in LCDC is set
|
// Window enable bit in LCDC is set
|
||||||
//Window
|
//Window
|
||||||
const uint8_t windowY = readOnlyAddressSpace.memoryLayout.WY;
|
const uint8_t windowY = readOnlyAddressSpace.memoryLayout.WY;
|
||||||
const int16_t windowX = readOnlyAddressSpace.memoryLayout.WX - 7;
|
const int16_t windowX = static_cast<int16_t>(readOnlyAddressSpace.memoryLayout.WX - 7);
|
||||||
if (testLCDCBitEnabled(WINDOW_ENABLE) && windowX >= 0 && line >= windowY) {
|
if (LCDCBitEnabled(WINDOW_ENABLE) && windowX >= 0 && windowX < RESOLUTION_X && line >= windowY) {
|
||||||
for (int pixel = windowX; pixel < RESOLUTION_X; pixel++) {
|
for (int pixel = windowX; pixel < RESOLUTION_X; pixel++) {
|
||||||
const uint16_t yIndex = line - windowY;
|
const uint16_t yIndex = windowLineCounter;
|
||||||
const uint16_t windowTileUpper = (yIndex / 8) << 5;
|
const uint16_t windowTileUpper = (yIndex / 8) << 5;
|
||||||
|
|
||||||
const uint16_t xIndex = pixel - windowX;
|
const uint16_t xIndex = pixel - windowX;
|
||||||
@@ -233,24 +238,114 @@ void GameBoy::drawLine() {
|
|||||||
// Apply the BGP register for palette mapping
|
// Apply the BGP register for palette mapping
|
||||||
const uint8_t palette = (readOnlyAddressSpace.memoryLayout.BGP >> (colourNum * 2)) & 0x3;
|
const uint8_t palette = (readOnlyAddressSpace.memoryLayout.BGP >> (colourNum * 2)) & 0x3;
|
||||||
|
|
||||||
switch (palette) {
|
currentLinePixels[pixel] = getColourFromPalette(palette);
|
||||||
case 0:
|
|
||||||
currentLinePixels[pixel] = 0xFFFFFFFF;
|
|
||||||
break; // White
|
|
||||||
case 1:
|
|
||||||
currentLinePixels[pixel] = 0xFFAAAAAA;
|
|
||||||
break; // Light gray
|
|
||||||
case 2:
|
|
||||||
currentLinePixels[pixel] = 0xFF555555;
|
|
||||||
break; // Dark gray
|
|
||||||
case 3:
|
|
||||||
currentLinePixels[pixel] = 0xFF000000;
|
|
||||||
break; // Black
|
|
||||||
default:
|
|
||||||
break; // Default case for safety, should not be reached
|
|
||||||
}
|
|
||||||
windowLineCounter += 1;
|
|
||||||
}
|
}
|
||||||
|
windowLineCounter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// oam/sprites
|
||||||
|
if (LCDCBitEnabled(OBJ_ENABLE)) {
|
||||||
|
uint32_t oamPixels[RESOLUTION_X];
|
||||||
|
std::fill_n(oamPixels, RESOLUTION_X, 0);
|
||||||
|
|
||||||
|
constexpr
|
||||||
|
Word oamAddrStart = 0xFE00;
|
||||||
|
const int spriteHeight = LCDCBitEnabled(OBJ_SIZE) ? 16 : 8;
|
||||||
|
|
||||||
|
int objects[10] = {0};
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
//oam hold 40 objects
|
||||||
|
//find first 10
|
||||||
|
for (int i = 0; i < 40 && found < 10; i++) {
|
||||||
|
const int offset = i * 4;
|
||||||
|
const int yPos = readOnlyAddressSpace[oamAddrStart + offset] - 16;
|
||||||
|
|
||||||
|
if (line >= yPos && line <= yPos + spriteHeight - 1) {
|
||||||
|
objects[found] = oamAddrStart + offset;
|
||||||
|
found += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort by xPos (lower has higher priority when rendering) and then earlier objects
|
||||||
|
for (int i = 0; i < found; i++) {
|
||||||
|
for (int j = 0; j < found - i - 1; j++) {
|
||||||
|
const int xPos1 = readOnlyAddressSpace[objects[j] + 1];
|
||||||
|
const int xPos2 = readOnlyAddressSpace[objects[j + 1] + 1];
|
||||||
|
|
||||||
|
if (xPos1 > xPos2) {
|
||||||
|
const int tmp = objects[j];
|
||||||
|
objects[j] = objects[j + 1];
|
||||||
|
objects[j + 1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int objectIndex = found - 1; objectIndex >= 0; objectIndex--) {
|
||||||
|
const int yPos = readOnlyAddressSpace[objects[objectIndex]] - 16;
|
||||||
|
const int xPos = readOnlyAddressSpace[objects[objectIndex] + 1] - 8;
|
||||||
|
const int tileIndex = readOnlyAddressSpace[objects[objectIndex] + 2];
|
||||||
|
const Byte attributes = readOnlyAddressSpace[objects[objectIndex] + 3];
|
||||||
|
|
||||||
|
const bool priority = oamBitEnabled(attributes, PRIORITY);
|
||||||
|
const bool yFlip = oamBitEnabled(attributes, Y_FLIP);
|
||||||
|
const bool xFlip = oamBitEnabled(attributes, X_FLIP);
|
||||||
|
//get obp0 or obj1
|
||||||
|
const Byte objPalette = oamBitEnabled(attributes, OBJ_PALETTE)
|
||||||
|
? addressSpace.memoryLayout.OBP1
|
||||||
|
: addressSpace.memoryLayout.OBP0;
|
||||||
|
|
||||||
|
for (int pixel = xPos; pixel < RESOLUTION_X && pixel < xPos + 8; pixel++) {
|
||||||
|
constexpr
|
||||||
|
Word objectTileAddr = 0x8000;
|
||||||
|
if (pixel < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const uint32_t colour = currentLinePixels[pixel];
|
||||||
|
const Byte BGP = readOnlyAddressSpace.memoryLayout.BGP;
|
||||||
|
if (priority && (colour == getColourFromPalette((BGP >> 2) & 0x3) ||
|
||||||
|
colour == getColourFromPalette((BGP >> 4) & 0x3) ||
|
||||||
|
colour == getColourFromPalette((BGP >> 6) & 0x3)
|
||||||
|
)) {
|
||||||
|
oamPixels[pixel] = colour;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte objectX = pixel - xPos;
|
||||||
|
Byte objectY = line - yPos;
|
||||||
|
if (xFlip)
|
||||||
|
objectX = 7 - objectX;
|
||||||
|
if (yFlip)
|
||||||
|
objectY = (spriteHeight - 1) - objectY;
|
||||||
|
|
||||||
|
const Word objTileDataAddr = spriteHeight == 8
|
||||||
|
? objectTileAddr + (tileIndex * 16)
|
||||||
|
: objectTileAddr + ((tileIndex & 0xFE) * 16);
|
||||||
|
|
||||||
|
const Byte tileRow = objectY * 2;
|
||||||
|
const Byte tileRowData1 = readOnlyAddressSpace[objTileDataAddr + tileRow];
|
||||||
|
const Byte tileRowData2 = readOnlyAddressSpace[objTileDataAddr + tileRow + 1];
|
||||||
|
|
||||||
|
const int bit = 7 - objectX;
|
||||||
|
const int colorIndex = ((tileRowData2 >> bit) & 1) << 1 | ((tileRowData1 >> bit) & 1);
|
||||||
|
|
||||||
|
// 0 is always transparent
|
||||||
|
if (colorIndex != 0) {
|
||||||
|
const uint8_t paletteColor = (objPalette >> (colorIndex * 2)) & 0x3;
|
||||||
|
const uint32_t finalColor = getColourFromPalette(paletteColor);
|
||||||
|
oamPixels[pixel] = finalColor;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
oamPixels[pixel] = currentLinePixels[pixel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//help ensure correct interaction with "BG OVER OBJ" flag
|
||||||
|
for (int i = 0; i < RESOLUTION_X; i++) {
|
||||||
|
if (oamPixels[i] != currentLinePixels[i] && oamPixels[i] != 0)
|
||||||
|
currentLinePixels[i] = oamPixels[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user