Ejemplo n.º 1
0
        /// <summary>
        /// "Silent Boot":  (See Section 9.2.2)
        /// Used when a BOOT STARTF is invoked; resets task MPCs and sets
        /// the starting bank (RAM0 or ROM0) appropriately based on the contents
        /// of RMR.
        /// All other register contents are left as-is.
        /// </summary>
        public void SoftReset()
        {
            // Soft-Reset tasks.
            for (int i = 0; i < _tasks.Length; i++)
            {
                if (_tasks[i] != null)
                {
                    _tasks[i].SoftReset();
                }
            }

            Log.Write(LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
            UCodeMemory.LoadBanksFromRMR(_rmr);

            // Reset RMR after reset.
            _rmr = 0x0;

            // Start in Emulator
            _currentTask = _tasks[0];

            //
            // TODO:
            // This is a hack of sorts, it ensures that the sector task initializes
            // itself as soon as the Emulator task yields after the reset.  (CopyDisk is broken otherwise due to the
            // sector task stomping over the KBLK CopyDisk sets up after the reset.  This is a race of sorts.)
            // Unsure if there is a deeper issue here or if there are other reset semantics
            // in play that are not understood.
            //
            WakeupTask(CPU.TaskType.DiskSector);
        }
Ejemplo n.º 2
0
            /// <summary>
            /// Executes a single microinstruction.
            /// </summary>
            /// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
            public InstructionCompletion ExecuteNext()
            {
                MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType);

                /*
                 * if (_taskType == TaskType.Emulator && UCodeMemory.GetBank(_taskType) == MicrocodeBank.RAM0)
                 * {
                 *  Console.WriteLine("{0}: {1}", Conversion.ToOctal(_mpc), UCodeDisassembler.DisassembleInstruction(instruction, _taskType));
                 * }*/

                return(ExecuteInstruction(instruction));
            }
