const expect = require('chai').expect; const MOSCIA = require('../src/MOSCIA.js'); describe("MOSCIA Tests...", function(){ //var cia = new MOSCIA(); it("R/W Pin", function(){ let cia = new MOSCIA(); // The RW pin is default low, so writting should be enabled. cia.RS = 0x02; expect(cia.DATA).to.be.equal(0x00); cia.DATA = 0xFF; expect(cia.DATA).to.be.equal(0xFF); // Set RW high to disable writting! cia.RW = 1; cia.DATA = 0x00; expect(cia.DATA).to.be.equal(0xFF); // Set it low once more cia.RW = 0; cia.DATA = 0x00; expect(cia.DATA).to.be.equal(0x00); }); it("Interrupts (via FLAG)", function(){ let cia = new MOSCIA(); cia.FLAG = 1; cia.RS = 0x0D; let IC = cia.DATA; expect(IC & 0x10).to.be.equal(0x10); expect(IC & 0x80).to.be.equal(0x80); expect(cia.DATA).to.be.equal(0x00); // Setting data changes the interrupt mask. This should mask the // FLAG interrupt. cia.DATA = 0x90; cia.FLAG = 1; IC = cia.DATA; expect(IC).to.be.equal(0x00); }); it("PD*/DD* Masking", function(){ let cia = new MOSCIA(); let pdaVal = 0x3C; let pdbVal = 0xC3; cia.onRead("PDA", (name, dda) => { if (dda < 255) cia.setPDA(pdaVal); }); cia.onRead("PDB", (name, ddb) => { if (ddb < 255) cia.setPDB(pdbVal); }); cia.RS = 0x02; cia.DATA = 0x00; cia.RS = 0x00; expect(cia.DATA).to.be.equal(pdaVal); cia.RS = 0x03; cia.DATA = 0x00; cia.RS = 0x01; expect(cia.DATA).to.be.equal(pdbVal); cia.RS = 0x02; cia.DATA = pdbVal; cia.RS = 0x00; cia.DATA = pdbVal; expect(cia.DATA).to.be.equal(0xFF); cia.RS = 0x03; cia.DATA = pdaVal; cia.RS = 0x01; cia.DATA = pdaVal; expect(cia.DATA).to.be.equal(0xFF); }); it("Serial IO", function(){ let cia = new MOSCIA(); let Input = (i) => { cia.SP = i; cia.CNT = 1; cia.phi2 = 1; }; var outval = 0; let Output = () => { cia.CNT = 1; cia.phi2 = 1; outval = (outval << 1) | cia.SP; } cia.RS = 0x0C; Input(1); expect(cia.DATA).to.be.equal(1); Input(1); expect(cia.DATA).to.be.equal(3); Input(0); expect(cia.DATA).to.be.equal(6); Input(0); Input(1); Input(1); Input(0); Input(1); expect(cia.DATA).to.be.equal(0xCD); cia.RS = 0x0D; let IC = cia.DATA; expect(IC & 0x08).to.be.equal(0x08); expect(IC & 0x80).to.be.equal(0x80); // Reading from RS = 0x0D should clear the interrupt values. Checking this expect(cia.DATA).to.be.equal(0); cia.RS = 0x0E; cia.DATA = 0x40; // Enable serial output. Output(); expect(outval).to.be.equal(1); Output(); expect(outval).to.be.equal(3); Output(); expect(outval).to.be.equal(6); Output(); Output(); Output(); Output(); Output(); expect(outval).to.be.equal(0xCD); cia.RS = 0x0C; expect(cia.DATA).to.be.equal(0); cia.RS = 0x0D; expect((cia.DATA & 0x08) >> 3).to.be.equal(1); expect(cia.DATA).to.be.equal(0); }); it("TOD 60hz", function(){ // NOTE: The TOD clock doesn't actually enforce the frequency. // The TOD 'pin' is expected to be attached to a clock pulsing at the // designated frequency. // As such, this test will run MUCH MUCH faster than the 1.1 seconds // actually being checked (because I'm not using a clock here). let cia = new MOSCIA(); let tick60 = () => { for (let i=0; i < 6; i++) cia.TOD = 1; }; // NOTE: The CIA defaults to 60hz TOD clock. Nothing needs to be set. // Check that seconds is at 0 (we'll test this again later) cia.RS = 0x09; expect(cia.DATA).to.be.equal(0); // Switch to 10ths of a second and go! cia.RS = 0x08; expect(cia.DATA).to.be.equal(0); tick60(); expect(cia.DATA).to.be.equal(0x01); tick60(); expect(cia.DATA).to.be.equal(0x02); tick60(); expect(cia.DATA).to.be.equal(0x03); for (let i=0; i < 8; i++) tick60(); // Should have looped back around a little at this point! expect(cia.DATA).to.be.equal(0x01); // Check seconds again. This should have gone up once! cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x01); }); it("TOD 50hz", function(){ // NOTE: The TOD clock doesn't actually enforce the frequency. // The TOD 'pin' is expected to be attached to a clock pulsing at the // designated frequency. // As such, this test will run MUCH MUCH faster than the 1.1 seconds // actually being checked (because I'm not using a clock here). let cia = new MOSCIA(); let tick50 = () => { for (let i=0; i < 5; i++) cia.TOD = 1; }; // enabling 50 hz mode. cia.RS = 0x0E; cia.DATA = cia.DATA | 0x80; // Check that seconds is at 0 (we'll test this again later) cia.RS = 0x09; expect(cia.DATA).to.be.equal(0); // Switch to 10ths of a second and go! cia.RS = 0x08; expect(cia.DATA).to.be.equal(0); tick50(); expect(cia.DATA).to.be.equal(0x01); tick50(); expect(cia.DATA).to.be.equal(0x02); tick50(); expect(cia.DATA).to.be.equal(0x03); for (let i=0; i < 8; i++) tick50(); // Should have looped back around a little at this point! expect(cia.DATA).to.be.equal(0x01); // Check seconds again. This should have gone up once! cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x01); }); it("TOD Latching", function(){ let cia = new MOSCIA(); let tick = (cycles) => { let ccount = cycles * 6; for (let i=0; i < ccount; i++) cia.TOD = 1; }; cia.setTOD(0x03, 0x59, 0x58, 0x00); // Copying current clock values for later reference. let TOD = [0,0,0,0]; cia.RS = 0x08; TOD[0] = cia.DATA; cia.RS = 0x09; TOD[1] = cia.DATA; cia.RS = 0x0A; TOD[2] = cia.DATA; cia.RS = 0x0B; TOD[3] = cia.DATA; // This should latch the timer!! tick(11); // Simulate 1.1 second time passing. // TOD Latch shouldn't be released until next read from RS=0x08, so... expect(cia.DATA).to.be.equal(TOD[3]); cia.RS = 0x0A; expect(cia.DATA).to.be.equal(TOD[2]); cia.RS = 0x09; expect(cia.DATA).to.be.equal(TOD[1]); cia.RS = 0x08; expect(cia.DATA).to.be.equal(TOD[0]); // Should release the TOD latch // With latch released, we should be able to read the new time... expect(cia.DATA).to.be.equal(0x01); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x59); }); it("TOD Time Rollover with AM/PM change", function(){ let cia = new MOSCIA(); let tick = () => { for (let i=0; i < 6; i++) cia.TOD = 1; }; cia.setTOD(0x11, 0x59, 0x59, 0x09); tick(); cia.RS = 0x0B; expect(cia.DATA).to.be.equal(0x80); cia.RS = 0x0A; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x08; expect(cia.DATA).to.be.equal(0x00); cia.setTOD(0x91, 0x59, 0x59, 0x09); tick(); cia.RS = 0x0B; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x0A; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x08; expect(cia.DATA).to.be.equal(0x00); }); it("TOD Write Time and Cycle Stop on Write", function(){ let cia = new MOSCIA(); let tick = (cycles) => { let ccount = cycles * 6; for (let i=0; i < ccount; i++) cia.TOD = 1; }; cia.RS = 0x0B; cia.DATA = 0x11; cia.RS = 0x0A; cia.DATA = 0x30; cia.RS = 0x09; cia.DATA = 0x20; // The lock on the timer isn't released until a write to RS=0x08 // so running a few ticks should NOT change the current values! tick(10); cia.RS = 0x0B; expect(cia.DATA).to.be.equal(0x11); cia.RS = 0x0A; expect(cia.DATA).to.be.equal(0x30); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x20); cia.RS = 0x08; expect(cia.DATA).to.be.equal(0x00); // Write to RS=0x08 and cycle again! cia.DATA = 0x08; // <= 8/10ths a second. tick(8); expect(cia.DATA).to.be.equal(0x06); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x21); // Check if writing is prevented unless Hours (0x0B) is written to first cia.DATA = 0x44; expect(cia.DATA).to.be.equal(0x21); cia.RS = 0x08; cia.DATA = 0x04; expect(cia.DATA).to.be.equal(0x06); }); it("TOD Alarm Set and Trigger", function(){ let cia = new MOSCIA(); let tick = (cycles) => { let ccount = cycles * 6; for (let i=0; i < ccount; i++) cia.TOD = 1; }; // Setting alarm values. cia.RS = 0x0F; cia.DATA = cia.DATA | 0x80; cia.RS = 0x0B; cia.DATA = 0x01; cia.RS = 0x0A; cia.DATA = 0x01; cia.RS = 0x09; cia.DATA = 0x01; cia.RS = 0x08; cia.DATA = 0x04; // Testing that all TOD values remain unchanged. expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x09; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x0A; expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x0B; expect(cia.DATA).to.be.equal(0x00); // Forcing TOD to be 4/10ths a second from triggering the alarm. cia.setTOD(0x01, 0x01, 0x01, 0x00); tick(4); cia.RS = 0x0D; // Hold the value as the IC clears after a read. let IC = cia.DATA; expect(IC & 0x04).to.be.equal(0x04); expect(IC & 0x80).to.be.equal(0x80); }); it("Timer A", function(){ let cia = new MOSCIA(); let tick = (cycles) => { for (let i=0; i < cycles; i++) cia.phi2 = 1; }; cia.RS = 0x04; cia.DATA = 0xFF; cia.RS = 0x05; cia.DATA = 0x01; cia.RS = 0x0E; // Force latch load into Timer A and activate Timer A cia.DATA = cia.DATA | 0x11; cia.RS = 0x04; expect(cia.DATA).to.be.equal(0xFF); tick(1); expect(cia.DATA).to.be.equal(0xFE); tick(1); expect(cia.DATA).to.be.equal(0xFD); tick(0x0200 - 3); expect(cia.DATA).to.be.equal(0x00); cia.RS = 0x05; expect(cia.DATA).to.be.equal(0x00); tick(1); expect(cia.DATA).to.be.equal(0x01); cia.RS = 0x04; expect(cia.DATA).to.be.equal(0xFF); cia.RS = 0x0D; let IC = cia.DATA; expect(IC & 0x01).to.be.equal(0x01); expect(IC & 0x80).to.be.equal(0x80); }); });