private void IncrementOamAddrAndCheckOverflow(int increment) { byte temp = (byte)(this.oamAddr + increment); if (temp < this.oamAddr) { this.currentState = SpriteEvaluationState.EvaluationComplete; } else { this.oamAddr = temp; } }
private void DoSpriteEvaluation() { if (this.cycle == 0) { this.currentState = SpriteEvaluationState.EvaluateYCoord; this.spritesFound = 0; this.spriteZeroOnNextScanline = false; } else if (this.cycle <= 64) { this.overrideOamRead = true; if ((this.cycle & 0x01) == 0x01) { // Odd cycle - write 0xFF to secondary OAM this.secondaryOam[this.cycle >> 1] = 0xFF; } } else if (this.cycle <= 256) { this.overrideOamRead = false; if ((this.cycle & 0x01) == 0x01) { // Odd cycle - read data for the current sprite from primary OAM // NOTE: This assumes OAMADDR was reset to 0 as expected during ticks 257-320 of the previous // scanline. If game code modifies OAMADDR afterwards, lots of weird stuff can happen. this.spriteEvalTemp = this.primaryOam[this.oamAddr]; } else { // Even cycle - write to secondary OAM switch (this.currentState) { case SpriteEvaluationState.EvaluateYCoord: this.secondaryOam[this.spritesFound * 4] = spriteEvalTemp; // If the Y coordinate of the sprite is in range, we need to copy the rest of its data // from primary OAM. Ignore results on line 239, as no sprites are shown on line 0. if (this.scanline >= this.spriteEvalTemp && this.scanline < (this.spriteEvalTemp + (this.tallSprites ? 16 : 8)) && this.scanline != 239) { if (!this.spriteZeroOnNextScanline) { this.spriteZeroOnNextScanline = this.cycle == 66; } currentState = SpriteEvaluationState.CopyTileIndex; this.oamAddr++; } else { // Sprite was not in range, check the next one this.IncrementOamAddrAndCheckOverflow(4); } break; case SpriteEvaluationState.CopyTileIndex: this.secondaryOam[(this.spritesFound * 4) + 1] = spriteEvalTemp; this.currentState = SpriteEvaluationState.CopyAttributes; this.IncrementOamAddrAndCheckOverflow(1); break; case SpriteEvaluationState.CopyAttributes: this.secondaryOam[(this.spritesFound * 4) + 2] = spriteEvalTemp; this.currentState = SpriteEvaluationState.CopyXCoord; this.IncrementOamAddrAndCheckOverflow(1); break; case SpriteEvaluationState.CopyXCoord: this.secondaryOam[(this.spritesFound * 4) + 3] = spriteEvalTemp; this.spritesFound++; if (this.spritesFound == 8) { // Found 8 sprites for this line - continue processing to check for overflow this.currentState = SpriteEvaluationState.OverflowCheck; } else { // Look for another sprite on this line this.currentState = SpriteEvaluationState.EvaluateYCoord; } this.IncrementOamAddrAndCheckOverflow(1); break; case SpriteEvaluationState.OverflowCheck: if (this.scanline >= this.spriteEvalTemp && this.scanline < (this.spriteEvalTemp + 8)) { // Found another sprite on a full scanline this.spriteOverflow = true; this.IncrementOamAddrAndCheckOverflow(4); } else { // Sprite isn't on this scanline, check the next one // 2C02 BUG: The OAM address is incremented by 5 in this case, so the Y coordinate // evaluated for further sprites won't actually be the Y coordinate this.IncrementOamAddrAndCheckOverflow(5); } break; case SpriteEvaluationState.EvaluationComplete: // Do nothing break; } } } else if (this.cycle <= 320) { this.oamAddr = 0; } }