mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-04-18 08:56:40 +00:00
Initial rough implementation of Display hardware and associated tasks. Not really working.
This commit is contained in:
@@ -5,6 +5,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Contralto.Logging;
|
||||
using Contralto.CPU;
|
||||
|
||||
namespace Contralto.Display
|
||||
{
|
||||
@@ -17,25 +18,240 @@ namespace Contralto.Display
|
||||
{
|
||||
public DisplayController(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
_system = system;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void AttachDisplay(Debugger display)
|
||||
{
|
||||
_display = display;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_evenField = true;
|
||||
_clocks = 0;
|
||||
_totalClocks = 0;
|
||||
_state = DisplayState.VerticalBlank;
|
||||
_scanline = 0;
|
||||
_word = 0;
|
||||
_dwtBlocked = false;
|
||||
_dhtBlocked = false;
|
||||
}
|
||||
|
||||
public void Clock()
|
||||
{
|
||||
// TODO: Move the electron beam appropriately and wake up the display tasks
|
||||
// as necessary.
|
||||
|
||||
//
|
||||
// Move the electron beam appropriately and wake up the display tasks
|
||||
// as necessary. Render the display at the end of the field.
|
||||
//
|
||||
|
||||
//
|
||||
// "The display control hardware also generates wakeup requests to the microprocessor
|
||||
// tasking hardware. The vertical task DVT is awakened once per field, at the beginning
|
||||
// of vertical retrace. The display horizontal task is awakened once at the beginning of
|
||||
// each field, and thereafter whenever the display word task blocks. DHT can block itself,
|
||||
// in which case neither it nor the word task can be awakened until the start of the next
|
||||
// field. The wakeup request for the display word task (DWT) is controlled by the state
|
||||
// of the 16 word buffer. If DWT has not executed a BLOCK, if DHT is not blocked, and if
|
||||
// the buffer is not full, DWT wakeups are generated. The hardware sets the buffer empty
|
||||
// and clears the DWT block flip-flop at the beginning of horizontal retrace for every
|
||||
// scan line."
|
||||
//
|
||||
|
||||
//
|
||||
// Check the BLOCK status of the DWT and DHT tasks for the last executed uInstruction.
|
||||
//
|
||||
_dhtBlocked = _system.CPU.CurrentTask.Priority == (int)CPU.TaskType.DisplayHorizontal &&
|
||||
_system.CPU.CurrentTask.BLOCK;
|
||||
|
||||
if (_system.CPU.CurrentTask.Priority == (int)CPU.TaskType.DisplayWord &&
|
||||
_system.CPU.CurrentTask.BLOCK)
|
||||
{
|
||||
//Console.WriteLine("DWT BLOCK");
|
||||
_dwtBlocked = true;
|
||||
|
||||
//
|
||||
// Wake up DHT if it has not blocked itself.
|
||||
//
|
||||
if (!_dhtBlocked)
|
||||
{
|
||||
_system.CPU.WakeupTask(TaskType.DisplayHorizontal);
|
||||
}
|
||||
}
|
||||
|
||||
_clocks++;
|
||||
_totalClocks++;
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case DisplayState.VerticalBlank:
|
||||
// Just killing time
|
||||
if (_clocks > _verticalBlankClocks)
|
||||
{
|
||||
// End of VBlank
|
||||
_clocks -= _verticalBlankClocks;
|
||||
_scanline = _evenField ? 0 : 1;
|
||||
_word = 0;
|
||||
_dataBuffer.Clear();
|
||||
|
||||
// Wake up DVT, DHT
|
||||
_dwtBlocked = false;
|
||||
_dhtBlocked = false;
|
||||
_system.CPU.WakeupTask(TaskType.DisplayVertical);
|
||||
_system.CPU.WakeupTask(TaskType.DisplayHorizontal);
|
||||
_system.CPU.BlockTask(TaskType.DisplayWord);
|
||||
|
||||
// Run MRT
|
||||
_system.CPU.WakeupTask(TaskType.MemoryRefresh);
|
||||
|
||||
// TODO: clear DWT, DHT block flip-flop
|
||||
_state = DisplayState.VisibleScanline;
|
||||
|
||||
//Console.WriteLine("Frame");
|
||||
}
|
||||
break;
|
||||
|
||||
case DisplayState.VisibleScanline:
|
||||
//
|
||||
// "If the DWT has not executed a BLOCK, if DHT is not blocked, and if the
|
||||
// buffer is not full, DWT wakeups are generated."
|
||||
//
|
||||
if (_dataBuffer.Count < 16 &&
|
||||
!_system.CPU.IsBlocked(TaskType.DisplayHorizontal) &&
|
||||
!_dwtBlocked)
|
||||
{
|
||||
//Console.WriteLine("DWT wakeup, {0}", _dataBuffer.Count);
|
||||
_system.CPU.WakeupTask(TaskType.DisplayWord);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't take any words right now, remove Wakeup from
|
||||
// the DWT.
|
||||
if (_dataBuffer.Count >= 16)
|
||||
{
|
||||
//Log.Write(LogComponent.Display, "buffer full, blocking DWT.");
|
||||
}
|
||||
_system.CPU.BlockTask(TaskType.DisplayWord);
|
||||
}
|
||||
|
||||
//
|
||||
// A scanline is 38 words wide; we clock in each word
|
||||
// from the buffer (if present).
|
||||
//
|
||||
if (_clocks > _wordClocks)
|
||||
{
|
||||
_clocks -= _wordClocks;
|
||||
|
||||
ushort displayWord = 0xaaaa;
|
||||
if (_dataBuffer.Count > 0)
|
||||
{
|
||||
// Dequeue a word and draw it to the screen
|
||||
displayWord = _dataBuffer.Dequeue();
|
||||
}
|
||||
|
||||
_display.DrawDisplayWord(_scanline, _word, displayWord);
|
||||
|
||||
_word++;
|
||||
if (_word > 37)
|
||||
{
|
||||
// Done with this line
|
||||
_dwtBlocked = false;
|
||||
_dataBuffer.Clear();
|
||||
_state = DisplayState.HorizontalBlank;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DisplayState.HorizontalBlank:
|
||||
// Kill time until end of HBlank
|
||||
if (_clocks > _horizontalBlankClocks)
|
||||
{
|
||||
_clocks -= _horizontalBlankClocks;
|
||||
|
||||
// Move to next scanline
|
||||
_scanline += 2;
|
||||
_word = 0;
|
||||
|
||||
if (_scanline > 807)
|
||||
{
|
||||
// Done with field, move to vblank, tell display to render
|
||||
_state = DisplayState.VerticalBlank;
|
||||
_evenField = !_evenField;
|
||||
|
||||
_system.CPU.BlockTask(TaskType.DisplayVertical);
|
||||
_system.CPU.BlockTask(TaskType.DisplayHorizontal);
|
||||
_system.CPU.BlockTask(TaskType.DisplayWord);
|
||||
|
||||
_display.RefreshAltoDisplay();
|
||||
|
||||
_frames++;
|
||||
//Log.Write(LogComponent.Display, "Display field completed. {0} total clocks elapsed.", _totalClocks);
|
||||
_totalClocks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = DisplayState.VisibleScanline;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadDDR(ushort word)
|
||||
{
|
||||
_dataBuffer.Enqueue(word);
|
||||
|
||||
bool _evenField;
|
||||
//Console.WriteLine("Enqueue {0}", _dataBuffer.Count);
|
||||
|
||||
// Sanity check: data length should never exceed 16 words.
|
||||
if (_dataBuffer.Count > 16)
|
||||
{
|
||||
_dataBuffer.Dequeue();
|
||||
_system.CPU.BlockTask(TaskType.DisplayWord);
|
||||
}
|
||||
}
|
||||
|
||||
public bool EVENFIELD
|
||||
{
|
||||
get { return _evenField; }
|
||||
}
|
||||
|
||||
private enum DisplayState
|
||||
{
|
||||
Invalid = 0,
|
||||
VerticalBlank,
|
||||
VisibleScanline,
|
||||
HorizontalBlank,
|
||||
}
|
||||
|
||||
private bool _evenField;
|
||||
private double _clocks;
|
||||
private double _totalClocks;
|
||||
private int _frames;
|
||||
private DisplayState _state;
|
||||
|
||||
// Indicates whether the DWT or DHT blocked itself
|
||||
// in which case they cannot be reawakened until the next field.
|
||||
private bool _dwtBlocked;
|
||||
private bool _dhtBlocked;
|
||||
|
||||
private int _scanline;
|
||||
private int _word;
|
||||
|
||||
private Queue<ushort> _dataBuffer = new Queue<ushort>(16);
|
||||
|
||||
private AltoSystem _system;
|
||||
private Debugger _display;
|
||||
|
||||
// Timing constants
|
||||
// 38uS per scanline; 4uS for hblank.
|
||||
// ~35 scanlines for vblank (1330uS)
|
||||
private const double _wordClocks = (34.0 / 38.0) / 0.17; // uSec to clocks
|
||||
private const double _horizontalBlankClocks = 4.0 / 0.17;
|
||||
private const double _verticalBlankClocks = 1333.0 / 0.17;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user