From a689b7185a01af25af5946f7e8eb4d6ee41799f4 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Wed, 4 Nov 2015 16:49:42 -0800 Subject: [PATCH] Fixed: Disk address (KADR) and Disk Data (KDATA) registers behave (more) correctly; in particular record commands hare processed properly and KDATA handles reads/writes more sanely. Fixed checksum calculation; due to above changes microcode now correctly validates checksums and boot progresses to read in an entire track's worth of data before dying with a "check failure" error. --- Contralto/CPU/Tasks/DiskTask.cs | 19 +++--- Contralto/CPU/Tasks/EmulatorTask.cs | 2 +- Contralto/Debugger.Designer.cs | 59 +++++++++-------- Contralto/Debugger.cs | 63 ++++++++++++++----- Contralto/Debugger.resx | 2 +- .../Disassembly/boot block disassembly.txt | 12 ++-- Contralto/IO/DiskController.cs | 58 ++++++++++------- Contralto/Memory/Memory.cs | 3 +- 8 files changed, 134 insertions(+), 84 deletions(-) diff --git a/Contralto/CPU/Tasks/DiskTask.cs b/Contralto/CPU/Tasks/DiskTask.cs index 7d695b2..2490f8d 100644 --- a/Contralto/CPU/Tasks/DiskTask.cs +++ b/Contralto/CPU/Tasks/DiskTask.cs @@ -46,8 +46,7 @@ namespace Contralto.CPU return _cpu._system.DiskController.KSTAT; case DiskBusSource.ReadKDATA: - ushort kdata = _cpu._system.DiskController.KDATA; - return kdata; + return _cpu._system.DiskController.KDATA; default: throw new InvalidOperationException(String.Format("Unhandled bus source {0}", bs)); @@ -61,8 +60,7 @@ namespace Contralto.CPU switch (df1) { case DiskF1.LoadKDATA: - // "The KDATA register is loaded from BUS[0-15]." - Log.Write(LogComponent.DiskController, "KDATA loaded with {0}", OctalHelpers.ToOctal(_busData)); + // "The KDATA register is loaded from BUS[0-15]." _cpu._system.DiskController.KDATA = _busData; break; @@ -70,8 +68,7 @@ namespace Contralto.CPU // "This causes the KADR register to be loaded from BUS[8-14]. // in addition, it causes the head address bit to be loaded from KDATA[13]." // (the latter is done by DiskController) - _cpu._system.DiskController.KADR = (ushort)((_busData & 0xfe) >> 1); - Log.Write(LogComponent.DiskController, "KADR bus data is {0}", OctalHelpers.ToOctal(_busData)); + _cpu._system.DiskController.KADR = (ushort)((_busData & 0xff)); break; case DiskF1.LoadKCOMM: @@ -89,9 +86,15 @@ namespace Contralto.CPU case DiskF1.LoadKSTAT: // "KSTAT[12-15] are loaded from BUS[12-15]. (Actually BUS[13] is ORed onto // KSTAT[13].)" + + // From the schematic (and ucode source, based on the values it actually uses for BUS[13]), BUS[13] + // is also inverted. So there's that, too. - // OR in BUS[12-15] after masking in KSTAT[13] so it is ORed in properly. - _cpu._system.DiskController.KSTAT = (ushort)(((_cpu._system.DiskController.KSTAT & 0xfff4)) | (_busData & 0xf)); + // build BUS[12-15] with bit 13 flipped. + int modifiedBusData = (_busData & 0xb) | ((~_busData) & 0x4); + + // OR in BUS[12-15] after masking in KSTAT[13] so it is ORed in properly. + _cpu._system.DiskController.KSTAT = (ushort)(((_cpu._system.DiskController.KSTAT & 0xfff4)) | modifiedBusData); break; case DiskF1.STROBE: diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index 65c310d..4bf1232 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -282,7 +282,7 @@ namespace Contralto.CPU case 0x400: case 0x500: case 0x600: - // NEG, INC, ADC, SUB, ADD, AND - invert the carry bit + // NEG, INC, ADC, SUB, ADD - invert the carry bit if (_cpu._aluC0 != 0) { carry = (~carry) & 0x1; diff --git a/Contralto/Debugger.Designer.cs b/Contralto/Debugger.Designer.cs index da2809d..554b743 100644 --- a/Contralto/Debugger.Designer.cs +++ b/Contralto/Debugger.Designer.cs @@ -79,11 +79,11 @@ this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.ResetButton = new System.Windows.Forms.Button(); this.RunToNextTaskButton = new System.Windows.Forms.Button(); - this.B = new System.Windows.Forms.DataGridViewCheckBoxColumn(); + this.NovaStep = new System.Windows.Forms.Button(); + this.Bkpt = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.Address = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Data = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Disassembly = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.NovaStep = new System.Windows.Forms.Button(); this.Microcode.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this._sourceViewer)).BeginInit(); this.groupBox1.SuspendLayout(); @@ -481,11 +481,13 @@ // this._memoryData.AllowUserToAddRows = false; this._memoryData.AllowUserToDeleteRows = false; + this._memoryData.AllowUserToResizeRows = false; dataGridViewCellStyle12.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this._memoryData.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle12; + this._memoryData.ClipboardCopyMode = System.Windows.Forms.DataGridViewClipboardCopyMode.EnableWithoutHeaderText; this._memoryData.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this._memoryData.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.B, + this.Bkpt, this.Address, this.Data, this.Disassembly}); @@ -503,6 +505,7 @@ this._memoryData.Name = "_memoryData"; this._memoryData.ReadOnly = true; this._memoryData.RowHeadersVisible = false; + this._memoryData.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing; this._memoryData.RowTemplate.DefaultCellStyle.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this._memoryData.RowTemplate.Height = 18; this._memoryData.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; @@ -512,7 +515,9 @@ this._memoryData.ShowRowErrors = false; this._memoryData.Size = new System.Drawing.Size(279, 273); this._memoryData.TabIndex = 0; + this._memoryData.VirtualMode = true; this._memoryData.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.MemoryViewCellClick); + this._memoryData.CellValueNeeded += new System.Windows.Forms.DataGridViewCellValueEventHandler(this.OnMemoryCellValueNeeded); // // label1 // @@ -613,21 +618,31 @@ this.RunToNextTaskButton.UseVisualStyleBackColor = true; this.RunToNextTaskButton.Click += new System.EventHandler(this.RunToNextTaskButton_Click); // - // B + // NovaStep // - this.B.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.B.FalseValue = "false"; - this.B.HeaderText = "B"; - this.B.Name = "B"; - this.B.ReadOnly = true; - this.B.Resizable = System.Windows.Forms.DataGridViewTriState.False; - this.B.ToolTipText = "Breakpoint"; - this.B.TrueValue = "true"; - this.B.Width = 20; + this.NovaStep.Location = new System.Drawing.Point(207, 954); + this.NovaStep.Name = "NovaStep"; + this.NovaStep.Size = new System.Drawing.Size(66, 23); + this.NovaStep.TabIndex = 14; + this.NovaStep.Text = "Nova Step"; + this.NovaStep.UseVisualStyleBackColor = true; + this.NovaStep.Click += new System.EventHandler(this.NovaStep_Click); + // + // Bkpt + // + this.Bkpt.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells; + this.Bkpt.FalseValue = "false"; + this.Bkpt.HeaderText = "B"; + this.Bkpt.Name = "Bkpt"; + this.Bkpt.ReadOnly = true; + this.Bkpt.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.Bkpt.ToolTipText = "Breakpoint"; + this.Bkpt.TrueValue = "true"; + this.Bkpt.Width = 20; // // Address // - this.Address.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.Address.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells; this.Address.HeaderText = "Addr"; this.Address.MinimumWidth = 16; this.Address.Name = "Address"; @@ -638,7 +653,7 @@ // // Data // - this.Data.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.Data.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells; this.Data.HeaderText = "Data"; this.Data.MinimumWidth = 16; this.Data.Name = "Data"; @@ -656,16 +671,6 @@ this.Disassembly.Resizable = System.Windows.Forms.DataGridViewTriState.False; this.Disassembly.ToolTipText = "Disassembly"; // - // NovaStep - // - this.NovaStep.Location = new System.Drawing.Point(207, 954); - this.NovaStep.Name = "NovaStep"; - this.NovaStep.Size = new System.Drawing.Size(66, 23); - this.NovaStep.TabIndex = 14; - this.NovaStep.Text = "Nova Step"; - this.NovaStep.UseVisualStyleBackColor = true; - this.NovaStep.Click += new System.EventHandler(this.NovaStep_Click); - // // Debugger // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -745,10 +750,10 @@ private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; private System.Windows.Forms.Button ResetButton; private System.Windows.Forms.Button RunToNextTaskButton; - private System.Windows.Forms.DataGridViewCheckBoxColumn B; + private System.Windows.Forms.Button NovaStep; + private System.Windows.Forms.DataGridViewCheckBoxColumn Bkpt; private System.Windows.Forms.DataGridViewTextBoxColumn Address; private System.Windows.Forms.DataGridViewTextBoxColumn Data; private System.Windows.Forms.DataGridViewTextBoxColumn Disassembly; - private System.Windows.Forms.Button NovaStep; } } \ No newline at end of file diff --git a/Contralto/Debugger.cs b/Contralto/Debugger.cs index ca50a93..fafc3f3 100644 --- a/Contralto/Debugger.cs +++ b/Contralto/Debugger.cs @@ -112,13 +112,7 @@ namespace Contralto _diskData.Rows[6].Cells[1].Value = OctalHelpers.ToOctal(_system.DiskController.KADR, 6); _diskData.Rows[7].Cells[1].Value = OctalHelpers.ToOctal(_system.DiskController.KCOM, 6); _diskData.Rows[8].Cells[1].Value = OctalHelpers.ToOctal(_system.DiskController.KSTAT, 6); - _diskData.Rows[9].Cells[1].Value = _system.DiskController.RECNO.ToString(); - - for (ushort i = 0; i < 1024; i++) - { - _memoryData.Rows[i].Cells[2].Value = OctalHelpers.ToOctal(_system.MemoryBus.DebugReadWord(i), 6); - _memoryData.Rows[i].Cells[3].Value = Contralto.CPU.Nova.NovaDisassembler.DisassembleInstruction(i, _system.MemoryBus.DebugReadWord(i)); - } + _diskData.Rows[9].Cells[1].Value = _system.DiskController.RECNO.ToString(); // Find the right source line. HighlightMicrocodeSourceLine(_system.CPU.CurrentTask.MPC); @@ -170,15 +164,8 @@ namespace Contralto _taskData.Rows.Add("0", "0", "0"); } - - for (ushort i=0;i<1024;i++) - { - _memoryData.Rows.Add( - false, - OctalHelpers.ToOctal(i, 6), - OctalHelpers.ToOctal(_system.MemoryBus.DebugReadWord(i), 6), - Contralto.CPU.Nova.NovaDisassembler.DisassembleInstruction(i, _system.MemoryBus.DebugReadWord(i))); - } + // TODO: handle extended memory + _memoryData.RowCount = 65536; _otherRegs.Rows.Add("L", "0"); _otherRegs.Rows.Add("T", "0"); @@ -266,11 +253,55 @@ namespace Contralto } } + + /// + /// Fill in memory view on demand. + /// + /// + /// + private void OnMemoryCellValueNeeded(object sender, DataGridViewCellValueEventArgs e) + { + // TODO: handle extended memory + if (e.RowIndex > 65535) + { + // Top of memory, nothing to do + return; + } + + switch(_memoryData.Columns[e.ColumnIndex].Name) + { + case "Bkpt": + e.Value = GetNovaBreakpoint((UInt16)e.RowIndex); + break; + + case "Address": + e.Value = OctalHelpers.ToOctal(e.RowIndex, 6); + break; + + case "Data": + e.Value = OctalHelpers.ToOctal(_system.MemoryBus.DebugReadWord((ushort)e.RowIndex), 6); + + break; + + case "Disassembly": + e.Value = CPU.Nova.NovaDisassembler.DisassembleInstruction( + (ushort)e.RowIndex, + _system.MemoryBus.DebugReadWord((ushort)e.RowIndex)); + break; + } + + } + private void ModifyMicrocodeBreakpoint(UInt16 address, bool set) { _microcodeBreakpointEnabled[address] = set; } + private bool GetNovaBreakpoint(UInt16 address) + { + return _novaBreakpointEnabled[address]; + } + private void ModifyNovaBreakpoint(UInt16 address, bool set) { _novaBreakpointEnabled[address] = set; diff --git a/Contralto/Debugger.resx b/Contralto/Debugger.resx index f85eb74..be4a8f3 100644 --- a/Contralto/Debugger.resx +++ b/Contralto/Debugger.resx @@ -153,7 +153,7 @@ True - + True diff --git a/Contralto/Disassembly/boot block disassembly.txt b/Contralto/Disassembly/boot block disassembly.txt index 7890cd4..3ca0b00 100644 --- a/Contralto/Disassembly/boot block disassembly.txt +++ b/Contralto/Disassembly/boot block disassembly.txt @@ -196,12 +196,12 @@ 213:035000 - LDA 3,AC2+0 ; AC3 gets current DCB address 214:025412 - LDA 1,AC3+12 ; AC1 gets label data (previous disk address) 215:022732 - LDA@ 0,.+-46 ;(147) ; Load AC0 with contents KBLK (check controller status) -216:101014 - MOV# 0,0,SZR ; if zero (error or idle) then go to 199 +216:101014 - MOV# 0,0,SZR ; if zero (idle) then go to 220 217:000406 - JMP .+6 ;(225) ; not idle, go to 225 -220:021401 - LDA 0,AC3+1 -221:101014 - MOV# 0,0,SZR -222:000406 - JMP .+6 ;(230) -223:056724 - STA@ 3,.+-54 ;(147) +220:021401 - LDA 0,AC3+1 ; Load AC3 with next DCB address +221:101014 - MOV# 0,0,SZR ; is this zero? +222:000406 - JMP .+6 ;(230) ; no -- goto 230 +223:056724 - STA@ 3,.+-54 ;(147) ; yes - write DCB to KBLK (tell disk controller to execute DCB) 224:000767 - JMP .+-11 ;(213) 225:125014 - MOV# 1,1,SZR ; we get here when the disk controller is not idle; see if prev. addr is zero @@ -238,7 +238,7 @@ 257:161000 - MOV 3,0 260:024765 - LDA 1,.+-13 ;(245) 261:034411 - LDA 3,.+11 ;(272) -262:061005 - BLT +262:061005 - BLT` 263:020757 - LDA 0,.+-21 ;(242) 264:024457 - LDA 1,.+57 ;(343) 265:034663 - LDA 3,.+-115 ;(150) diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs index 3f8f436..64fd44d 100644 --- a/Contralto/IO/DiskController.cs +++ b/Contralto/IO/DiskController.cs @@ -28,15 +28,20 @@ namespace Contralto.IO fs.Close(); } + /// + /// TODO: this is messy; the read and write sides of KDATA are distinct hardware. + /// According to docs, on a Write, eventually it appears on the Read side during an actual write to the disk + /// but not right away. For now, this never happens (since we don't yet support writing). + /// public ushort KDATA { get { - return _kData; + return _kDataRead; } set { - _kData = value; + _kDataWrite = value; } } @@ -49,29 +54,28 @@ namespace Contralto.IO _recNo = 0; // "In addition, it causes the head address bit to be loaded from KDATA[13]." - _head = (_kData & 0x4) >> 2; + _head = (_kDataWrite & 0x4) >> 2; // "0 normally, 1 if the command is to terminate immediately after the correct cylinder // position is reached (before any data is transferred)." _dataXfer = (_kAdr & 0x2) != 0x2; - Log.Write(LogComponent.DiskController, "KADR set to {0} (Seal {1}, Header {2}, Label {3}, Data {4}, Xfer {5}, Drive {6})", + Log.Write(LogComponent.DiskController, "KADR set to {0} (Header {1}, Label {2}, Data {3}, Xfer {4}, Drive {5})", OctalHelpers.ToOctal(_kAdr), - OctalHelpers.ToOctal((_kAdr & 0xff00) >> 8), OctalHelpers.ToOctal((_kAdr & 0xc0) >> 6), OctalHelpers.ToOctal((_kAdr & 0x30) >> 4), OctalHelpers.ToOctal((_kAdr & 0xc) >> 2), _dataXfer, _kAdr & 0x1); - Log.Write(LogComponent.DiskController, " Disk Address is C/H/S {0}/{1}/{2}, Drive {3} Restore {4}", - (_kData & 0x0ff8) >> 3, - (_kData & 0x4) >> 2, - (_kData & 0xf000) >> 12, - ((_kData & 0x2) >> 1), - (_kData & 0x1)); + Log.Write(LogComponent.DiskController, " -Disk Address is C/H/S {0}/{1}/{2}, Drive {3} Restore {4}", + (_kDataWrite & 0x0ff8) >> 3, + _head, + (_kDataWrite & 0xf000) >> 12, + (_kDataWrite & 0x2) >> 1, + (_kDataWrite & 0x1)); - Log.Write(LogComponent.DiskController, " Selected disk is {0}", ((_kData & 0x2) >> 1) ^ (_kAdr & 0x1)); + Log.Write(LogComponent.DiskController, " -Selected disk is {0}", ((_kDataWrite & 0x2) >> 1) ^ (_kAdr & 0x1)); } } @@ -187,7 +191,8 @@ namespace Contralto.IO _sector = 0; _head = 0; _kStat = 0; - _kData = 0; + _kDataRead = 0; + _kDataWrite = 0; _sendAdr = false; _wdInhib = true; @@ -229,7 +234,7 @@ namespace Contralto.IO _sectorWordIndex = 0; _sectorWordTime = 0.0; - _kData = 13; + _kDataRead = 0; // Load new sector in LoadSector(); @@ -326,7 +331,7 @@ namespace Contralto.IO Log.Write(LogComponent.DiskController, "STROBE: Seek initialized."); - _destCylinder = (_kData & 0x0ff8) >> 3; + _destCylinder = (_kDataWrite & 0x0ff8) >> 3; // set "seek fail" bit based on selected cylinder (if out of bounds) and do not // commence a seek if so. @@ -428,8 +433,8 @@ namespace Contralto.IO { if (!_xferOff) { - Log.Write(LogComponent.DiskWordTask, "Word {0} read into KDATA", OctalHelpers.ToOctal(diskWord)); - _kData = diskWord; + Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Sector {0} Word {1} read into KDATA", _sector, OctalHelpers.ToOctal(diskWord)); + _kDataRead = diskWord; } if (!_wdInhib) @@ -451,7 +456,7 @@ namespace Contralto.IO if (bWakeup) { - Log.Write(LogComponent.DiskWordTask, "Sector task awoken."); + Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Word task awoken."); _system.CPU.WakeupTask(CPU.TaskType.DiskWord); } @@ -477,7 +482,9 @@ namespace Contralto.IO _sectorData[i] = new DataCell(sector.Header[j], CellType.Data); } - _sectorData[_headerOffset + 3].Data = CalculateChecksum(_sectorData, _headerOffset + 1, 2); + ushort checksum = CalculateChecksum(_sectorData, _headerOffset + 1, 2); + _sectorData[_headerOffset + 3].Data = checksum; + Log.Write(LogType.Verbose, LogComponent.DiskController, "Header checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, OctalHelpers.ToOctal(checksum)); // Label (8 words data, 1 word cksum) for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--) @@ -486,7 +493,9 @@ namespace Contralto.IO _sectorData[i] = new DataCell(sector.Label[j], CellType.Data); } - _sectorData[_labelOffset + 9].Data = CalculateChecksum(_sectorData, _labelOffset + 1, 8); + checksum = CalculateChecksum(_sectorData, _labelOffset + 1, 8); + _sectorData[_labelOffset + 9].Data = checksum; + Log.Write(LogType.Verbose, LogComponent.DiskController, "Label checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, OctalHelpers.ToOctal(checksum)); // sector data (256 words data, 1 word cksum) for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--) @@ -495,7 +504,9 @@ namespace Contralto.IO _sectorData[i] = new DataCell(sector.Data[j], CellType.Data); } - _sectorData[_dataOffset + 257].Data = CalculateChecksum(_sectorData, _dataOffset + 1, 256); + checksum = CalculateChecksum(_sectorData, _dataOffset + 1, 256); + _sectorData[_dataOffset + 257].Data = checksum; + Log.Write(LogType.Verbose, LogComponent.DiskController, "Data checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, OctalHelpers.ToOctal(checksum)); } @@ -542,7 +553,7 @@ namespace Contralto.IO // ushort checksum = 0x151; - for(int i = offset; i < length;i++) + for(int i = offset; i < offset + length;i++) { // Sanity check that we're checksumming actual data if (sectorData[i].Type != CellType.Data) @@ -556,7 +567,8 @@ namespace Contralto.IO return checksum; } - private ushort _kData; + private ushort _kDataRead; + private ushort _kDataWrite; private ushort _kAdr; private ushort _kCom; private ushort _kStat; diff --git a/Contralto/Memory/Memory.cs b/Contralto/Memory/Memory.cs index 86c5d70..8c81d64 100644 --- a/Contralto/Memory/Memory.cs +++ b/Contralto/Memory/Memory.cs @@ -25,8 +25,7 @@ namespace Contralto.Memory } public void Load(int address, ushort data) - { - //Log.Write(LogComponent.DiskWordTask, "Word {0} written to {1}", OctalHelpers.ToOctal(data), OctalHelpers.ToOctal(address)); + { _mem[address] = data; }