mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-13 15:18:23 +00:00
345 lines
9.4 KiB
C#
345 lines
9.4 KiB
C#
/*
|
|
This file is part of ContrAlto.
|
|
|
|
ContrAlto is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ContrAlto is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using Contralto.CPU;
|
|
using Contralto.IO;
|
|
using Contralto.Memory;
|
|
using Contralto.Display;
|
|
using System.IO;
|
|
using System;
|
|
|
|
namespace Contralto
|
|
{
|
|
/// <summary>
|
|
/// Encapsulates all Alto hardware; represents a complete Alto system.
|
|
/// Provides interfaces for controlling and debugging the system externally.
|
|
/// </summary>
|
|
public class AltoSystem
|
|
{
|
|
public AltoSystem()
|
|
{
|
|
_scheduler = new Scheduler();
|
|
|
|
_memBus = new MemoryBus();
|
|
_mem = new Memory.Memory();
|
|
_keyboard = new Keyboard();
|
|
_diskController = new DiskController(this);
|
|
_displayController = new DisplayController(this);
|
|
_mouseAndKeyset = new MouseAndKeyset();
|
|
_ethernetController = new EthernetController(this);
|
|
_orbitController = new OrbitController(this);
|
|
_audioDAC = new AudioDAC(this);
|
|
_organKeyboard = new OrganKeyboard(this);
|
|
_tridentController = new TridentController(this);
|
|
|
|
_cpu = new AltoCPU(this);
|
|
|
|
// Attach memory-mapped devices to the bus
|
|
_memBus.AddDevice(_mem);
|
|
_memBus.AddDevice(_keyboard);
|
|
_memBus.AddDevice(_mouseAndKeyset);
|
|
_memBus.AddDevice(_audioDAC);
|
|
_memBus.AddDevice(_organKeyboard);
|
|
|
|
Reset();
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
_scheduler.Reset();
|
|
|
|
_memBus.Reset();
|
|
_mem.Reset();
|
|
ALU.Reset();
|
|
Shifter.Reset();
|
|
_diskController.Reset();
|
|
_displayController.Reset();
|
|
_keyboard.Reset();
|
|
_mouseAndKeyset.Reset();
|
|
_cpu.Reset();
|
|
_ethernetController.Reset();
|
|
_orbitController.Reset();
|
|
_tridentController.Reset();
|
|
|
|
UCodeMemory.Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attaches an emulated display device to the system.
|
|
/// </summary>
|
|
/// <param name="d"></param>
|
|
public void AttachDisplay(IAltoDisplay d)
|
|
{
|
|
_displayController.AttachDisplay(d);
|
|
}
|
|
|
|
public void DetachDisplay()
|
|
{
|
|
_displayController.DetachDisplay();
|
|
}
|
|
|
|
public void Shutdown(bool commitDisks)
|
|
{
|
|
// Kill any host interface threads that are running.
|
|
if (_ethernetController.HostInterface != null)
|
|
{
|
|
_ethernetController.HostInterface.Shutdown();
|
|
}
|
|
|
|
//
|
|
// Allow the DAC to flush its output
|
|
//
|
|
_audioDAC.Shutdown();
|
|
|
|
if (commitDisks)
|
|
{
|
|
//
|
|
// Save disk contents
|
|
//
|
|
_diskController.CommitDisk(0);
|
|
_diskController.CommitDisk(1);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
_tridentController.CommitDisk(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SingleStep()
|
|
{
|
|
// Run every device that needs attention for a single clock cycle.
|
|
_memBus.Clock();
|
|
_cpu.Clock();
|
|
|
|
// Clock the scheduler
|
|
_scheduler.Clock();
|
|
}
|
|
|
|
public void LoadDiabloDrive(int drive, string path, bool newImage)
|
|
{
|
|
if (drive < 0 || drive > 1)
|
|
{
|
|
throw new InvalidOperationException("drive must be 0 or 1.");
|
|
}
|
|
|
|
//
|
|
// Commit the current disk first
|
|
//
|
|
_diskController.CommitDisk(drive);
|
|
|
|
DiskGeometry geometry;
|
|
|
|
//
|
|
// We select the disk type based on the file extension. Very elegant.
|
|
//
|
|
switch(Path.GetExtension(path).ToLowerInvariant())
|
|
{
|
|
case ".dsk":
|
|
geometry = DiskGeometry.Diablo31;
|
|
break;
|
|
|
|
case ".dsk44":
|
|
geometry = DiskGeometry.Diablo44;
|
|
break;
|
|
|
|
default:
|
|
geometry = DiskGeometry.Diablo31;
|
|
break;
|
|
}
|
|
|
|
|
|
IDiskPack newPack;
|
|
|
|
if (newImage)
|
|
{
|
|
newPack = InMemoryDiskPack.CreateEmpty(geometry, path);
|
|
}
|
|
else
|
|
{
|
|
newPack = InMemoryDiskPack.Load(geometry, path);
|
|
}
|
|
|
|
_diskController.Drives[drive].LoadPack(newPack);
|
|
}
|
|
|
|
public void UnloadDiabloDrive(int drive)
|
|
{
|
|
if (drive < 0 || drive > 1)
|
|
{
|
|
throw new InvalidOperationException("drive must be 0 or 1.");
|
|
}
|
|
|
|
//
|
|
// Commit the current disk first
|
|
//
|
|
_diskController.CommitDisk(drive);
|
|
|
|
_diskController.Drives[drive].UnloadPack();
|
|
}
|
|
|
|
public void LoadTridentDrive(int drive, string path, bool newImage)
|
|
{
|
|
if (drive < 0 || drive > 8)
|
|
{
|
|
throw new InvalidOperationException("drive must be between 0 and 7.");
|
|
}
|
|
|
|
//
|
|
// Commit the current disk first
|
|
//
|
|
_tridentController.CommitDisk(drive);
|
|
|
|
DiskGeometry geometry;
|
|
|
|
//
|
|
// We select the disk type based on the file extension. Very elegant.
|
|
//
|
|
switch (Path.GetExtension(path).ToLowerInvariant())
|
|
{
|
|
case ".dsk80":
|
|
geometry = DiskGeometry.TridentT80;
|
|
break;
|
|
|
|
case ".dsk300":
|
|
geometry = DiskGeometry.TridentT300;
|
|
break;
|
|
|
|
default:
|
|
geometry = DiskGeometry.TridentT80;
|
|
break;
|
|
}
|
|
|
|
|
|
IDiskPack newPack;
|
|
|
|
if (newImage)
|
|
{
|
|
newPack = FileBackedDiskPack.CreateEmpty(geometry, path);
|
|
}
|
|
else
|
|
{
|
|
newPack = FileBackedDiskPack.Load(geometry, path);
|
|
}
|
|
|
|
_tridentController.Drives[drive].LoadPack(newPack);
|
|
}
|
|
|
|
public void UnloadTridentDrive(int drive)
|
|
{
|
|
if (drive < 0 || drive > 7)
|
|
{
|
|
throw new InvalidOperationException("drive must be between 0 and 7.");
|
|
}
|
|
|
|
//
|
|
// Commit the current disk first
|
|
//
|
|
_tridentController.CommitDisk(drive);
|
|
|
|
_tridentController.Drives[drive].UnloadPack();
|
|
}
|
|
|
|
|
|
public void PressBootKeys(AlternateBootType bootType)
|
|
{
|
|
switch(bootType)
|
|
{
|
|
case AlternateBootType.Disk:
|
|
_keyboard.PressBootKeys(Configuration.BootAddress, false);
|
|
break;
|
|
|
|
case AlternateBootType.Ethernet:
|
|
_keyboard.PressBootKeys(Configuration.BootFile, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public AltoCPU CPU
|
|
{
|
|
get { return _cpu; }
|
|
}
|
|
|
|
public Memory.Memory Memory
|
|
{
|
|
get { return _mem; }
|
|
}
|
|
|
|
public MemoryBus MemoryBus
|
|
{
|
|
get { return _memBus; }
|
|
}
|
|
|
|
public DiskController DiskController
|
|
{
|
|
get { return _diskController; }
|
|
}
|
|
|
|
public DisplayController DisplayController
|
|
{
|
|
get { return _displayController; }
|
|
}
|
|
|
|
public Keyboard Keyboard
|
|
{
|
|
get { return _keyboard; }
|
|
}
|
|
|
|
public MouseAndKeyset MouseAndKeyset
|
|
{
|
|
get { return _mouseAndKeyset; }
|
|
}
|
|
|
|
public EthernetController EthernetController
|
|
{
|
|
get { return _ethernetController; }
|
|
}
|
|
|
|
public OrbitController OrbitController
|
|
{
|
|
get { return _orbitController; }
|
|
}
|
|
|
|
public TridentController TridentController
|
|
{
|
|
get { return _tridentController; }
|
|
}
|
|
|
|
public Scheduler Scheduler
|
|
{
|
|
get { return _scheduler; }
|
|
}
|
|
|
|
private AltoCPU _cpu;
|
|
private MemoryBus _memBus;
|
|
private Memory.Memory _mem;
|
|
private Keyboard _keyboard;
|
|
private MouseAndKeyset _mouseAndKeyset;
|
|
private DiskController _diskController;
|
|
private DisplayController _displayController;
|
|
private EthernetController _ethernetController;
|
|
private OrbitController _orbitController;
|
|
private AudioDAC _audioDAC;
|
|
private OrganKeyboard _organKeyboard;
|
|
private TridentController _tridentController;
|
|
|
|
private Scheduler _scheduler;
|
|
}
|
|
}
|