Ejemplo n.º 3
0
            /// <summary>
            /// ExecuteInstruction causes the Task to execute the next instruction (the one
            /// _mpc is pointing to).  The base implementation covers non-task specific logic,
            /// subclasses (specific task implementations) may provide their own implementations.
            /// </summary>
            /// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
            protected virtual InstructionCompletion ExecuteInstruction(MicroInstruction instruction)
            {
                InstructionCompletion completion = InstructionCompletion.Normal;
                bool   swMode = false;
                ushort aluData;
                ushort nextModifier;
                bool   softReset = _softReset;

                _loadR     = false;
                _loadS     = false;
                _rSelect   = 0;
                _busData   = 0;
                _softReset = false;

                Shifter.Reset();

                //
                // Wait for memory state machine if a memory operation is requested by this instruction and
                // the memory isn't ready yet.
                //
                if (instruction.MemoryAccess)
                {
                    if (!_cpu._system.MemoryBus.Ready(instruction.MemoryOperation))
                    {
                        // Suspend operation for this cycle.
                        return(InstructionCompletion.MemoryWait);
                    }
                }

                // If we have a modified next field from the last instruction, make sure it gets applied to this one.
                nextModifier  = _nextModifier;
                _nextModifier = 0;

                _rSelect = instruction.RSELECT;

                // Give tasks the chance to modify parameters early on (like RSELECT)
                ExecuteSpecialFunction2Early(instruction);

                // Select BUS data.
                if (!instruction.ConstantAccess)
                {
                    // Normal BUS data (not constant ROM access).
                    switch (instruction.BS)
                    {
                    case BusSource.ReadR:
                        _busData = _cpu._r[_rSelect];
                        break;

                    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;
                        break;

                    case BusSource.None:
                        _busData = 0xffff;      // "Enables no source to the BUS, leaving it all ones"
                        break;

                    case BusSource.TaskSpecific1:
                    case BusSource.TaskSpecific2:
                        _busData = GetBusSource(instruction);            // task specific -- call into specific implementation
                        break;

                    case BusSource.ReadMD:
                        _busData = _cpu._system.MemoryBus.ReadMD();
                        break;

                    case BusSource.ReadMouse:
                        // "BUS[12-15]<-MOUSE; BUS[0-13]<- -1"
                        // (Note -- BUS[0-13] appears to be a typo, and should likely be BUS[0-11]).
                        _busData = (ushort)(_cpu._system.MouseAndKeyset.PollMouseBits() | 0xfff0);
                        break;

                    case BusSource.ReadDisp:
                        // "The high-order bits of IR cannot be read directly, but the displacement field of IR (8 low order bits),
                        // may be read with the <-DISP bus source.  If the X field of the instruction is zero (i.e. it specifies page 0
                        // addressing) then the DISP field of the instruction is put on BUS[8-15] and BUS[0-7] is zeroed.  If the X
                        // field of the instruction is nonzero (i.e. it specifies PC-relative or base-register addressing) then the DISP
                        // field is sign-extended and put on the bus."
                        // NB: the "X" field of the NOVA instruction is IR[6-7]
                        _busData = (ushort)(_cpu._ir & 0xff);

                        if ((_cpu._ir & 0x300) != 0)
                        {
                            // sign extend if necessary
                            if ((_cpu._ir & 0x80) != 0)
                            {
                                _busData |= (0xff00);
                            }
                        }
                        break;

                    default:
                        throw new InvalidOperationException(String.Format("Unhandled bus source {0}.", instruction.BS));
                    }
                }
                else
                {
                    // See also comments below.
                    _busData = instruction.ConstantValue;
                }

                // Constant ROM access:
                // "The constant memory is gated to the bus by F1=7, F2=7, or BS>4.  The constant memory is addressed by the
                // (8 bit) concatenation of RSELECT and BS.  The intent in enabling constants with BS>4 is to provide a masking
                // facility, particularly for the <-MOUSE and <-DISP bus sources.  This works because the processor bus ANDs if
                // 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."
                // This is precached by the MicroInstruction object.
                if (instruction.BS4)
                {
                    _busData &= instruction.ConstantValue;
                }

                //
                // Let F2s that need to modify bus data before the ALU runs do their thing.
                // (This is used by the Trident KDTA special functions)
                //
                ExecuteSpecialFunction2PostBusSource(instruction);

                //
                // If there was a RDRAM operation last cycle, we AND in the uCode RAM data here.
                //
                if (_rdRam)
                {
                    _busData &= UCodeMemory.ReadRAM();
                    _rdRam    = false;
                }

                //
                // Let F1s that need to modify bus data before the ALU runs do their thing
                // (this is used by the emulator RSNF and Ethernet EILFCT)
                //
                ExecuteSpecialFunction1Early(instruction);

                // Do ALU operation.
                // Small optimization: if we're just taking bus data across the ALU, we
                // won't go through the ALU.Execute call; this is a decent performance gain for a bit
                // more ugly code...
                if (instruction.ALUF != AluFunction.Bus)
                {
                    aluData = ALU.Execute(instruction.ALUF, _busData, _cpu._t, _skip);
                }
                else
                {
                    aluData   = _busData;
                    ALU.Carry = 0;
                }

                //
                // If there was a WRTRAM operation last cycle, we write the uCode RAM here
                // using the results of this instruction's ALU operation and the M register
                // from the last instruction.
                //
                if (_wrtRam)
                {
                    UCodeMemory.WriteRAM(aluData, _cpu._m);
                    _wrtRam = false;
                }

                //
                // If there was an SWMODE operation last cycle, we set the flag to ensure it
                // takes effect at the end of this cycle.
                //
                if (_swMode)
                {
                    _swMode = false;
                    swMode  = true;
                }

                //
                // Do Special Functions
                //
                switch (instruction.F1)
                {
                case SpecialFunction1.None:
                    // Do nothing.  Well, that was easy.
                    break;

                case SpecialFunction1.LoadMAR:
                    // Do MAR or XMAR reference based on whether F2 is MD<- (for Alto IIs), indicating an extended memory reference.
                    _cpu._system.MemoryBus.LoadMAR(
                        aluData,
                        _taskType,
                        _systemType == SystemType.AltoI ? false : instruction.F2 == SpecialFunction2.StoreMD);
                    break;

                case SpecialFunction1.Task:
                    //
                    // If the first uOp executed after a task switch contains a TASK F1, it does not take effect.
                    // This is observed on the real hardware, and does not appear to be documented.
                    // It also doesn't appear to affect the execution of the standard Alto uCode in any significant
                    // way, but is included here for correctness.
                    //
                    if (!_firstInstructionAfterSwitch)
                    {
                        // Yield to other more important tasks
                        completion = InstructionCompletion.TaskSwitch;
                    }
                    break;

                case SpecialFunction1.Block:
                    // Technically this is to be invoked by the hardware device associated with a task.
                    // That logic would be circuituous and unless there's a good reason not to that is discovered
                    // later, I'm just going to directly block the current task here.
                    _cpu.BlockTask(this._taskType);
                    break;

                case SpecialFunction1.LLSH1:
                    Shifter.SetOperation(ShifterOp.ShiftLeft);
                    break;

                case SpecialFunction1.LRSH1:
                    Shifter.SetOperation(ShifterOp.ShiftRight);
                    break;

                case SpecialFunction1.LLCY8:
                    Shifter.SetOperation(ShifterOp.RotateLeft);
                    break;

                case SpecialFunction1.Constant:
                    // Ignored here; handled by Constant ROM access logic above.
                    break;

                default:
                    // Let the specific task implementation take a crack at this.
                    ExecuteSpecialFunction1(instruction);
                    break;
                }

                switch (instruction.F2)
                {
                case SpecialFunction2.None:
                    // Nothing!
                    break;

                case SpecialFunction2.BusEq0:
                    if (_busData == 0)
                    {
                        _nextModifier |= 1;
                    }
                    break;

                case SpecialFunction2.ShLt0:
                    // Handled below, after the Shifter runs
                    break;

                case SpecialFunction2.ShEq0:
                    // Same as above.
                    break;

                case SpecialFunction2.Bus:
                    // Select bits 6-15 (bits 0-9 in modern parlance) of the bus
                    _nextModifier |= (ushort)(_busData & 0x3ff);
                    break;

                case SpecialFunction2.ALUCY:
                    // ALUC0 is the carry produced by the ALU during the most recent microinstruction
                    // that loaded L.  It is *not* the carry produced during the execution of the microinstruction
                    // that contains the ALUCY function.
                    _nextModifier |= _cpu._aluC0;
                    break;

                case SpecialFunction2.StoreMD:
                    // Special case for XMAR on non-Alto I machines: if F1 is a LoadMAR we do nothing here;
                    // the handler for LoadMAR will load the correct bank.
                    if (_systemType == SystemType.AltoI)
                    {
                        _cpu._system.MemoryBus.LoadMD(_busData);
                    }
                    else if (instruction.F1 != SpecialFunction1.LoadMAR)
                    {
                        _cpu._system.MemoryBus.LoadMD(_busData);
                    }
                    break;

                case SpecialFunction2.Constant:
                    // Ignored here; handled by Constant ROM access logic above.
                    break;

                default:
                    // Let the specific task implementation take a crack at this.
                    ExecuteSpecialFunction2(instruction);
                    break;
                }

                //
                // Do the shifter operation if we're doing an operation that requires the shifter output (loading R, doing a LoadDNS,
                // modifying NEXT based on the shifter output.)
                //
                if (_loadR || instruction.NeedShifterOutput)
                {
                    // A crude optimization:  if there's no shifter operation,
                    // we bypass the call to DoOperation and stuff L in Shifter.Output ourselves.
                    if (Shifter.Op == ShifterOp.None)
                    {
                        Shifter.Output = _cpu._l;
                    }
                    else
                    {
                        Shifter.DoOperation(_cpu._l, _cpu._t);
                    }
                }

                //
                // Handle NEXT modifiers that rely on the Shifter output.
                //
                switch (instruction.F2)
                {
                case SpecialFunction2.ShLt0:
                    //
                    // Note:
                    // "the value of SHIFTER OUTPUT is determined by the value of L as the microinstruction
                    // *begins* execution and the shifter function specified during the  *current* microinstruction.
                    //
                    // Since we haven't modifed L yet, and we've calculated the shifter output above, we're good to go here.
                    //
                    if ((short)Shifter.Output < 0)
                    {
                        _nextModifier |= 1;
                    }
                    break;

                case SpecialFunction2.ShEq0:
                    // See note above.
                    if (Shifter.Output == 0)
                    {
                        _nextModifier |= 1;
                    }
                    break;
                }

                //
                // Write back to registers:
                //
                // Do writeback to selected R register from shifter output.
                //
                if (_loadR)
                {
                    _cpu._r[_rSelect] = Shifter.Output;
                }

                // Do writeback to selected S register from M
                if (_loadS)
                {
                    _cpu._s[_rb][instruction.RSELECT] = _cpu._m;
                }

                // Load T
                if (instruction.LoadT)
                {
                    // Does this operation change the source for T?
                    _cpu._t = instruction.LoadTFromALU ? aluData : _busData;

                    //
                    // Control RAM: "...the control RAM address is specified by the control RAM
                    // address register... which is loaded from the ALU output whenver T is loaded
                    // from its source."
                    //
                    UCodeMemory.LoadControlRAMAddress(aluData);
                }

                // Load L (and M) from ALU outputs.
                if (instruction.LoadL)
                {
                    _cpu._l = aluData;

                    // Only RAM-related tasks can modify M.
                    if (_ramTask)
                    {
                        _cpu._m = aluData;
                    }

                    // Save ALUC0 for use in the next ALUCY special function.
                    _cpu._aluC0 = (ushort)ALU.Carry;
                }

                //
                // Execute special functions that happen late in the cycle
                //
                ExecuteSpecialFunction2Late(instruction);

                //
                // Switch banks if the last instruction had an SWMODE F1;
                // this depends on the value of the NEXT field in this instruction.
                // (And apparently the modifier applied to NEXT in this instruction -- MADTEST expects this.)
                //
                if (swMode)
                {
                    //Log.Write(LogType.Verbose, LogComponent.Microcode, "SWMODE: uPC {0}, next uPC {1} (NEXT is {2})", Conversion.ToOctal(_mpc), Conversion.ToOctal(instruction.NEXT | nextModifier), Conversion.ToOctal(instruction.NEXT));
                    UCodeMemory.SwitchMode((ushort)(instruction.NEXT | nextModifier), _taskType);
                }

                //
                // Do task-specific BLOCK behavior if the last instruction had a BLOCK F1.
                //
                if (instruction.F1 == SpecialFunction1.Block)
                {
                    ExecuteBlock();
                }

                //
                // Select next address, using the address modifier from the last instruction.
                // (Unless a soft reset occurred during this instruction)
                //
                if (!softReset)
                {
                    _mpc = (ushort)(instruction.NEXT | nextModifier);
                }
                else
                {
                    _cpu.SoftReset();
                }

                _firstInstructionAfterSwitch = false;
                return(completion);
            }
Ejemplo n.º 4
0
            /// <summary>
            /// Executes a single microinstruction.
            /// </summary>
            /// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
            public InstructionCompletion ExecuteNext()
            {
                MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType);

                return(ExecuteInstruction(instruction));
            }