From fa80fb4a92905714670edd289ba4dcd57e6a18ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:30:13 +0000 Subject: [PATCH 1/6] Initial plan From ddf829fe1fc40be6a35be4746c71385179870874 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:43:55 +0000 Subject: [PATCH 2/6] Fix disc preservation across reset operations Co-authored-by: mattgodbolt <633973+mattgodbolt@users.noreply.github.com> --- src/6502.js | 28 +++++ tests/unit/test-reset-disc-preservation.js | 130 +++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 tests/unit/test-reset-disc-preservation.js diff --git a/src/6502.js b/src/6502.js index ea0e8956..f679f3cc 100644 --- a/src/6502.js +++ b/src/6502.js @@ -1168,14 +1168,42 @@ export class Cpu6502 extends Base6502 { this.acia.reset(); this.serial.reset(); this.ddNoise.spinDown(); + + // Preserve loaded discs across FDC reset + const savedDiscs = []; + for (let i = 0; i < this.fdc.drives.length; i++) { + savedDiscs[i] = this.fdc.drives[i].disc; + } + this.fdc.powerOnReset(); + + // Restore the preserved discs + for (let i = 0; i < savedDiscs.length; i++) { + if (savedDiscs[i]) { + this.fdc.loadDisc(i, savedDiscs[i]); + } + } + this.adconverter.reset(); this.touchScreen = new TouchScreen(this.scheduler); if (this.model.hasTeletextAdaptor) this.teletextAdaptor = new TeletextAdaptor(this); if (this.econet) this.filestore = new Filestore(this, this.econet); } else { + // Preserve loaded discs across FDC reset + const savedDiscs = []; + for (let i = 0; i < this.fdc.drives.length; i++) { + savedDiscs[i] = this.fdc.drives[i].disc; + } + this.fdc.reset(); + + // Restore the preserved discs + for (let i = 0; i < savedDiscs.length; i++) { + if (savedDiscs[i]) { + this.fdc.loadDisc(i, savedDiscs[i]); + } + } } this.tube.reset(hard); if (hard) { diff --git a/tests/unit/test-reset-disc-preservation.js b/tests/unit/test-reset-disc-preservation.js new file mode 100644 index 00000000..b1c73d93 --- /dev/null +++ b/tests/unit/test-reset-disc-preservation.js @@ -0,0 +1,130 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { Cpu6502 } from "../../src/6502.js"; +import { TEST_6502 } from "../../src/models.js"; +import * as disc from "../../src/fdc.js"; +import { FakeVideo } from "../../src/video.js"; + +describe("Reset disc preservation tests", () => { + let processor; + let mockDisc; + + beforeEach(() => { + // Setup DOM globals for tests + global.window = { localStorage: {} }; + global.document = { getElementById: () => null }; + + // Create processor with minimal setup + const dbgr = { setCpu: () => {}, stop: () => {} }; + const video = new FakeVideo(); + const soundChip = { + toneGenerator: { + setChannel: () => {}, + getChannelValues: () => [0, 0, 0, 0], + }, + reset: () => {}, + setScheduler: () => {}, + }; + const ddNoise = { + spinDown: () => {}, + }; + const config = { extraRoms: [], debugFlags: {} }; + + processor = new Cpu6502(TEST_6502, dbgr, video, soundChip, ddNoise, null, null, config, null); + + // Create a mock disc + const mockDiscData = new Uint8Array(1024); + mockDiscData.fill(0x42); + mockDisc = disc.discFor(processor.fdc, "test.ssd", mockDiscData); + }); + + it("should preserve disc after soft reset", () => { + // Load disc + processor.fdc.loadDisc(0, mockDisc); + expect(processor.fdc.drives[0].disc).toBeDefined(); + + // Store reference to original disc + const originalDisc = processor.fdc.drives[0].disc; + + // Perform soft reset + processor.reset(false); + + // Check if disc is still loaded + expect(processor.fdc.drives[0].disc).toBeDefined(); + // Should be the same disc object (reference preserved) + expect(processor.fdc.drives[0].disc).toBe(originalDisc); + }); + + it("should preserve disc after hard reset", () => { + // Load disc + processor.fdc.loadDisc(0, mockDisc); + expect(processor.fdc.drives[0].disc).toBeDefined(); + + // Store reference to original disc + const originalDisc = processor.fdc.drives[0].disc; + + // Perform hard reset + processor.reset(true); + + // Check if disc is still loaded + expect(processor.fdc.drives[0].disc).toBeDefined(); + // Should be the same disc object (reference preserved) + expect(processor.fdc.drives[0].disc).toBe(originalDisc); + }); + + it("should preserve discs on both drives after reset", () => { + // Create second disc + const mockDiscData2 = new Uint8Array(1024); + mockDiscData2.fill(0x84); + const mockDisc2 = disc.discFor(processor.fdc, "test2.ssd", mockDiscData2); + + // Load discs on both drives + processor.fdc.loadDisc(0, mockDisc); + processor.fdc.loadDisc(1, mockDisc2); + expect(processor.fdc.drives[0].disc).toBeDefined(); + expect(processor.fdc.drives[1].disc).toBeDefined(); + + // Store references to original discs + const originalDisc0 = processor.fdc.drives[0].disc; + const originalDisc1 = processor.fdc.drives[1].disc; + + // Perform hard reset + processor.reset(true); + + // Check if both discs are still loaded + expect(processor.fdc.drives[0].disc).toBeDefined(); + expect(processor.fdc.drives[1].disc).toBeDefined(); + // Should be the same disc objects (references preserved) + expect(processor.fdc.drives[0].disc).toBe(originalDisc0); + expect(processor.fdc.drives[1].disc).toBe(originalDisc1); + }); + + it("should handle reset when no discs are loaded", () => { + // Ensure no discs are loaded + expect(processor.fdc.drives[0].disc).toBeUndefined(); + expect(processor.fdc.drives[1].disc).toBeUndefined(); + + // Perform reset - should not crash + processor.reset(true); + + // Should still have no discs loaded + expect(processor.fdc.drives[0].disc).toBeUndefined(); + expect(processor.fdc.drives[1].disc).toBeUndefined(); + }); + + it("should preserve mixed loaded/unloaded state", () => { + // Load disc only on drive 0 + processor.fdc.loadDisc(0, mockDisc); + expect(processor.fdc.drives[0].disc).toBeDefined(); + expect(processor.fdc.drives[1].disc).toBeUndefined(); + + const originalDisc = processor.fdc.drives[0].disc; + + // Perform reset + processor.reset(true); + + // Drive 0 should still have disc, drive 1 should still be empty + expect(processor.fdc.drives[0].disc).toBeDefined(); + expect(processor.fdc.drives[1].disc).toBeUndefined(); + expect(processor.fdc.drives[0].disc).toBe(originalDisc); + }); +}); From 0e88f39bba9dbc01ccac89ee516fcb0a86c1630c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:48:09 +0000 Subject: [PATCH 3/6] Refactor disc preservation to use helper function Co-authored-by: mattgodbolt <633973+mattgodbolt@users.noreply.github.com> --- src/6502.js | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/6502.js b/src/6502.js index f679f3cc..1f2a8dce 100644 --- a/src/6502.js +++ b/src/6502.js @@ -1128,6 +1128,24 @@ export class Cpu6502 extends Base6502 { this.resetLine = !resetOn; } + _resetFdcWithDiscPreservation(resetFn) { + // Preserve loaded discs across FDC reset + const savedDiscs = []; + for (let i = 0; i < this.fdc.drives.length; i++) { + savedDiscs[i] = this.fdc.drives[i].disc; + } + + // Perform FDC reset + resetFn(); + + // Restore the preserved discs + for (let i = 0; i < savedDiscs.length; i++) { + if (savedDiscs[i]) { + this.fdc.loadDisc(i, savedDiscs[i]); + } + } + } + reset(hard) { if (hard) { // On the Master, opcodes executing from 0xc000 - 0xdfff can optionally have their memory accesses @@ -1169,20 +1187,7 @@ export class Cpu6502 extends Base6502 { this.serial.reset(); this.ddNoise.spinDown(); - // Preserve loaded discs across FDC reset - const savedDiscs = []; - for (let i = 0; i < this.fdc.drives.length; i++) { - savedDiscs[i] = this.fdc.drives[i].disc; - } - - this.fdc.powerOnReset(); - - // Restore the preserved discs - for (let i = 0; i < savedDiscs.length; i++) { - if (savedDiscs[i]) { - this.fdc.loadDisc(i, savedDiscs[i]); - } - } + this._resetFdcWithDiscPreservation(() => this.fdc.powerOnReset()); this.adconverter.reset(); @@ -1190,20 +1195,7 @@ export class Cpu6502 extends Base6502 { if (this.model.hasTeletextAdaptor) this.teletextAdaptor = new TeletextAdaptor(this); if (this.econet) this.filestore = new Filestore(this, this.econet); } else { - // Preserve loaded discs across FDC reset - const savedDiscs = []; - for (let i = 0; i < this.fdc.drives.length; i++) { - savedDiscs[i] = this.fdc.drives[i].disc; - } - - this.fdc.reset(); - - // Restore the preserved discs - for (let i = 0; i < savedDiscs.length; i++) { - if (savedDiscs[i]) { - this.fdc.loadDisc(i, savedDiscs[i]); - } - } + this._resetFdcWithDiscPreservation(() => this.fdc.reset()); } this.tube.reset(hard); if (hard) { From a979d512a7e19f83edf28fc6a36b48ae8b95721e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:56:50 +0000 Subject: [PATCH 4/6] Move disc preservation logic into FDC reset methods Co-authored-by: mattgodbolt <633973+mattgodbolt@users.noreply.github.com> --- src/6502.js | 22 ++-------------------- src/intel-fdc.js | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/6502.js b/src/6502.js index 1f2a8dce..81fa5be8 100644 --- a/src/6502.js +++ b/src/6502.js @@ -1128,24 +1128,6 @@ export class Cpu6502 extends Base6502 { this.resetLine = !resetOn; } - _resetFdcWithDiscPreservation(resetFn) { - // Preserve loaded discs across FDC reset - const savedDiscs = []; - for (let i = 0; i < this.fdc.drives.length; i++) { - savedDiscs[i] = this.fdc.drives[i].disc; - } - - // Perform FDC reset - resetFn(); - - // Restore the preserved discs - for (let i = 0; i < savedDiscs.length; i++) { - if (savedDiscs[i]) { - this.fdc.loadDisc(i, savedDiscs[i]); - } - } - } - reset(hard) { if (hard) { // On the Master, opcodes executing from 0xc000 - 0xdfff can optionally have their memory accesses @@ -1187,7 +1169,7 @@ export class Cpu6502 extends Base6502 { this.serial.reset(); this.ddNoise.spinDown(); - this._resetFdcWithDiscPreservation(() => this.fdc.powerOnReset()); + this.fdc.powerOnReset(); this.adconverter.reset(); @@ -1195,7 +1177,7 @@ export class Cpu6502 extends Base6502 { if (this.model.hasTeletextAdaptor) this.teletextAdaptor = new TeletextAdaptor(this); if (this.econet) this.filestore = new Filestore(this, this.econet); } else { - this._resetFdcWithDiscPreservation(() => this.fdc.reset()); + this.fdc.reset(); } this.tube.reset(hard); if (hard) { diff --git a/src/intel-fdc.js b/src/intel-fdc.js index c45b85d8..d91afeb8 100644 --- a/src/intel-fdc.js +++ b/src/intel-fdc.js @@ -348,6 +348,12 @@ export class IntelFdc { } powerOnReset() { + // Preserve loaded discs across power-on reset + const savedDiscs = []; + for (let i = 0; i < this._drives.length; i++) { + savedDiscs[i] = this._drives[i].disc; + } + // The reset line does most things. this.reset(); this._regs.fill(0); @@ -356,9 +362,22 @@ export class IntelFdc { this._mmioClocks = 0; this._stateCount = 0; this._stateIsIndexPulse = false; + + // Restore the preserved discs + for (let i = 0; i < savedDiscs.length; i++) { + if (savedDiscs[i]) { + this.loadDisc(i, savedDiscs[i]); + } + } } reset() { + // Preserve loaded discs across reset + const savedDiscs = []; + for (let i = 0; i < this._drives.length; i++) { + savedDiscs[i] = this._drives[i].disc; + } + // Abort any in-progress command. this._commandAbort(); this._clearCallbacks(); @@ -366,6 +385,13 @@ export class IntelFdc { this._setDriveOut(0); // On a real machine, status appears to be cleared but result and data not. this._statusLower(this.internalStatus); + + // Restore the preserved discs + for (let i = 0; i < savedDiscs.length; i++) { + if (savedDiscs[i]) { + this.loadDisc(i, savedDiscs[i]); + } + } } get internalStatus() { From 465475acc78186ad7c5b0a2b4966ec8324177766 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:12:22 +0000 Subject: [PATCH 5/6] Remove unnecessary disc preservation logic from FDC reset methods Co-authored-by: mattgodbolt <633973+mattgodbolt@users.noreply.github.com> --- src/6502.js | 2 -- src/intel-fdc.js | 26 -------------------------- 2 files changed, 28 deletions(-) diff --git a/src/6502.js b/src/6502.js index 81fa5be8..ea0e8956 100644 --- a/src/6502.js +++ b/src/6502.js @@ -1168,9 +1168,7 @@ export class Cpu6502 extends Base6502 { this.acia.reset(); this.serial.reset(); this.ddNoise.spinDown(); - this.fdc.powerOnReset(); - this.adconverter.reset(); this.touchScreen = new TouchScreen(this.scheduler); diff --git a/src/intel-fdc.js b/src/intel-fdc.js index d91afeb8..c45b85d8 100644 --- a/src/intel-fdc.js +++ b/src/intel-fdc.js @@ -348,12 +348,6 @@ export class IntelFdc { } powerOnReset() { - // Preserve loaded discs across power-on reset - const savedDiscs = []; - for (let i = 0; i < this._drives.length; i++) { - savedDiscs[i] = this._drives[i].disc; - } - // The reset line does most things. this.reset(); this._regs.fill(0); @@ -362,22 +356,9 @@ export class IntelFdc { this._mmioClocks = 0; this._stateCount = 0; this._stateIsIndexPulse = false; - - // Restore the preserved discs - for (let i = 0; i < savedDiscs.length; i++) { - if (savedDiscs[i]) { - this.loadDisc(i, savedDiscs[i]); - } - } } reset() { - // Preserve loaded discs across reset - const savedDiscs = []; - for (let i = 0; i < this._drives.length; i++) { - savedDiscs[i] = this._drives[i].disc; - } - // Abort any in-progress command. this._commandAbort(); this._clearCallbacks(); @@ -385,13 +366,6 @@ export class IntelFdc { this._setDriveOut(0); // On a real machine, status appears to be cleared but result and data not. this._statusLower(this.internalStatus); - - // Restore the preserved discs - for (let i = 0; i < savedDiscs.length; i++) { - if (savedDiscs[i]) { - this.loadDisc(i, savedDiscs[i]); - } - } } get internalStatus() { From b375e58abfe5a5b29f0ef0e12628359b5846aa2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:34:22 +0000 Subject: [PATCH 6/6] Remove unnecessary disc preservation test file Co-authored-by: mattgodbolt <633973+mattgodbolt@users.noreply.github.com> --- tests/unit/test-reset-disc-preservation.js | 130 --------------------- 1 file changed, 130 deletions(-) delete mode 100644 tests/unit/test-reset-disc-preservation.js diff --git a/tests/unit/test-reset-disc-preservation.js b/tests/unit/test-reset-disc-preservation.js deleted file mode 100644 index b1c73d93..00000000 --- a/tests/unit/test-reset-disc-preservation.js +++ /dev/null @@ -1,130 +0,0 @@ -import { beforeEach, describe, expect, it } from "vitest"; -import { Cpu6502 } from "../../src/6502.js"; -import { TEST_6502 } from "../../src/models.js"; -import * as disc from "../../src/fdc.js"; -import { FakeVideo } from "../../src/video.js"; - -describe("Reset disc preservation tests", () => { - let processor; - let mockDisc; - - beforeEach(() => { - // Setup DOM globals for tests - global.window = { localStorage: {} }; - global.document = { getElementById: () => null }; - - // Create processor with minimal setup - const dbgr = { setCpu: () => {}, stop: () => {} }; - const video = new FakeVideo(); - const soundChip = { - toneGenerator: { - setChannel: () => {}, - getChannelValues: () => [0, 0, 0, 0], - }, - reset: () => {}, - setScheduler: () => {}, - }; - const ddNoise = { - spinDown: () => {}, - }; - const config = { extraRoms: [], debugFlags: {} }; - - processor = new Cpu6502(TEST_6502, dbgr, video, soundChip, ddNoise, null, null, config, null); - - // Create a mock disc - const mockDiscData = new Uint8Array(1024); - mockDiscData.fill(0x42); - mockDisc = disc.discFor(processor.fdc, "test.ssd", mockDiscData); - }); - - it("should preserve disc after soft reset", () => { - // Load disc - processor.fdc.loadDisc(0, mockDisc); - expect(processor.fdc.drives[0].disc).toBeDefined(); - - // Store reference to original disc - const originalDisc = processor.fdc.drives[0].disc; - - // Perform soft reset - processor.reset(false); - - // Check if disc is still loaded - expect(processor.fdc.drives[0].disc).toBeDefined(); - // Should be the same disc object (reference preserved) - expect(processor.fdc.drives[0].disc).toBe(originalDisc); - }); - - it("should preserve disc after hard reset", () => { - // Load disc - processor.fdc.loadDisc(0, mockDisc); - expect(processor.fdc.drives[0].disc).toBeDefined(); - - // Store reference to original disc - const originalDisc = processor.fdc.drives[0].disc; - - // Perform hard reset - processor.reset(true); - - // Check if disc is still loaded - expect(processor.fdc.drives[0].disc).toBeDefined(); - // Should be the same disc object (reference preserved) - expect(processor.fdc.drives[0].disc).toBe(originalDisc); - }); - - it("should preserve discs on both drives after reset", () => { - // Create second disc - const mockDiscData2 = new Uint8Array(1024); - mockDiscData2.fill(0x84); - const mockDisc2 = disc.discFor(processor.fdc, "test2.ssd", mockDiscData2); - - // Load discs on both drives - processor.fdc.loadDisc(0, mockDisc); - processor.fdc.loadDisc(1, mockDisc2); - expect(processor.fdc.drives[0].disc).toBeDefined(); - expect(processor.fdc.drives[1].disc).toBeDefined(); - - // Store references to original discs - const originalDisc0 = processor.fdc.drives[0].disc; - const originalDisc1 = processor.fdc.drives[1].disc; - - // Perform hard reset - processor.reset(true); - - // Check if both discs are still loaded - expect(processor.fdc.drives[0].disc).toBeDefined(); - expect(processor.fdc.drives[1].disc).toBeDefined(); - // Should be the same disc objects (references preserved) - expect(processor.fdc.drives[0].disc).toBe(originalDisc0); - expect(processor.fdc.drives[1].disc).toBe(originalDisc1); - }); - - it("should handle reset when no discs are loaded", () => { - // Ensure no discs are loaded - expect(processor.fdc.drives[0].disc).toBeUndefined(); - expect(processor.fdc.drives[1].disc).toBeUndefined(); - - // Perform reset - should not crash - processor.reset(true); - - // Should still have no discs loaded - expect(processor.fdc.drives[0].disc).toBeUndefined(); - expect(processor.fdc.drives[1].disc).toBeUndefined(); - }); - - it("should preserve mixed loaded/unloaded state", () => { - // Load disc only on drive 0 - processor.fdc.loadDisc(0, mockDisc); - expect(processor.fdc.drives[0].disc).toBeDefined(); - expect(processor.fdc.drives[1].disc).toBeUndefined(); - - const originalDisc = processor.fdc.drives[0].disc; - - // Perform reset - processor.reset(true); - - // Drive 0 should still have disc, drive 1 should still be empty - expect(processor.fdc.drives[0].disc).toBeDefined(); - expect(processor.fdc.drives[1].disc).toBeUndefined(); - expect(processor.fdc.drives[0].disc).toBe(originalDisc); - }); -});