diff --git a/Contralto.sln b/Contralto.sln index 06a43cd..d5d444c 100644 --- a/Contralto.sln +++ b/Contralto.sln @@ -8,9 +8,6 @@ EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ContraltoSetup", "ContraltoSetup\ContraltoSetup.wixproj", "{47BBC195-80C5-43F3-B691-7D27B0803B84}" EndProject Global - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs index d2f6418..50dee65 100644 --- a/Contralto/AltoSystem.cs +++ b/Contralto/AltoSystem.cs @@ -21,7 +21,7 @@ namespace Contralto _scheduler = new Scheduler(); _memBus = new MemoryBus(); - _mem = new Memory.Memory(); + _mem = new Memory.Memory(); _keyboard = new Keyboard(); _diskController = new DiskController(this); _displayController = new DisplayController(this); @@ -30,6 +30,7 @@ namespace Contralto _cpu = new AltoCPU(this); + // Attach memory-mapped devices to the bus _memBus.AddDevice(_mem); _memBus.AddDevice(_keyboard); @@ -37,8 +38,7 @@ namespace Contralto // Register devices that need clocks _clockableDevices = new List(); - _clockableDevices.Add(_memBus); - _clockableDevices.Add(_displayController); + _clockableDevices.Add(_memBus); _clockableDevices.Add(_cpu); Reset(); @@ -176,11 +176,15 @@ namespace Contralto { get { return _scheduler; } } + + public int _novaInst; private void T_Elapsed(object sender, ElapsedEventArgs e) { System.Console.WriteLine("{0} CPU clocks/sec %{1}. {2} fields/sec", _clocks, ((double)_clocks / 5882353.0) * 100.0, _displayController.Fields); + System.Console.WriteLine("{0} nova instructions/sec.", _novaInst); _clocks = 0; + _novaInst = 0; _displayController.Fields = 0; } diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs index 5e71484..df8ed83 100644 --- a/Contralto/CPU/CPU.cs +++ b/Contralto/CPU/CPU.cs @@ -191,7 +191,7 @@ namespace Contralto.CPU { if (_tasks[(int)task] != null) { - Log.Write(LogComponent.TaskSwitch, "Wakeup enabled for Task {0}", task); + // Log.Write(LogComponent.TaskSwitch, "Wakeup enabled for Task {0}", task); _tasks[(int)task].WakeupTask(); } } @@ -205,7 +205,7 @@ namespace Contralto.CPU { if (_tasks[(int)task] != null) { - Log.Write(LogComponent.TaskSwitch, "Removed wakeup for Task {0}", task); + // Log.Write(LogComponent.TaskSwitch, "Removed wakeup for Task {0}", task); _tasks[(int)task].BlockTask(); } } @@ -239,7 +239,7 @@ namespace Contralto.CPU if (_tasks[i] != null && _tasks[i].Wakeup) { _nextTask = _tasks[i]; - _nextTask.FirstInstructionAfterSwitch = true; + _nextTask.FirstInstructionAfterSwitch = true; /* if (_nextTask != _currentTask && _currentTask != null) diff --git a/Contralto/CPU/Tasks/DisplayVerticalTask.cs b/Contralto/CPU/Tasks/DisplayVerticalTask.cs index 6047fa5..8228f4e 100644 --- a/Contralto/CPU/Tasks/DisplayVerticalTask.cs +++ b/Contralto/CPU/Tasks/DisplayVerticalTask.cs @@ -21,6 +21,9 @@ namespace Contralto.CPU protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction) { // We put ourselves back to sleep immediately once we've started running + // TODO: for this and other similar patterns: rework this so we don't need to + // override ExecuteInstruction just to do this (or similar polling). Virtual calls + // are expensive, especially when millions of them are being made a second. _wakeup = false; return base.ExecuteInstruction(instruction); diff --git a/Contralto/CPU/Tasks/DisplayWordTask.cs b/Contralto/CPU/Tasks/DisplayWordTask.cs index 42095e0..261d726 100644 --- a/Contralto/CPU/Tasks/DisplayWordTask.cs +++ b/Contralto/CPU/Tasks/DisplayWordTask.cs @@ -17,18 +17,7 @@ namespace Contralto.CPU _wakeup = false; _displayController = _cpu._system.DisplayController; - } - - protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction) - { - // We remove our wakeup only if there isn't a wakeup being generated for us by the - // display controller. - _wakeup = (!_displayController.FIFOFULL && - !_displayController.DHTBLOCK && - !_displayController.DWTBLOCK); - - return base.ExecuteInstruction(instruction); - } + } protected override void ExecuteSpecialFunction2(MicroInstruction instruction) { diff --git a/Contralto/CPU/Tasks/MemoryRefreshTask.cs b/Contralto/CPU/Tasks/MemoryRefreshTask.cs index d4e7968..61e9900 100644 --- a/Contralto/CPU/Tasks/MemoryRefreshTask.cs +++ b/Contralto/CPU/Tasks/MemoryRefreshTask.cs @@ -14,6 +14,7 @@ _wakeup = false; } + /* protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction) { // @@ -22,7 +23,7 @@ // "; This version assumes MRTACT is cleared by BLOCK, not MAR<- R37" // return base.ExecuteInstruction(instruction); - } + }*/ } } } diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index e609d24..2d38570 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -250,8 +250,18 @@ namespace Contralto.CPU // ExecuteSpecialFunction1Early(instruction); - // Do ALU operation - aluData = ALU.Execute(instruction.ALUF, _busData, _cpu._t, _skip); + // 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; + } // // If there was a WRTRAM operation last cycle, we write the uCode RAM here @@ -463,9 +473,9 @@ namespace Contralto.CPU // (And apparently the modifier applied to NEXT in this instruction -- MADTEST expects this.) // if (swMode) - { - UCodeMemory.SwitchMode((ushort)(instruction.NEXT | nextModifier), _taskType); - Log.Write(Logging.LogComponent.Microcode, "SWMODE: uPC {0}, next uPC {1}", Conversion.ToOctal(_mpc), Conversion.ToOctal(instruction.NEXT | nextModifier)); + { + // Log.Write(Logging.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); } // diff --git a/Contralto/CPU/UCodeMemory.cs b/Contralto/CPU/UCodeMemory.cs index a937021..edc9be4 100644 --- a/Contralto/CPU/UCodeMemory.cs +++ b/Contralto/CPU/UCodeMemory.cs @@ -127,7 +127,6 @@ namespace Contralto.CPU /// public static void SwitchMode(ushort nextAddress, TaskType task) { - // Log.Write(Logging.LogComponent.Microcode, "SWMODE: Current Bank {0}", _microcodeBank[(int)task]); switch (Configuration.SystemType) @@ -235,7 +234,8 @@ namespace Contralto.CPU // No-op, can't write to ROM. return; } - + + /* Log.Write(Logging.LogComponent.Microcode, "CRAM address for write: Bank {0}, addr {1}", _ramBank, Conversion.ToOctal(_ramAddr)); @@ -243,6 +243,7 @@ namespace Contralto.CPU Log.Write(Logging.LogComponent.Microcode, "CRAM write of low {0}, high {1}", Conversion.ToOctal(low), Conversion.ToOctal(high)); + */ ushort address = (ushort)(_ramAddr + _ramBank * 1024); diff --git a/Contralto/ClassDiagram1.cd b/Contralto/ClassDiagram1.cd deleted file mode 100644 index 7b89419..0000000 --- a/Contralto/ClassDiagram1.cd +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Contralto/ClassDiagram2.cd b/Contralto/ClassDiagram2.cd deleted file mode 100644 index 7b89419..0000000 --- a/Contralto/ClassDiagram2.cd +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index 9842186..0d0567b 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -86,6 +86,9 @@ MinimumRecommendedRules.ruleset true + + Alto.ico + False @@ -194,8 +197,6 @@ - - @@ -401,8 +402,10 @@ - - + + PreserveNewest + + diff --git a/Contralto/Disk/Josh.dsk b/Contralto/Disk/Josh.dsk index 0363bbb..58acc0a 100644 Binary files a/Contralto/Disk/Josh.dsk and b/Contralto/Disk/Josh.dsk differ diff --git a/Contralto/Disk/allgames.dsk b/Contralto/Disk/allgames.dsk index 14546d2..a77bc72 100644 Binary files a/Contralto/Disk/allgames.dsk and b/Contralto/Disk/allgames.dsk differ diff --git a/Contralto/Disk/diag.dsk b/Contralto/Disk/diag.dsk index 4c8fe51..f6f9374 100644 Binary files a/Contralto/Disk/diag.dsk and b/Contralto/Disk/diag.dsk differ diff --git a/Contralto/Display/DisplayController.cs b/Contralto/Display/DisplayController.cs index 39e7779..ec4e756 100644 --- a/Contralto/Display/DisplayController.cs +++ b/Contralto/Display/DisplayController.cs @@ -8,7 +8,7 @@ namespace Contralto.Display /// as it scans across the screen. It implements the logic of the display's sync generator /// and wakes up the DVT and DHT tasks as necessary during a display field. /// - public class DisplayController : IClockable + public class DisplayController { public DisplayController(AltoSystem system) { @@ -35,13 +35,21 @@ namespace Contralto.Display public bool DWTBLOCK { get { return _dwtBlocked; } - set { _dwtBlocked = value; } + set + { + _dwtBlocked = value; + CheckWordWakeup(); + } } public bool DHTBLOCK { get { return _dhtBlocked; } - set { _dhtBlocked = value; } + set + { + _dhtBlocked = value; + CheckWordWakeup(); + } } public bool FIFOFULL @@ -59,6 +67,12 @@ namespace Contralto.Display _word = 0; _dwtBlocked = true; _dhtBlocked = false; + _dataBuffer.Clear(); + + if (_system.CPU != null) + { + CheckWordWakeup(); + } _whiteOnBlack = _whiteOnBlackLatch = false; _lowRes = _lowResLatch = false; @@ -133,8 +147,8 @@ namespace Contralto.Display _dataBuffer.Clear(); - _dwtBlocked = false; - _dhtBlocked = false; + DWTBLOCK = false; + DHTBLOCK = false; // Run CURT _system.CPU.WakeupTask(TaskType.Cursor); @@ -188,6 +202,7 @@ namespace Contralto.Display if (_dataBuffer.Count > 0) { displayWord = _whiteOnBlack ? _dataBuffer.Dequeue() : (ushort)~_dataBuffer.Dequeue(); + CheckWordWakeup(); } _display.DrawDisplayWord(_scanline, _word, displayWord, _lowRes); @@ -232,7 +247,7 @@ namespace Contralto.Display // Schedule HBlank wakeup for end of next HBlank _horizontalWakeup.TimestampNsec = _horizontalBlankDuration - skewNsec; _system.Scheduler.Schedule(_horizontalWakeup); - _dwtBlocked = false; + DWTBLOCK = false; _dataBuffer.Clear(); // Deal with SWMODE latches for the scanline we're about to draw @@ -261,21 +276,28 @@ namespace Contralto.Display } } - public void Clock() - { - // - // "If the DWT has not executed a BLOCK, if DHT is not blocked, and if the - // buffer is not full, DWT wakeups are generated." - // - // TODO: move this logic elsewhere so it doesn't have to be polled. - if (_dataBuffer.Count < 16 && - !_dhtBlocked && - !_dwtBlocked) - { + private void CheckWordWakeup() + { + if (FIFOFULL || + DHTBLOCK || + DWTBLOCK) + { + // If the fifo is full or either the horizontal or word tasks have blocked, + // the word task must be blocked. + _system.CPU.BlockTask(TaskType.DisplayWord); + } + else if (!FIFOFULL && + !DHTBLOCK && + !DWTBLOCK) + { + // + // "If the DWT has not executed a BLOCK, if DHT is not blocked, and if the + // buffer is not full, DWT wakeups are generated." + // _system.CPU.WakeupTask(TaskType.DisplayWord); - } + } } - + public void LoadDDR(ushort word) { _dataBuffer.Enqueue(word); @@ -285,6 +307,8 @@ namespace Contralto.Display { _dataBuffer.Dequeue(); } + + CheckWordWakeup(); } public void LoadXPREG(ushort word) diff --git a/Contralto/UI/AltoWindow.cs b/Contralto/UI/AltoWindow.cs index fa06075..4fad1a5 100644 --- a/Contralto/UI/AltoWindow.cs +++ b/Contralto/UI/AltoWindow.cs @@ -754,12 +754,13 @@ namespace Contralto _keyMap.Add(Keys.F1, AltoKey.BlankTop); _keyMap.Add(Keys.F2, AltoKey.BlankMiddle); _keyMap.Add(Keys.F3, AltoKey.BlankBottom); + _keyMap.Add(Keys.F4, AltoKey.Lock); _keyMap.Add(Keys.Back, AltoKey.BS); _keyMap.Add(Keys.Tab, AltoKey.TAB); _keyMap.Add(Keys.OemSemicolon, AltoKey.Semicolon); _keyMap.Add(Keys.OemOpenBrackets, AltoKey.LBracket); _keyMap.Add(Keys.OemCloseBrackets, AltoKey.RBracket); - _keyMap.Add(Keys.Down, AltoKey.LF); + _keyMap.Add(Keys.Down, AltoKey.LF); } diff --git a/Contralto/UI/Debugger.cs b/Contralto/UI/Debugger.cs index 42c2227..fe0a096 100644 --- a/Contralto/UI/Debugger.cs +++ b/Contralto/UI/Debugger.cs @@ -826,8 +826,13 @@ namespace Contralto } } #endif + if (_system.CPU.CurrentTask.MPC == 0x10) // MPC is 20(octal) meaning a new Nova instruction. + { + // count nova instructions (for profiling) + _system._novaInst++; + } - // See if we need to stop here + // See if we need to stop here if (_execAbort || // The Stop button was hit _microcodeBreakpointEnabled[(int)UCodeMemory.GetBank(_system.CPU.CurrentTask.TaskType),_system.CPU.CurrentTask.MPC] || // A microcode breakpoint was hit (_execType == ExecutionType.NextTask && diff --git a/Contralto/readme.txt b/Contralto/readme.txt index 05dac58..8bf60fb 100644 --- a/Contralto/readme.txt +++ b/Contralto/readme.txt @@ -1,11 +1,12 @@ Readme.txt for Contralto v0.1: 1. Introduction and Overview ----------------------------- +============================ -ContrAlto purports to be a faithful emulation of the Xerox Alto series of +ContrAlto aspires to be a faithful emulation of the Xerox Alto II series of pioneering graphical workstations developed at Xerox PARC in 1973. + 1.1 What's Emulated ------------------- @@ -21,8 +22,9 @@ ContrAlto currently emulates the following Alto hardware: At this time, ContrAlto does not support more exotic hardware such as Diablo 44 and Trident disks, the Orbit printer interface, or the keyset input device. + 2.0 Requirements ----------------- +================ ContrAlto will run on any Windows PC running Windows XP or later, with version 2.0 or later of the .NET Framework installed. .NET should be present by default @@ -34,9 +36,14 @@ it will also run under Mono (http://www.mono-project.com/) on Unix and MacOS, however this usage is not as well tested -- please file bugs if you find them (see Section 7 for details). +A three-button mouse is essential for using most Alto software. On most mice, +the "mousewheel" can be clicked to provide the third (middle) button. Laptops +with trackpads may have configuration options to simulate three buttons but +will likely be clumsy to use. + 3.0 Getting Started -------------------- +=================== To launch ContrAlto, simply click on the shortcut created by the installer on your Start Menu (or Start Screen, depending on your Windows version.) Two @@ -63,19 +70,127 @@ on the display window to start interacting with it using the keyboard and mouse (and if you need your mouse back for other things, press either "Alt" key on your keyboard.) + 3.1 Using the Alto ------------------- +================== 3.1.1 The Basics ---------------- -3.1.2 Reading Materials ------------------------ +3.1.1.1 Mouse +------------- + +ContrAlto uses your computer's mouse to simulate the one the Alto uses. +In order to accurately simulate the mouse, ContrAlto must "capture" the real +mouse, which effectively makes your system's mouse exclusive to the ContrAlto +window. (If you've ever used virtualization software like VMWare, VirtualBox, +Virtual PC, or Parallels, you may be familiar with this behavior.) + +Clicking on the ContrAlto display window will cause ContrAlto to capture the +mouse. Once the mouse has been captured, any mouse movements will be reflected +by the Alto's mouse cursor. While ContrAlto has control of the mouse, you will +not be able to use the mouse for other programs running on your PC. To release +ContrAlto's control, press either "Alt" key on your keyboard. Mouse movements +will return to normal, and you will not be able to control the Alto's mouse +until you click on the ContrAlto display window again. + +The Alto mouse is a three-button mouse. Alto mouse buttons are mapped as you +would expect. If you have a real three-button mouse then this is completely +straightforward. If you have a two button mouse with a "mousewheel" then +a mousewheel click maps to a click of the Alto's middle mouse button. + +If you have a trackpad or other pointing device, using the middle mouse button +may be more complicated. See what configuration options your operating system +and/or drivers provides you for mapping mouse buttons. +3.1.1.2 Keyboard +---------------- + +ContrAlto emulates the 61-key Alto II keyboard. The vast majority of keys +(the alphanumerics and punctuation) work as you would expect them to, but the +Alto has a few special keys, which are described below: + +Alto Key PC Key +-------- ---------- +LF Down Arrow +BS Backspace +Blank-Bottom F3 +Blank-Middle F2 +Blank-Top F1 +<- (arrow) Left Arrow +DEL Del +LOCK F4 + + +3.1.1.3 Disk Packs +------------------ + +A real Alto uses large 14" disk packs for disk storage, each containing +approximately 2.5 megabytes of data. ContrAlto uses files, referred to as +"disk images" or just "images" that contain a bit-for-bit copy of these original +packs. These are a lot easier to use with a modern PC. + +Disk images can be loaded and unloaded via the "System->Drive 0" and +System->Drive 1" menus. + +If you modify the contents of a loaded disk (for example creating new files or +deleting existing ones) the changes will be written back out to the disk image +when a new image is loaded or when ContrAlto exits. For this reason it may be +a good idea to make backups of packs from time to time (just like on the real +machine.) + + +3.1.1.4 Startup, Reset and Shutdown +----------------------------------- + +The system can be started at any time by using the "System->Start" menu, though +in general having a pack image loaded first is a good idea. Similarly, the +"Start->Reset" menu will reset the Alto. + +You can shut down the Alto by closing the ContrAlto window; this will commit +disk changes made to the currently loaded disks back to the disk image files. +However, you will want to be sure the software running on the Alto is ready +to be shutdown first, or else you may lose work or corrupt your disk. + + +3.1.2 Additional Reading Materials +---------------------------------- + +The Bitsavers Alto archive at http://http://bitsavers.org/pdf/xerox/alto is an +excellent repository of original Alto documentation, here are a few documents to +get you started: + +- The "Alto User's Handbook" is indispensable and contains an overview of the + Alto Executive (the OS "shell"), Bravo (great-granddaddy of Microsoft Word) + and other utilities. + http://bitsavers.org/pdf/xerox/alto/Alto_Users_Handbook_Sep79.pdf + +- "Alto Subsystems" documents many of the common Alto programs and tools + ("Subsystems" in Alto parlance) in detail. + http://bitsavers.org/pdf/xerox/alto/AltoSubsystems_Oct79.pdf + +- "Alto Operating System Reference Manual" is useful if you are going to do + any programming for the Alto. + http://bitsavers.org/pdf/xerox/alto/AltoSWRef.part1.pdf + http://bitsavers.org/pdf/xerox/alto/AltoSWRef.part2.pdf + +- "BCPL Reference Manual" is definitely required if you are going to do any + programming on the Alto (in BCPL, anyway...) + http://bitsavers.org/pdf/xerox/alto/bcpl/AltoBCPLdoc.pdf + +- "Bravo Course Outline" is a tutorial that will show you how to use the Bravo + editor. + http://bitsavers.org/pdf/xerox/alto/BravoCourse.pdf + +- The "Alto Hardware Manual" is fun to read through if you're planning on + writing an Alto emulator of your own. If you're into that sort of thing. + http://bitsavers.org/pdf/xerox/alto/AltoHWRef.part1.pdf + http://bitsavers.org/pdf/xerox/alto/AltoHWRef.part2.pdf + 4.0 Configuration ------------------ +================= 4.1 CPU ------- @@ -89,8 +204,13 @@ your keyboard.) ----------- +4.4 Alternate ("keyboard") Boots +-------------------------------- + + + 5.0 Debugger ------------- +============ ContrAlto contains a fairly capable debugger window that can be invoked via the "System->Show Debugger" menu (or Ctrl+Alt+D) at any time. When the debugger @@ -101,6 +221,7 @@ microcode addresses or Nova instruction addresses. Usage of the debugger is mostly straightforward but it is intended for "expert" users only and still has many rough edges. + 5.1 The Controls ---------------- At the very bottom of the debugger window is a row of buttons. These are (from @@ -145,6 +266,7 @@ to make it easy to differentiate. ROM1 contains the listing for the Mesa microcode ROMs. + 5.3 Memory Pane --------------- @@ -152,6 +274,7 @@ The pane near the lower-left (labeled "Memory") shows a view into the main memory of the Alto, providing address/data and an automated disassembly of Alto (Nova) instructions. + 5.4 Breakpoints --------------- @@ -161,6 +284,7 @@ breakpoint. Nova code breakpoints will only work if the standard Nova microcode is running. + 5.5 Everything Else ------------------- @@ -185,9 +309,9 @@ Reserved Memory: 6.0 Known Issues ----------------- +================ 7.0 Reporting Bugs ------------------- +================== diff --git a/ContraltoSetup/Product.wxs b/ContraltoSetup/Product.wxs index 81f2953..a947b76 100644 --- a/ContraltoSetup/Product.wxs +++ b/ContraltoSetup/Product.wxs @@ -10,7 +10,8 @@ - + + @@ -23,8 +24,7 @@ - - + @@ -37,24 +37,37 @@ - - + + + + + + + + + + - - - - + + + + - + + + + + + @@ -87,8 +100,13 @@ - - + + +