1
0
mirror of https://github.com/livingcomputermuseum/sImlac.git synced 2026-01-11 23:53:24 +00:00
livingcomputermuseum.sImlac/imlac/IO/InterruptFacility.cs
2017-05-30 11:01:26 -07:00

184 lines
6.4 KiB
C#

/*
This file is part of sImlac.
sImlac 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.
sImlac 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 sImlac. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
namespace imlac.IO
{
//
// From the PDS-1 Technical manual:
// The PDS-1 program interrupt facility, when enabled under program control (001 162),
// allows the status flip-flop of an I/O device to force an interrupt upon the completion of the
// current instruction, thereby alleviating the need for repeated flag checking by the main program.
// An interrupt is the equivalent of a subroutine jump to memory location 0000.
// ...
// The interrupt causes the address of the next instruction of the main program to be stored in memory
// location 0000, the next instruction to be taken from 0001, and the program interrupt facility to be
// disabled.
//
// The implementation here is overly simplistic -- on every clock, if enabled, we check the
// implemented system devices to see if their status flags indicate pending data, if the mask enables
// interrupts we do the ISR routine at 0000.
//
// The interrupt status bits are:
// 15 - Light pen (LPA-1)
// 14 - 40 Cycle Sync & End of Display Frame
// 13 - Memory Protect (PMP-1)
// 12 - TTY Receive
// 11 - Keyboard
// 10 - TTY send
// 9 - Joystick, Mouse, or Trackball (JST-1, GMI-1, or TBL-1)
// 8 - Tablet (TBI-1)
// 7 - Punch (PTP-1)
// 6 - Keyboard #2 (KYB-1)
// 5 - TKA IN
// 4 - TKA OUT (TKA-1)
// 3 - 16 Bit input/PTR (GSI-1 or PTR-1)
// 2 - Addressable clock w/input (ACI-1)
// 1 - unused
// 0 - Printer (PRT-1)
//
public class InterruptFacility : IIOTDevice
{
public InterruptFacility(ImlacSystem system)
{
_system = system;
}
public void Reset()
{
_interruptsEnabled = false;
_interruptMask = 0;
_interruptStatus = 0;
_interruptPending = false;
}
public void Clock()
{
if (_interruptsEnabled)
{
// Collect up devices that want to interrupt us.
_interruptStatus = 0;
// bit 14: 40 cycle sync
if (_system.DisplayProcessor.FrameLatch)
{
_interruptStatus |= 0x0002;
}
// bit 12 - TTY rcv
if (_system.TTY.DataReady)
{
_interruptStatus |= 0x0008;
}
// bit 11 - keyboard
if (_system.Keyboard.KeyReady)
{
_interruptStatus |= 0x0010;
}
// bit 2 - ACI-1 (clock)
if (_system.Clock.TimerTriggered)
{
_interruptStatus |= 0x2000;
}
// mask it with our interrupt mask and if non-zero then we have a pending interrupt,
// which we will execute at the start of the next CPU instruction.
if ((_interruptMask & _interruptStatus) != 0)
{
_interruptPending = true;
}
//
// If we have an interrupt pending and the processor is starting the next instruction
// we will interrupt now, otherwise we wait until the processor is ready.
//
if (_interruptPending && _system.Processor.InstructionState == ExecState.Fetch)
{
// save the current PC at 0
_system.Memory.Store(0x0000, _system.Processor.PC);
// continue execution at 1
_system.Processor.PC = 0x0001;
// and disable further interrupts
_interruptsEnabled = false;
_interruptPending = false;
Trace.Log(LogType.Interrupt, "Interrupt triggered (for device(s) {0})", Helpers.ToOctal((ushort)(_interruptMask & _interruptStatus)));
}
}
}
public int[] GetHandledIOTs()
{
return _handledIOTs;
}
public void ExecuteIOT(int iotCode)
{
//
// Dispatch the IOT instruction.
//
switch (iotCode)
{
case 0x71:
_interruptsEnabled = false;
_interruptPending = false;
Trace.Log(LogType.Interrupt, "Interrupts disabled");
break;
case 0x72:
_interruptsEnabled = true;
Trace.Log(LogType.Interrupt, "Interrupts enabled");
break;
case 0x41:
_system.Processor.AC |= (ushort)(_interruptStatus);
Trace.Log(LogType.Interrupt, "Interrupt status {0} copied to AC", Helpers.ToOctal((ushort)_interruptStatus));
break;
case 0x61:
_interruptMask = _system.Processor.AC;
Trace.Log(LogType.Interrupt, "Interrupt mask set to {0}", Helpers.ToOctal((ushort)_interruptMask));
break;
default:
throw new NotImplementedException(String.Format("Unimplemented Interrupt IOT instruction {0:x4}", iotCode));
}
}
private bool _interruptsEnabled;
private bool _interruptPending;
private int _interruptMask;
private int _interruptStatus;
private ImlacSystem _system;
private readonly int[] _handledIOTs =
{
0x41, // read interrupt status bits
0x61, // arm/disarm devices (set interrupt mask)
0x71, // IOF (disable interrupts)
0x72, // ION (enabled masked interrupts)
};
}
}