private void DoVisibleScanline() { if (_dot >= 1 && _dot <= 256) { var tX = (byte)(_cntTile % 32); var tY = (byte)(_cntTile / 32 / 8); var tileId = tY * 32 + tX; var bX = tX / 2; var bY = tY / 2; var aX = bX / 2; var aY = bY / 2; var aId = aY * 8 + aX; var attrShift = (bY % 2 * 2 + bX % 2) * 2; switch (_nextTileFetchStatus) { case TileFetchStatus.Nametable_1: _masterClient.Read((ushort)(_nametableBaseAddr + tileId)); _nextTileFetchStatus = TileFetchStatus.Nametable_2; break; case TileFetchStatus.Nametable_2: _nametable = _masterClient.Value; _nextTileFetchStatus = TileFetchStatus.Attribute_1; break; case TileFetchStatus.Attribute_1: _masterClient.Read((ushort)(0x23C0 + aId)); _nextTileFetchStatus = TileFetchStatus.Attribute_2; break; case TileFetchStatus.Attribute_2: _attribute = (byte)((_masterClient.Value & (0b11 << attrShift)) >> attrShift); _nextTileFetchStatus = TileFetchStatus.BitmapLow_1; break; case TileFetchStatus.BitmapLow_1: _masterClient.Read(GetBgPatternTableAddress()); _nextTileFetchStatus = TileFetchStatus.BitmapLow_2; break; case TileFetchStatus.BitmapLow_2: _bitmapLow = _masterClient.Value; _nextTileFetchStatus = TileFetchStatus.BitmapHigh_1; break; case TileFetchStatus.BitmapHigh_1: _masterClient.Read((ushort)(GetBgPatternTableAddress() + 8)); _nextTileFetchStatus = TileFetchStatus.BitmapHigh_2; break; case TileFetchStatus.BitmapHigh_2: _bitmapHigh = _masterClient.Value; OutputPixel(); _cntTile++; _nextTileFetchStatus = TileFetchStatus.Nametable_1; break; } } }
void IClockSink.OnTick() { if (_enabled) { _masterClient.Read(_srcAddr++); _masterClient.Write(0x2014); _writeCount++; if (_writeCount == 0) { _enabled = false; _masterClient.Release(); } } }
private void DispatchAddressing() { void ReadSource(AddressOperand addressOperand, ref byte result) { switch (addressOperand) { // In Result case AddressOperand.None: break; case AddressOperand.Memory: if (_addressState.Destination != AddressOperand.PC) { _masterClient.Read(_addressState.MemoryAddress); result = _masterClient.Value; } break; case AddressOperand.X: result = Registers.X; break; case AddressOperand.Y: result = Registers.Y; break; case AddressOperand.A: result = Registers.A; break; default: throw new ArgumentException(nameof(_addressState.SourceA)); } } // Read Source ReadSource(_addressState.SourceA, ref _addressState.ResultA); ReadSource(_addressState.SourceB, ref _addressState.ResultB); // Do Operation switch (_addressState.Operation) { case AddressOperation.None: if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; case AddressOperation.Inc: _addressState.ResultA++; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; case AddressOperation.Dec: _addressState.ResultA--; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; case AddressOperation.BitTest: if (_addressState.AffectFlags) { Status.Z = (_addressState.ResultA & _addressState.ResultB) == 0; Status.N = (_addressState.ResultB & 0x80) != 0; Status.V = (_addressState.ResultB & 0x40) != 0; } break; case AddressOperation.Compare: if (_addressState.AffectFlags) { Status.C = _addressState.ResultA >= _addressState.ResultB; UpdateNZ((byte)(_addressState.ResultA - _addressState.ResultB)); } break; case AddressOperation.And: _addressState.ResultA &= _addressState.ResultB; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; case AddressOperation.Or: _addressState.ResultA |= _addressState.ResultB; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; case AddressOperation.Adc: { var value = (short)(_addressState.ResultA + _addressState.ResultB + (Status.C ? 1 : 0)); if (_addressState.AffectFlags) { UpdateCV(_addressState.ResultA, _addressState.ResultB, value); } _addressState.ResultA = (byte)value; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } } break; case AddressOperation.Rol: { var c = Status.C; if (_addressState.AffectFlags) { Status.C = (_addressState.ResultA & 0x80) != 0; } _addressState.ResultA = (byte)((_addressState.ResultA << 1) | (Status.C ? 1 : 0)); if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } } break; case AddressOperation.Asl: { if (_addressState.AffectFlags) { Status.C = (_addressState.ResultA & 0x80) != 0; } _addressState.ResultA = (byte)(_addressState.ResultA << 1); if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } } break; case AddressOperation.Xor: _addressState.ResultA ^= _addressState.ResultB; if (_addressState.AffectFlags) { UpdateNZ(_addressState.ResultA); } break; default: throw new ArgumentException(nameof(_addressState.Operation)); } // Write Destination switch (_addressState.Destination) { case AddressOperand.None: break; case AddressOperand.Memory: _masterClient.Value = _addressState.ResultA; _masterClient.Write(_addressState.MemoryAddress); break; case AddressOperand.X: Registers.X = _addressState.ResultA; break; case AddressOperand.Y: Registers.Y = _addressState.ResultA; break; case AddressOperand.A: Registers.A = _addressState.ResultA; break; case AddressOperand.S: Registers.S = _addressState.ResultA; break; case AddressOperand.PC: Registers.PC = _addressState.MemoryAddress; break; default: throw new ArgumentException(nameof(_addressState.Destination)); } }