/// <summary> /// Drop extra source words outside of the update region when searching for /// the first edge of a new scan line. Updates _srcNeedsAligned when found. /// To avoid some overhead, is only called in Idle state if _srcNeedsAligned /// is true and the _srcFifo is not empty. [could just inline this in Clock() /// and save a funtion call] /// </summary> private void ClearLeadingSrcWords() { // We only need to align the first edge in a Begin phase; else no-op if (_phase == Phase.Begin || _phase == Phase.BeginEnd || _phase == Phase.BeginEndClear) { ROpWord w = _srcFifo.Peek(); w.Mask = SrcWordMask(w.Index); if ((w.Mask == CombinerFlags.Both) || (_direction == Direction.LeftToRight && w.Mask == CombinerFlags.LeftEdge) || (_direction == Direction.RightToLeft && w.Mask == CombinerFlags.RightEdge)) { _srcNeedsAligned = false; // Found our first edge _halfPipe = w; // Prime the half-pipeline register } if (_srcNeedsAligned) { #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: --> Dropping leading word ({0})", w.Mask); } #endif _srcFifo.Dequeue(); } } }
private RasterOp() { _ropShifter = new Shifter(); // Our own private Idaho _srcFifo = new Queue <ROpWord>(16); // 4 quads (hardware limit) _destFifo = new Queue <ROpWord>(4); // 1 quad _halfPipe = new ROpWord(); // 1 word, for overlap _rdsTable = new CombinerFlags[512]; // 9 bit index _rscTable = new EdgeStrategy[128]; // 7 bit index LoadRasterOpROMs(); }
/// <summary> /// Drop extra words from the Source FIFO that are outside the update region. /// Called in SrcFetch if _leftOver is true. /// </summary> private void ClearExtraSrcWords() { // We only need to clear after the second edge in an End/Clear phase if (_phase == Phase.EndClear || _phase == Phase.BeginEndClear) { if (_srcFifo.Count == 0) { // We've completed our DestFetch and used up all the source words; obviously // there ain't no more to clear, so reset for the start of the next line. _leftOver = false; _srcNeedsAligned = true; #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: --> Reset for first edge"); } #endif } else { // We're here when there are still extra source words left over from the previous // scan line. We only want to drop words through the end of the current quad. ROpWord w = _srcFifo.Peek(); if ((_direction == Direction.LeftToRight && w.Index == 3) || (_direction == Direction.RightToLeft && w.Index == 0)) { _leftOver = false; _srcNeedsAligned = true; #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: --> End of scan line, reset for first edge ({0})", w.Mask); } #endif _srcFifo.Dequeue(); } if (_leftOver) { #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: --> Clearing extra word ({0})", w.Mask); } #endif _srcFifo.Dequeue(); } } } }
/// <summary> /// Populates and returns a ROpWord with the address, index and data /// of the current memory word (but throws an error if MDI is invalid). /// </summary> private ROpWord FetchNextWord() { ROpWord w = new ROpWord(); if (MemoryBoard.Instance.MDIValid) { // The microcode calculates the addresses and initiates fetches; // we just pluck the next incoming word off the MDI. w.Address = MemoryBoard.Instance.MADR; w.Index = MemoryBoard.Instance.MIndex; w.Data = MemoryBoard.Instance.MDI; } else { #if DEBUG // For debugging we just try to continue, but we're pretty hosed at this point... Console.WriteLine("RasterOp: FetchNextWord in {0} while MDI was invalid!", _state); w.Clear(); #else throw new InvalidOperationException("RasterOp: FetchNextWord while MDI was invalid!"); #endif } return(w); }
/// <summary> /// Aligns and combines the Source and Destination words to produce a RasterOp /// result word. Called during DestFetch as each word arrives to avoid /// complications from the delay in the overlapped Fetch/Store cycle. /// </summary> private ROpWord ComputeResult(ROpWord dest) { ROpWord src = dest; // init to silence the Xamarin compiler... EdgeStrategy e = EdgeStrategy.NoPopNoPeek; // assume nothing! n/a in non-edge cases anyway ushort aligned, combined; #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Result dest word: {0}", dest); } #endif #region Align Words // The Emperor has made a critical error, and the time for our word // alignment has come. Admiral Ackbar will explain the plan of attack: // // 1. At the start of a scanline, our source FIFO is aligned and the // half-pipeline register is "primed". This means we should be // guaranteed to have at least the first edge word in the FIFO; // 2. If the source spans the quad but the destination doesn't (or // vice versa), we have to account for the "two edges into one" // problem - pull in the extra source word (source L+R -> dest B) // or hold the source word one extra cycle (dest L+R <- source B); // 3. In all other modes, we should be able to just pop the next word // so the FIFOs move in lock step (while in the source region), he // said, handwaving furiously. // // The RSC03 ROM tells the hardware how to cope with the source FIFO; here // we use a lookup table to deal with all the complicated edge alignment // rules outlined above. // // Many Bothans died to bring us this information... // Look at the destination word to find our edges and set the _leftOver flag switch (dest.Mask) { // Outside the first edge: return dest unmodified, don't touch the source FIFO case CombinerFlags.DontMask: return(dest); // Outside the second edge: return dest unmodified, but pop the source word too. // In some cases we do legitimately clear the last source word, so test for that. case CombinerFlags.Leftover: if (_srcFifo.Count > 0) { src = _srcFifo.Dequeue(); #if DEBUG src.Mask = SrcWordMask(src.Index); // for debugging, not necessary otherwise #endif #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Dropped src word: {0}", src); } #endif } return(dest); // Full word (any phase): should always have a matching source word case CombinerFlags.FullWord: try { src = _srcFifo.Dequeue(); #if DEBUG src.Mask = SrcWordMask(src.Index); // for debugging, not necessary otherwise #endif } catch (InvalidOperationException) { #if DEBUG Console.WriteLine("Source FIFO empty at Full word!"); // continuing will probably fail... #else throw new InvalidOperationException("Source FIFO empty and Result expected"); #endif } break; // Left edge: flag the beginning or end of the update region // Right edge: opposite of left, flag the other end :-) case CombinerFlags.LeftEdge: case CombinerFlags.RightEdge: _leftOver = (dest.Mask == CombinerFlags.LeftEdge && _direction == Direction.RightToLeft) || (dest.Mask == CombinerFlags.RightEdge && _direction == Direction.LeftToRight); if (_srcFifo.Count > 0) { src = _srcFifo.Peek(); src.Mask = SrcWordMask(src.Index); e = GetEdgeStrategy(dest.Mask, src.Mask); } else if (_leftOver && (_extraSrcWord || _xOffset > 0)) { // At the end of a line (either direction) we are peeking forward // but have run out of source words. In this case we copy the half- // pipeline register (in essence, not popping the second edge word) // to provide enough bits to complete the line. src = _halfPipe; } else { #if DEBUG Console.WriteLine("Source FIFO empty at {0} word!", dest.Mask); // continuing will probably fail... #else throw new InvalidOperationException("Source FIFO empty and Result expected"); #endif } break; // Both edges in one word case CombinerFlags.Both: _leftOver = true; // But... but... it's false too! Ow, my head. try { src = _srcFifo.Peek(); src.Mask = SrcWordMask(src.Index); e = GetEdgeStrategy(dest.Mask, src.Mask); } catch (InvalidOperationException) { #if DEBUG Console.WriteLine("Source FIFO empty at Both edges word!"); // continuing will probably fail.. #else throw new InvalidOperationException("Source FIFO empty and Result expected"); #endif } break; // Should never happen if our RDS00 ROM table is correct! case CombinerFlags.Invalid: throw new InvalidOperationException("RasterOp Destination result word has Invalid mask"); } // Pop the current word? if (e == EdgeStrategy.PopPeek || e == EdgeStrategy.PopNoPeek) { _srcFifo.Dequeue(); // Mask was already set } // Peek ahead to the next? if (e == EdgeStrategy.PopPeek || e == EdgeStrategy.NoPopPeek) { src = _srcFifo.Dequeue(); src.Mask = SrcWordMask(src.Index); } #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Next source word: {0}", src); } #endif #endregion #region Align Bits // The destination word is in the update region and our source word is aligned. // If bit alignment is needed, feed the saved word from the half pipe to the // shifter with the current word. Note that the MSB of the combined shifter // inputs is always the leftmost pixel in the update region (so, dependent upon // the direction of transfer). if (_xOffset != 0) { if (_direction == Direction.LeftToRight) { #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Result xtra (hi): {0:x4}", _halfPipe); } #endif _ropShifter.Shift(src.Data, _halfPipe.Data); } else { #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Result xtra (lo): {0:x4}", _halfPipe); } #endif _ropShifter.Shift(_halfPipe.Data, src.Data); } // Save the current source word _halfPipe = src; // Grab the aligned source word from the shifter aligned = _ropShifter.ShifterOutput; } else { // Already in alignment aligned = src.Data; } #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Result aligned: {0:x4}", aligned); } #endif #endregion #region Combine 'em // Finally! Combine source & dest words using the appropriate mask switch (dest.Mask) { case CombinerFlags.LeftEdge: combined = Combine(dest.Data, aligned, _leftEdgeMask); break; case CombinerFlags.RightEdge: combined = Combine(dest.Data, aligned, _rightEdgeMask); break; case CombinerFlags.Both: combined = Combine(dest.Data, aligned, _bothEdgesMask); break; case CombinerFlags.FullWord: combined = Combine(dest.Data, aligned, 0xffff); break; default: combined = dest.Data; // This can't actually happen (silence a warning) break; } #if TRACING_ENABLED if (Trace.TraceOn) { Trace.Log(LogType.RasterOp, "RasterOp: Result combined: {0:x4} (func={1})", combined, _function); } #endif #endregion // For debugging, we return the ROpWord, updated with the combined result. // At some point when this is fully debugged, the dest FIFO could be a // simple queue of ushorts, which could improve efficiency. dest.Data = combined; return(dest); }