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 TODTick(cia){ cia.__TODTick += 1; if (cia.__TODTick === 6){ cia.__TODTick = cia.__CTA & 0x01; if (cia.__TOD[0] == 0x09){ cia.__TOD[0] = 0; if (cia.__TOD[1] == 0x59){ cia.__TOD[1] = 0; 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; } else { let pm = cia.__TOD[3] & 0x80; cia.__TOD[3] = BCD.add(cia.__TOD[3] & 0x7F, 0x01, 2) | pm; } } else {cia.__TOD[2] = BCD.add(cia.__TOD[2], 0x01, 2);} } else {cia.__TOD[1] = BCD.add(cia.__TOD[1], 0x01, 2);} } else {cia.__TOD[0] = BCD.add(cia.__TOD[0], 0x01, 2);} } 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); } } 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]; } } function TimerATick(cia){ if (cia.__timerA > 0){ cia.__timerA -= 1; } else { // Check if Timer A is continuous if (BITM.isOn(cia.__CTA, 3) === 0){ cia.__timerA = cia.__timerALatch; } else { // Not continuous, so clear bit 0 (stop timer). cia.__CTA = BITM.clear(cia.__CTA, 0); } // Setting the interrupt bit for timer A 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. if (BITM.isOn(cia.__CTA, 1)) cia.__PDB = (BITM.isOn(cia.__CTA, 2) === 0) ? BITM.set(cia.__PDB, 6) : BITM.toggle(cia.__PDB, 6); // If timer B is running, and timer B tracks timer A underflows // tick timer B (or, tick it if CNT is positive) if (BITM.isOn(cia.__CTB, 0)){ let cs = (this.__CTB & 0x60) >> 5; if (cs === 2 || (cs === 3 && cia.__CNT === 1)) TimerBTick(cia); } } } function TimerBTick(cia){ if (cia.__timerB > 0){ cia.__timerB -= 1; } else { if (BITM.isOn(cia.__CTB, 3) === 0){ cia.__timerB = cia.__timerBLatch; } else { cia.__CTB = BITM.clear(cia.__CTB, 0); } 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); } } function Tick(cia){ if (BITM.isOn(cia.__CTA, 0)){ if (BITM.isOn(cia.__CTA, 1) && !BITM.isOn(cia.__CTA, 2)) cia.__PDB = BITM.clear(cia.__PDB, 6); if (!BITM.isOn(cia.__CTA, 5)) TimerATick(cia); } if (BITM.isOn(cia.__CTB, 0)){ if (BITM.isOn(cia.__CTB, 1) && !BITM.isOn(cia.__CTB, 2)) cia.__PDB = BITM.clear(cia.__PDB, 7); let cs = (cia.__CTB & 0x60) >> 5; if (cs === 0) TimerBTick(cia); } if (cia.__CNT === 1){ if (BITM.isOn(cia.__CTA, 0) && BITM.isOn(cia.__CTA, 5)) TimerATick(cia); if (BITM.isOn(cia.__CTB, 0)){ let cs = (cia.__CTB & 0x60) >> 5; if (cs === 1) TimerBTick(cia); } if (BITM.isOn(cia.__CTA, 6)){ if (cia.__SPi > 0){ cia.__SP = BITM.val(cia.__SData, 7); cia.__SData = (cia.__SData << 1) & 0xFF; cia.__SPi -= 1; if (cia.__SPi === 0) 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); } } } cia.__CNT = 0; } class MOSCIA{ constructor(){ this.__regsel = 0; this.__RW = 0; this.__CNT = 0; this.__SP = 0; this.__SPi = 0; this.__SData = 0; this.__PDA = 0; this.__PDB = 0; this.__DDA = 0; this.__DDB = 0; this.__CTA = 0; this.__CTB = 0; this.__IC = 0; this.__ICMask = 0; this.__timerA = 0; this.__timerALatch = 1; this.__timerB = 0; this.__timerBLatch = 1; // Time Of Day this.__TODLatch = 0; this.__TODTick = 0; this.__TOD = [0,0,0,0]; this.__LTOD = [0,0,0,0]; this.__ALTOD = [0,0,0,0]; } get RS(){return this.__regsel;} set RS(rs){this.__regsel = rs & 0x0F;} get RW(){return this.__RW;} set RW(rw){this.__RW = (rw === true || rw === 1) ? 1 : 0;} get DATA(){ let val = 0; switch(this.__regsel){ case 0x00: // Peripheral Data A break; case 0x01: // Peripheral Data B break; case 0x02: // Data Direction Reg A break; case 0x03: // Data Direction Reg B break; case 0x04: // Timer A Low val = (this.__timerA & 0x00FF); break; case 0x05: // Timer A High val = ((this.__timerA & 0xFF00) >> 4); break; case 0x06: // Timer B Low val = (this.__timerB & 0x00FF); break; 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; case 0x09: // Seconds Reg val = (this.__TODLatch > 0) ? this.__LTOD[1] : this.__TOD[1]; break; case 0x0A: // Minutes Reg val = (this.__TODLatch > 0) ? this.__LTOD[2] : this.__TOD[2]; break; case 0x0B: // Hours AM/PM Reg TODLatch(this); val = this.__LTOD[3]; break; case 0x0C: // Serial Data val = this.__SData; break; case 0x0D: // Interrupt Control let v = this.__IC; this.__IC = 0; return v; break; case 0x0E: // Control Timer Reg A val = this.__CTA; break; case 0x0F: // Control Timer Reg B val = this.__CTB; break; } return val; } set DATA(d){ if (this.__RW === 1){return;} let tod = 0; switch(this.__regsel){ case 0x00: // Peripheral Data A break; case 0x01: // Peripheral Data B break; case 0x02: // Data Direction Reg A break; case 0x03: // Data Direction Reg B break; case 0x04: // Timer A Low this.__timerALatch = (this.__timerALatch & 0xFF00) | (d & 0xFF); break; case 0x05: // Timer A High this.__timerALatch = (this.__timerALatch & 0x00FF) | ((d & 0xFF) << 4); break case 0x06: // Timer B Low this.__timerBLatch = (this.__timerBLatch & 0xFF00) | (d & 0xFF); break; case 0x07: // Timer B High 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; break; case 0x09: // Seconds Reg TODLatch(this); this.__TODLatch = 2; 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; break; case 0x0B: // Hours AM/PM Reg TODLatch(this); this.__TODLatch = 2; tod = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; tod[3] = d & 0xFF; break; case 0x0C: // Serial Data break; case 0x0D: // Interrupt Control if (BITM.isOn(d & 0xFF, 7)){ for (let i=0; i < 5; i++){ if (BITM.isOn(d & 0xFF, i)) this.__ICMask = BITM.set(this.__ICMask, i); } } else { for (let i=0; i < 5; i++){ if (BITM.isOn(d & 0xFF, i)) this.__ICMask = BITM.clear(this.__ICMask, i); } } break; case 0x0E: // Control Timer Reg A this.__CTA = d & 0xFF; if (BITM.isOn(this.__CTA, 4)) this.__timerA = this.__timerALatch; break; case 0x0F: // Control Timer Reg B this.__CTB = d & 0xFF; if (BITM.isOn(this.__CTB, 4)) this.__timerB = this.__timerBLatch; break; } } get CNT(){return this.__CNT;} set CNT(c){ this.__CNT = (c >= 1) ? 1 : 0; } get SP(){return this.__SP;} set SP(sp){ this.__SP = (sp === true || sp === 1) ? 1 : 0; } get TOD(){return 0;} set TOD(b){ if (b > 0 || this.__TODLatch < 2) TODTick(this); } get phi2(){return 0;} set phi2(p){ if (p === true || p === 1){ Tick(this); } } reset(){ this.__CNT = 0; this.__PDA = 0; this.__PDB = 0; this.__DDA = 0; this.__DDB = 0; this.__CTA = 0; this.__CTB = 0; this.__IC = 0; this.__ICMask = 0; this.__timerA = 0; this.__timerALatch = 1; this.__timerB = 0; this.__timerBLatch = 1; // Time Of Day this.__TODLatch = 0; this.__TODTick = 0; this.__TOD = [0,0,0,0]; this.__LTOD = [0,0,0,0]; this.__ALTOD = [0,0,0,0]; } } module.exports = MOSCIA;