mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-18 09:03:01 +00:00
Fixed instruction register decoding SFs for Emulator Task; first stab at handling (most) DNS<- operations including setting SKIP and CARRY flip flops. BLT now succeeds, Nova code in bootstrap is running.
This commit is contained in:
parent
2918ede7ce
commit
c0f23685b1
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -65,7 +66,13 @@ namespace Contralto.CPU
|
||||
|
||||
case AluFunction.BusMinus1:
|
||||
r = bus - 1;
|
||||
_carry = (r < 0) ? 1 : 0;
|
||||
|
||||
// Just for clarification; the datasheet specifies:
|
||||
// "Because subtraction is actually performed by complementary
|
||||
// addition (1s complement), a carry out means borrow; thus,
|
||||
// a carry is generated when there is no underflow and no carry
|
||||
// is generated when there is underflow."
|
||||
_carry = (r <= 0) ? 0 : 1;
|
||||
break;
|
||||
|
||||
case AluFunction.BusPlusT:
|
||||
@ -75,12 +82,12 @@ namespace Contralto.CPU
|
||||
|
||||
case AluFunction.BusMinusT:
|
||||
r = bus - t;
|
||||
_carry = (r < 0) ? 1 : 0;
|
||||
_carry = (r <= 0) ? 0 : 1;
|
||||
break;
|
||||
|
||||
case AluFunction.BusMinusTMinus1:
|
||||
r = bus - t - 1;
|
||||
_carry = (r < 0) ? 1 : 0;
|
||||
_carry = (r <= 0) ? 0 : 1;
|
||||
break;
|
||||
|
||||
case AluFunction.BusPlusTPlus1:
|
||||
|
||||
@ -134,8 +134,8 @@ namespace Contralto.CPU
|
||||
}
|
||||
}
|
||||
|
||||
// And invert lines 1-7
|
||||
return ((~mappedData) & 0x7f) | (mappedData & 0x80);
|
||||
// And invert data lines
|
||||
return (~mappedData) & 0xff;
|
||||
}
|
||||
|
||||
private static RomFile[] _constantRoms =
|
||||
|
||||
@ -12,6 +12,36 @@ namespace Contralto.CPU.Nova
|
||||
/// </summary>
|
||||
public static class NovaDisassembler
|
||||
{
|
||||
|
||||
static NovaDisassembler()
|
||||
{
|
||||
_altoIOTable = new Dictionary<ushort, string>();
|
||||
|
||||
_altoIOTable.Add(0x6210, "MUL");
|
||||
_altoIOTable.Add(0x6211, "DIV");
|
||||
_altoIOTable.Add(0x6000, "CYCLE");
|
||||
_altoIOTable.Add(0x6900, "JSRII");
|
||||
_altoIOTable.Add(0x6a00, "JSRIS");
|
||||
_altoIOTable.Add(0x6e00, "CONVERT");
|
||||
_altoIOTable.Add(0x6203, "RCLK");
|
||||
_altoIOTable.Add(0x6204, "SIO");
|
||||
_altoIOTable.Add(0x6205, "BLT");
|
||||
_altoIOTable.Add(0x6206, "BLKS");
|
||||
_altoIOTable.Add(0x6207, "SIT");
|
||||
_altoIOTable.Add(0x6208, "JMPRAM");
|
||||
_altoIOTable.Add(0x6209, "RDRAM");
|
||||
_altoIOTable.Add(0x620a, "WRTRAM");
|
||||
_altoIOTable.Add(0x620c, "VERSION");
|
||||
_altoIOTable.Add(0x620d, "DREAD");
|
||||
_altoIOTable.Add(0x620e, "DWRITE");
|
||||
_altoIOTable.Add(0x620f, "DEXCH");
|
||||
_altoIOTable.Add(0x6212, "DIAGNOSE1");
|
||||
_altoIOTable.Add(0x6213, "DIAGNOSE2");
|
||||
_altoIOTable.Add(0x6214, "BITBLT");
|
||||
_altoIOTable.Add(0x6215, "XMLDA");
|
||||
_altoIOTable.Add(0x6216, "XMSTA");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disassembles the specified instruction
|
||||
/// </summary>
|
||||
@ -165,6 +195,15 @@ namespace Contralto.CPU.Nova
|
||||
{
|
||||
StringBuilder d = new StringBuilder();
|
||||
|
||||
//
|
||||
// First see if this is an Alto-specific instruction; if so
|
||||
// use those mnemonics. Otherwise decode as a Nova I/O instruction.
|
||||
//
|
||||
if (_altoIOTable.ContainsKey(instructionWord))
|
||||
{
|
||||
return _altoIOTable[instructionWord];
|
||||
}
|
||||
|
||||
// Accumulator
|
||||
int ac = (instructionWord & 0x1800) >> 11;
|
||||
|
||||
@ -239,6 +278,11 @@ namespace Contralto.CPU.Nova
|
||||
return d.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds a map from opcode to Alto I/O mnemonics
|
||||
/// </summary>
|
||||
private static Dictionary<ushort, string> _altoIOTable;
|
||||
|
||||
private enum InstructionClass
|
||||
{
|
||||
MEM = 0x0000,
|
||||
|
||||
@ -29,9 +29,26 @@ namespace Contralto.CPU
|
||||
{
|
||||
_op = ShifterOp.Invalid;
|
||||
_count = 0;
|
||||
_output = 0;
|
||||
_magic = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the result of the last Shifter operation (via DoOperation).
|
||||
/// </summary>
|
||||
public static ushort Output
|
||||
{
|
||||
get { return _output; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last DNS-style Carry bit from the last operation (via DoOperation).
|
||||
/// </summary>
|
||||
public static int DNSCarry
|
||||
{
|
||||
get { return _dnsCarry; }
|
||||
}
|
||||
|
||||
public static void SetOperation(ShifterOp op, int count)
|
||||
{
|
||||
_op = op;
|
||||
@ -52,17 +69,18 @@ namespace Contralto.CPU
|
||||
/// TODO: this is still kind of clumsy.
|
||||
/// </summary>
|
||||
/// <param name="dns"></param>
|
||||
public static void SetDNS(bool dns)
|
||||
public static void SetDNS(bool dns, int carry)
|
||||
{
|
||||
_dns = dns;
|
||||
_dnsCarry = carry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the last specified operation to the specified inputs
|
||||
/// Does the last specified operation to the specified inputs; the result
|
||||
/// can be read from Output.
|
||||
/// </summary>
|
||||
/// <param name="input">Normal input to be shifted</param>
|
||||
/// <param name="t">CPU t register, for MAGIC shifts only</param>
|
||||
/// <returns></returns>
|
||||
/// <param name="t">CPU t register, for MAGIC shifts only</param>
|
||||
public static ushort DoOperation(ushort input, ushort t)
|
||||
{
|
||||
// Sanity check: MAGIC and DNS cannot be set at the same time.
|
||||
@ -70,25 +88,24 @@ namespace Contralto.CPU
|
||||
{
|
||||
throw new InvalidOperationException("Both MAGIC and DNS bits are set.");
|
||||
}
|
||||
|
||||
ushort output = 0;
|
||||
|
||||
switch(_op)
|
||||
{
|
||||
case ShifterOp.Invalid:
|
||||
throw new InvalidOperationException("Shifter op has not been set.");
|
||||
|
||||
case ShifterOp.None:
|
||||
output = input;
|
||||
_output = input;
|
||||
break;
|
||||
|
||||
case ShifterOp.ShiftLeft:
|
||||
output = (ushort)(input << _count);
|
||||
_output = (ushort)(input << _count);
|
||||
|
||||
if (_magic)
|
||||
{
|
||||
// "MAGIC places the high order bit of T into the low order bit of the
|
||||
// shifter output on left shifts..."
|
||||
output |= (ushort)((t & 0x8000) >> 15);
|
||||
_output |= (ushort)((t & 0x8000) >> 15);
|
||||
}
|
||||
else if (_dns)
|
||||
{
|
||||
@ -97,13 +114,13 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case ShifterOp.ShiftRight:
|
||||
output = (ushort)(input >> _count);
|
||||
_output = (ushort)(input >> _count);
|
||||
|
||||
if (_magic)
|
||||
{
|
||||
// "...and places the low order bit of T into the high order bit position
|
||||
// of the shifter output on right shifts."
|
||||
output |= (ushort)((t & 0x1) << 15);
|
||||
_output |= (ushort)((t & 0x1) << 15);
|
||||
}
|
||||
else if (_dns)
|
||||
{
|
||||
@ -113,11 +130,11 @@ namespace Contralto.CPU
|
||||
|
||||
case ShifterOp.RotateLeft:
|
||||
// TODO: optimize, this is stupid
|
||||
output = input;
|
||||
_output = input;
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
int c = (output & 0x8000) >> 15;
|
||||
output = (ushort)((output << 1) | c);
|
||||
int c = (_output & 0x8000) >> 15;
|
||||
_output = (ushort)((_output << 1) | c);
|
||||
}
|
||||
|
||||
if (_dns)
|
||||
@ -128,11 +145,11 @@ namespace Contralto.CPU
|
||||
|
||||
case ShifterOp.RotateRight:
|
||||
// TODO: optimize, this is still stupid
|
||||
output = input;
|
||||
_output = input;
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
int c = (output & 0x1) << 15;
|
||||
output = (ushort)((output >> 1) | c);
|
||||
int c = (_output & 0x1) << 15;
|
||||
_output = (ushort)((_output >> 1) | c);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -140,12 +157,14 @@ namespace Contralto.CPU
|
||||
throw new InvalidOperationException(String.Format("Unhandled shift operation {0}", _op));
|
||||
}
|
||||
|
||||
return output;
|
||||
return _output;
|
||||
}
|
||||
|
||||
private static ShifterOp _op;
|
||||
private static ushort _output;
|
||||
private static int _count;
|
||||
private static bool _magic;
|
||||
private static bool _dns;
|
||||
private static int _dnsCarry;
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +99,13 @@ namespace Contralto.CPU
|
||||
_rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3);
|
||||
break;
|
||||
|
||||
case EmulatorF2.LoadDNS:
|
||||
//
|
||||
// "...DNS also addresses R from (3-IR[3 - 4])..."
|
||||
//
|
||||
_rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,10 +213,8 @@ namespace Contralto.CPU
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the PROM.
|
||||
// We OR in 0x80 because the top address line is controlled by the value of ACSOURCE(2), which is always
|
||||
// 1 here (since ACSOURCE is 14 decimal).
|
||||
_nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8) | 0x80];
|
||||
// Use the PROM.
|
||||
_nextModifier = ControlROM.ACSourceROM[((_cpu._ir & 0x7f00) >> 8)];
|
||||
}
|
||||
break;
|
||||
|
||||
@ -228,8 +233,67 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case EmulatorF2.LoadDNS:
|
||||
// DNS<- modifies the normal shift operations.
|
||||
Shifter.SetDNS(true);
|
||||
// DNS<- does the following:
|
||||
// - modifies the normal shift operations to perform Nova-style shifts (done here)
|
||||
// - addresses R from 3-IR[3-4] (destination AC) (see Early LoadDNS handler)
|
||||
// - stores into R unless IR[12] is set (done here)
|
||||
// - calculates Nova-style CARRY bit (done here)
|
||||
// - sets the SKIP and CARRY flip-flops appropriately (see Late LoadDNS handler)
|
||||
int carry = 0;
|
||||
|
||||
// Also indicates modifying CARRY
|
||||
_loadR = (_cpu._ir & 0x0008) == 0;
|
||||
|
||||
// At this point the ALU has already done its operation but the shifter has not yet run.
|
||||
// We need to set the CARRY bit that will be passed through the shifter appropriately.
|
||||
|
||||
// Select carry input value based on carry control
|
||||
switch(_cpu._ir & 0x30)
|
||||
{
|
||||
case 0x00:
|
||||
// Nothing; CARRY unaffected.
|
||||
carry = _carry;
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
carry = 0; // Z
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
carry = 1; // O
|
||||
break;
|
||||
|
||||
case 0x30:
|
||||
carry = (~_carry) & 0x1; // C
|
||||
break;
|
||||
}
|
||||
|
||||
// Now modify the result based on the current ALU result
|
||||
switch (_cpu._ir & 0x700)
|
||||
{
|
||||
case 0x000:
|
||||
case 0x200:
|
||||
case 0x700:
|
||||
// COM, MOV, AND - Carry unaffected
|
||||
break;
|
||||
|
||||
case 0x100:
|
||||
case 0x300:
|
||||
case 0x400:
|
||||
case 0x500:
|
||||
case 0x600:
|
||||
// NEG, INC, ADC, SUB, ADD, AND - invert the carry bit
|
||||
if (ALU.Carry != 0)
|
||||
{
|
||||
carry = (~carry) & 0x1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Tell the Shifter to do a Nova-style shift with the
|
||||
// given carry bit.
|
||||
Shifter.SetDNS(true, carry);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -238,6 +302,69 @@ namespace Contralto.CPU
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecialFunction2Late(MicroInstruction instruction)
|
||||
{
|
||||
EmulatorF2 ef2 = (EmulatorF2)instruction.F2;
|
||||
switch (ef2)
|
||||
{
|
||||
case EmulatorF2.LoadDNS:
|
||||
//
|
||||
// Set SKIP and CARRY flip-flops based on the final result of the operation after having
|
||||
// passed through the shifter.
|
||||
//
|
||||
ushort result = Shifter.Output;
|
||||
int carry = Shifter.DNSCarry;
|
||||
switch (_cpu._ir & 0x7)
|
||||
{
|
||||
case 0:
|
||||
// None, SKIP is reset
|
||||
_skip = 0;
|
||||
break;
|
||||
|
||||
case 1: // SKP
|
||||
// Always skip
|
||||
_skip = 1;
|
||||
break;
|
||||
|
||||
case 2: // SZC
|
||||
// Skip if carry result is zero
|
||||
_skip = (carry == 0) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 3: // SNC
|
||||
// Skip if carry result is nonzero
|
||||
_skip = carry;
|
||||
break;
|
||||
|
||||
case 4: // SZR
|
||||
_skip = (result == 0) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 5: // SNR
|
||||
_skip = (result != 0) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 6: // SEZ
|
||||
_skip = (result == 0 || carry == 0) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 7: // SBN
|
||||
_skip = (result != 0 && carry != 0) ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_loadR)
|
||||
{
|
||||
// Write carry flag back.
|
||||
_carry = carry;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// From Section 3, Pg. 31:
|
||||
// "The emulator has two additional bits of state, the SKIP and CARRY flip flops. CARRY is distinct from the
|
||||
// microprocessor’s ALUC0 bit, tested by the ALUCY function. CARRY is set or cleared as a function of IR and
|
||||
|
||||
@ -90,16 +90,17 @@ namespace Contralto.CPU
|
||||
/// <returns>True if a task switch has been requested by a TASK instruction, false otherwise.</returns>
|
||||
protected virtual bool ExecuteInstruction(MicroInstruction instruction)
|
||||
{
|
||||
bool nextTask = false;
|
||||
bool loadR = false;
|
||||
bool nextTask = false;
|
||||
ushort aluData = 0;
|
||||
ushort nextModifier = 0;
|
||||
_loadR = false;
|
||||
_loadS = false;
|
||||
_rSelect = 0;
|
||||
_busData = 0;
|
||||
|
||||
|
||||
Shifter.SetMagic(false);
|
||||
Shifter.SetDNS(false, 0);
|
||||
|
||||
//
|
||||
// Wait for memory state machine if a memory operation is requested by this instruction and
|
||||
@ -157,7 +158,7 @@ namespace Contralto.CPU
|
||||
|
||||
case BusSource.LoadR:
|
||||
_busData = 0; // "Loading R forces the BUS to 0 so that an ALU function of 0 and T may be executed simultaneously"
|
||||
loadR = true;
|
||||
_loadR = true;
|
||||
break;
|
||||
|
||||
case BusSource.None:
|
||||
@ -215,7 +216,7 @@ namespace Contralto.CPU
|
||||
// more than one source is gated to it. Up to 32 such mask contans can be provided for each of the four bus sources
|
||||
// > 4.
|
||||
// NOTE also:
|
||||
// "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR aaffect only the
|
||||
// "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR affect only the
|
||||
// selection of R; they do not affect the address supplied to the constant ROM."
|
||||
// Hence we use the unmodified RSELECT value here and above.
|
||||
if ((int)instruction.BS > 4 ||
|
||||
@ -342,9 +343,9 @@ namespace Contralto.CPU
|
||||
// Write back to registers:
|
||||
//
|
||||
// Do writeback to selected R register from shifter output
|
||||
if (loadR)
|
||||
{
|
||||
_cpu._r[_rSelect] = Shifter.DoOperation(_cpu._l, _cpu._t);
|
||||
if (_loadR)
|
||||
{
|
||||
_cpu._r[_rSelect] = Shifter.DoOperation(_cpu._l, _cpu._t);
|
||||
}
|
||||
|
||||
// Do writeback to selected R register from M
|
||||
@ -384,6 +385,11 @@ namespace Contralto.CPU
|
||||
_cpu._aluC0 = (ushort)ALU.Carry;
|
||||
}
|
||||
|
||||
//
|
||||
// Execute special functions that happen late in the cycle
|
||||
//
|
||||
ExecuteSpecialFunction2Late(instruction);
|
||||
|
||||
//
|
||||
// Select next address, using the address modifier from the last instruction.
|
||||
//
|
||||
@ -405,6 +411,11 @@ namespace Contralto.CPU
|
||||
|
||||
protected abstract void ExecuteSpecialFunction2(MicroInstruction instruction);
|
||||
|
||||
protected virtual void ExecuteSpecialFunction2Late(MicroInstruction instruction)
|
||||
{
|
||||
// Nothing by default.
|
||||
}
|
||||
|
||||
//
|
||||
// Per uInstruction Task Data:
|
||||
// Modified by both the base Task implementation and any subclasses
|
||||
@ -415,6 +426,7 @@ namespace Contralto.CPU
|
||||
protected ushort _nextModifier; // Bits ORed onto the NEXT field of the current instruction
|
||||
protected uint _rSelect; // RSELECT field from current instruction, potentially modified by task
|
||||
protected bool _loadS; // Whether to load S from M at and of cycle
|
||||
protected bool _loadR; // Whether to load R from shifter at end of cycle.
|
||||
|
||||
|
||||
//
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user