| const BCD = require('./utils/bcd.js'); | const BCD = require('./utils/bcd.js'); | ||||
| const BITM = require('./utils/bitman.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){ | function TODTick(cia){ | ||||
| if (cia.__TODTick < 0){return;} | |||||
| cia.__TODTick += 1; | cia.__TODTick += 1; | ||||
| if (cia.__TODTick === 6){ | if (cia.__TODTick === 6){ | ||||
| cia.__TODTick = cia.__CTA & 0x01; | |||||
| cia.__TODTick = (cia.__CTA & 0x80) >> 7; | |||||
| if (cia.__TOD[0] == 0x09){ | if (cia.__TOD[0] == 0x09){ | ||||
| cia.__TOD[0] = 0; | cia.__TOD[0] = 0; | ||||
| if (cia.__TOD[1] == 0x59){ | if (cia.__TOD[1] == 0x59){ | ||||
| if (cia.__TOD[2] == 0x59){ | if (cia.__TOD[2] == 0x59){ | ||||
| cia.__TOD[2] = 0; | cia.__TOD[2] = 0; | ||||
| if (cia.__TOD[3] == 0x11 || cia.__TOD[3] == 0x91){ | 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 { | } else { | ||||
| let pm = cia.__TOD[3] & 0x80; | let pm = cia.__TOD[3] & 0x80; | ||||
| cia.__TOD[3] = BCD.add(cia.__TOD[3] & 0x7F, 0x01, 2) | pm; | cia.__TOD[3] = BCD.add(cia.__TOD[3] & 0x7F, 0x01, 2) | pm; | ||||
| } | } | ||||
| 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]){ | 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){ | 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] | |||||
| ]; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| // Setting the interrupt bit for timer A | // 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. | // Check to see if timer modifies the PORT B bit 6. | ||||
| // If so, determine if it's a pulse or an toggle. | // If so, determine if it's a pulse or an toggle. | ||||
| cia.__CTB = BITM.clear(cia.__CTB, 0); | 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)) | if (BITM.isOn(cia.__CTB, 1)) | ||||
| cia.__PDB = (BITM.isOn(cia.__CTB, 2) === 0) ? BITM.set(cia.__PDB, 7) : BITM.toggle(cia.__PDB, 7); | cia.__PDB = (BITM.isOn(cia.__CTB, 2) === 0) ? BITM.set(cia.__PDB, 7) : BITM.toggle(cia.__PDB, 7); | ||||
| cia.__SData = (cia.__SData << 1) & 0xFF; | cia.__SData = (cia.__SData << 1) & 0xFF; | ||||
| cia.__SPi -= 1; | cia.__SPi -= 1; | ||||
| if (cia.__SPi === 0) | if (cia.__SPi === 0) | ||||
| cia.__IC = BITM.set(cia.__IC, 3); | |||||
| EnableICBit(cia, 3); | |||||
| //cia.__IC = BITM.set(cia.__IC, 3); | |||||
| } | } | ||||
| } else { | } else { | ||||
| if (cia.__SPi < 8){ | if (cia.__SPi < 8){ | ||||
| cia.__SData = (cia.__SData << 1) | cia.__SP; | cia.__SData = (cia.__SData << 1) | cia.__SP; | ||||
| cia.__SPi += 1; | cia.__SPi += 1; | ||||
| if (cia.__SPi === 8) | if (cia.__SPi === 8) | ||||
| cia.__IC = BITM.set(cia.__IC, 3); | |||||
| EnableICBit(cia, 3); | |||||
| //cia.__IC = BITM.set(cia.__IC, 3); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| this.__timerBLatch = 1; | this.__timerBLatch = 1; | ||||
| // Time Of Day | // Time Of Day | ||||
| this.__TODLatch = 0; | |||||
| this.__TODTick = 0; | this.__TODTick = 0; | ||||
| this.__TODLatch = null; | |||||
| this.__TOD = [0,0,0,0]; | this.__TOD = [0,0,0,0]; | ||||
| this.__LTOD = [0,0,0,0]; | |||||
| this.__ALTOD = [0,0,0,0]; | this.__ALTOD = [0,0,0,0]; | ||||
| } | } | ||||
| case 0x07: // Timer B High | case 0x07: // Timer B High | ||||
| val = ((this.__timerB & 0xFF00) >> 4); break; | val = ((this.__timerB & 0xFF00) >> 4); break; | ||||
| case 0x08: // 10th of Sec Reg | 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 | 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 | 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 | case 0x0B: // Hours AM/PM Reg | ||||
| TODLatch(this); | TODLatch(this); | ||||
| val = this.__LTOD[3]; break; | |||||
| val = this.__TODLatch[3]; break; | |||||
| case 0x0C: // Serial Data | case 0x0C: // Serial Data | ||||
| val = this.__SData; break; | val = this.__SData; break; | ||||
| case 0x0D: // Interrupt Control | case 0x0D: // Interrupt Control | ||||
| this.__timerBLatch = (this.__timerBLatch & 0x00FF) | ((d & 0xFF) << 4); | this.__timerBLatch = (this.__timerBLatch & 0x00FF) | ((d & 0xFF) << 4); | ||||
| break; | break; | ||||
| case 0x08: // 10th of Sec Reg | 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; | break; | ||||
| case 0x09: // Seconds Reg | 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; | break; | ||||
| case 0x0A: // Minutes Reg | 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; | break; | ||||
| case 0x0B: // Hours AM/PM Reg | 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 = (BITM.isOn(this.__CTB, 7)) ? this.__ALTOD : this.__TOD; | ||||
| tod[3] = d & 0xFF; | tod[3] = d & 0xFF; | ||||
| break; | break; | ||||
| this.__CTA = d & 0xFF; | this.__CTA = d & 0xFF; | ||||
| if (BITM.isOn(this.__CTA, 4)) | if (BITM.isOn(this.__CTA, 4)) | ||||
| this.__timerA = this.__timerALatch; | 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; | break; | ||||
| case 0x0F: // Control Timer Reg B | case 0x0F: // Control Timer Reg B | ||||
| this.__CTB = d & 0xFF; | this.__CTB = d & 0xFF; | ||||
| this.__SP = (sp === true || sp === 1) ? 1 : 0; | this.__SP = (sp === true || sp === 1) ? 1 : 0; | ||||
| } | } | ||||
| set FLAG(f){ | |||||
| if (f === true || f === 1) | |||||
| EnableICBit(this, 4); | |||||
| } | |||||
| get TOD(){return 0;} | get TOD(){return 0;} | ||||
| set TOD(b){ | set TOD(b){ | ||||
| if (b > 0 || this.__TODLatch < 2) | if (b > 0 || this.__TODLatch < 2) | ||||
| return this; | 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(){ | reset(){ | ||||
| this.__CNT = 0; | this.__CNT = 0; | ||||