| @@ -2,17 +2,19 @@ const IO = require('./common/io.js'); | |||
| const BCD = require('./utils/bcd.js'); | |||
| const BITM = require('./utils/bitman.js'); | |||
| function ChangeICBit(cia, v, pos){ | |||
| let mask = ~(1 << pos); | |||
| v = ((v === true || v === 1) ? 1 : 0) << pos; | |||
| cia.__IC = (cia.__IC & mask) | v; | |||
| function EnableICBit(cia, pos){ | |||
| v = 1 << pos; | |||
| cia.__IC = cia.__IC | v; | |||
| // Enabling any bit enables bit 7 as well! | |||
| cia.__IC = cia.__IC | 0x80; | |||
| } | |||
| function TODTick(cia){ | |||
| if (cia.__TODTick < 0){return;} | |||
| cia.__TODTick += 1; | |||
| if (cia.__TODTick === 6){ | |||
| cia.__TODTick = cia.__CTA & 0x01; | |||
| cia.__TODTick = (cia.__CTA & 0x80) >> 7; | |||
| if (cia.__TOD[0] == 0x09){ | |||
| cia.__TOD[0] = 0; | |||
| if (cia.__TOD[1] == 0x59){ | |||
| @@ -20,7 +22,7 @@ function TODTick(cia){ | |||
| if (cia.__TOD[2] == 0x59){ | |||
| cia.__TOD[2] = 0; | |||
| if (cia.__TOD[3] == 0x11 || cia.__TOD[3] == 0x91){ | |||
| cia.__TOD[3] = (cia.__TOD[3] == 0x91) ? 0x00 : 0x10; | |||
| cia.__TOD[3] = (cia.__TOD[3] == 0x91) ? 0x00 : 0x80; | |||
| } else { | |||
| let pm = cia.__TOD[3] & 0x80; | |||
| cia.__TOD[3] = BCD.add(cia.__TOD[3] & 0x7F, 0x01, 2) | pm; | |||
| @@ -31,17 +33,18 @@ function TODTick(cia){ | |||
| } | |||
| if (cia.__TOD[0] == cia.__ALTOD[0] && cia.__TOD[1] == cia.__ALTOD[1] && cia.__TOD[2] == cia.__ALTOD[2] && cia.__TOD[3] == cia.__ALTOD[3]){ | |||
| ChangeICBit(cia, 1, 2); | |||
| EnableICBit(cia, 2); | |||
| } | |||
| } | |||
| function TODLatch(cia){ | |||
| if (cia.__TODLatch === 0){ | |||
| cia.__TODLatch = 1; | |||
| cia.__LTOD[0] = cia.__TOD[0]; | |||
| cia.__LTOD[1] = cia.__TOD[1]; | |||
| cia.__LTOD[2] = cia.__TOD[2]; | |||
| cia.__LTOD[3] = cia.__TOD[3]; | |||
| if (cia.__TODLatch === null){ | |||
| cia.__TODLatch = [ | |||
| cia.__TOD[0], | |||
| cia.__TOD[1], | |||
| cia.__TOD[2], | |||
| cia.__TOD[3] | |||
| ]; | |||
| } | |||
| } | |||
| @@ -59,7 +62,8 @@ function TimerATick(cia){ | |||
| } | |||
| // Setting the interrupt bit for timer A | |||
| cia.__IC = BITM.set(cia.__IC, 0); | |||
| EnableICBit(cia, 0); | |||
| //cia.__IC = BITM.set(cia.__IC, 0); | |||
| // Check to see if timer modifies the PORT B bit 6. | |||
| // If so, determine if it's a pulse or an toggle. | |||
| @@ -86,7 +90,8 @@ function TimerBTick(cia){ | |||
| cia.__CTB = BITM.clear(cia.__CTB, 0); | |||
| } | |||
| cia.__IC = BITM.set(cia.__IC, 1); | |||
| EnableICBit(cia, 1); | |||
| //cia.__IC = BITM.set(cia.__IC, 1); | |||
| if (BITM.isOn(cia.__CTB, 1)) | |||
| cia.__PDB = (BITM.isOn(cia.__CTB, 2) === 0) ? BITM.set(cia.__PDB, 7) : BITM.toggle(cia.__PDB, 7); | |||
| @@ -127,14 +132,16 @@ function Tick(cia){ | |||
| cia.__SData = (cia.__SData << 1) & 0xFF; | |||
| cia.__SPi -= 1; | |||
| if (cia.__SPi === 0) | |||
| cia.__IC = BITM.set(cia.__IC, 3); | |||
| EnableICBit(cia, 3); | |||
| //cia.__IC = BITM.set(cia.__IC, 3); | |||
| } | |||
| } else { | |||
| if (cia.__SPi < 8){ | |||
| cia.__SData = (cia.__SData << 1) | cia.__SP; | |||
| cia.__SPi += 1; | |||
| if (cia.__SPi === 8) | |||
| cia.__IC = BITM.set(cia.__IC, 3); | |||
| EnableICBit(cia, 3); | |||
| //cia.__IC = BITM.set(cia.__IC, 3); | |||
| } | |||
| } | |||
| } | |||
| @@ -171,10 +178,9 @@ class MOSCIA{ | |||
| this.__timerBLatch = 1; | |||
| // Time Of Day | |||
| this.__TODLatch = 0; | |||
| this.__TODTick = 0; | |||
| this.__TODLatch = null; | |||
| this.__TOD = [0,0,0,0]; | |||
| this.__LTOD = [0,0,0,0]; | |||
| this.__ALTOD = [0,0,0,0]; | |||
| } | |||
| @@ -206,15 +212,16 @@ class MOSCIA{ | |||
| case 0x07: // Timer B High | |||
| val = ((this.__timerB & 0xFF00) >> 4); break; | |||
| case 0x08: // 10th of Sec Reg | |||
| this.__TODLatch = 0; | |||
| val = this.__TOD[0]; break; | |||
| val = (this.__TODLatch !== null) ? this.__TODLatch[0] : this.__TOD[0]; | |||
| this.__TODLatch = null; | |||
| break; | |||
| case 0x09: // Seconds Reg | |||
| val = (this.__TODLatch > 0) ? this.__LTOD[1] : this.__TOD[1]; break; | |||
| val = (this.__TODLatch !== null) ? this.__TODLatch[1] : this.__TOD[1]; break; | |||
| case 0x0A: // Minutes Reg | |||
| val = (this.__TODLatch > 0) ? this.__LTOD[2] : this.__TOD[2]; break; | |||
| val = (this.__TODLatch !== null) ? this.__TODLatch[2] : this.__TOD[2]; break; | |||
| case 0x0B: // Hours AM/PM Reg | |||
| TODLatch(this); | |||
| val = this.__LTOD[3]; break; | |||
| val = this.__TODLatch[3]; break; | |||
| case 0x0C: // Serial Data | |||
| val = this.__SData; break; | |||
| case 0x0D: // Interrupt Control | |||
| @@ -260,26 +267,26 @@ class MOSCIA{ | |||
| this.__timerBLatch = (this.__timerBLatch & 0x00FF) | ((d & 0xFF) << 4); | |||
| break; | |||
| case 0x08: // 10th of Sec Reg | |||
| TODLatch(this); | |||
| this.__TODLatch = 2; | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[0] = d & 0xFF; | |||
| if (this.__TODTick < 0){ | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[0] = d & 0xFF; | |||
| this.__TODTick = (this.__CTA & 0x80) >> 7; // Restarts TOD cycling. | |||
| } | |||
| break; | |||
| case 0x09: // Seconds Reg | |||
| TODLatch(this); | |||
| this.__TODLatch = 2; | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[1] = d & 0xFF; | |||
| if (this.__TODTick < 0){ | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[1] = d & 0xFF; | |||
| } | |||
| break; | |||
| case 0x0A: // Minutes Reg | |||
| TODLatch(this); | |||
| this.__TODLatch = 2; | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[2] = d & 0xFF; | |||
| if (this.__TODTick < 0){ | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[2] = d & 0xFF; | |||
| } | |||
| break; | |||
| case 0x0B: // Hours AM/PM Reg | |||
| TODLatch(this); | |||
| this.__TODLatch = 2; | |||
| this.__TODTick = -1; // This will prevent TOD from cycling. | |||
| tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | |||
| tod[3] = d & 0xFF; | |||
| break; | |||
| @@ -302,6 +309,17 @@ class MOSCIA{ | |||
| this.__CTA = d & 0xFF; | |||
| if (BITM.isOn(this.__CTA, 4)) | |||
| this.__timerA = this.__timerALatch; | |||
| // If __TODTick is less than 0, TOD is in write mode. | |||
| // As such, the __TODTick counter will be reset once 10ths is | |||
| // written to... so there's no need to adjust the ticks here... | |||
| // Besides, doing so will break the system. | |||
| if (this.__TODTick >= 0){ | |||
| if (BITM.isOn(this.__CTA, 7)){ | |||
| this.__TODTick = (this.__TODTick === 5) ? 0 : this.__TODTick + 1; | |||
| } else { | |||
| this.__TODTick -= (this.__TODTick === 5) ? this.__TODTick : 0; | |||
| } | |||
| } | |||
| break; | |||
| case 0x0F: // Control Timer Reg B | |||
| this.__CTB = d & 0xFF; | |||
| @@ -321,6 +339,11 @@ class MOSCIA{ | |||
| this.__SP = (sp === true || sp === 1) ? 1 : 0; | |||
| } | |||
| set FLAG(f){ | |||
| if (f === true || f === 1) | |||
| EnableICBit(this, 4); | |||
| } | |||
| get TOD(){return 0;} | |||
| set TOD(b){ | |||
| if (b > 0 || this.__TODLatch < 2) | |||
| @@ -354,6 +377,15 @@ class MOSCIA{ | |||
| return this; | |||
| } | |||
| setTOD(h,m,s,t){ | |||
| // This method primarily exists for testing purposes. This should NOT | |||
| // be used in a true emulation. | |||
| this.__TOD[0] = t & 0xFF; | |||
| this.__TOD[1] = s & 0xFF; | |||
| this.__TOD[2] = m & 0xFF; | |||
| this.__TOD[3] = h & 0xFF; | |||
| } | |||
| reset(){ | |||
| this.__CNT = 0; | |||