import { debug, toHex } from '../util'; import { Card, Restorable, byte } from '../types'; import { rom } from '../roms/cards/thunderclock'; const LOC = { CONTROL: 0x80, AUX: 0x88 } as const; const COMMANDS = { MASK: 0x18, REGHOLD: 0x00, REGSHIFT: 0x08, TIMED: 0x18 } as const; const FLAGS = { DATA: 0x01, CLOCK: 0x02, STROBE: 0x04 } as const; export interface ThunderclockState {} export default class Thunderclock implements Card, Restorable { constructor() { debug('Thunderclock'); } private clock = false; private strobe = false; private shiftMode = false; private register = 0; private bits: boolean[] = []; private command: byte = COMMANDS.REGHOLD; private debug(..._args: any[]) { // debug.apply(this, arguments); } private calcBits() { const shift = (val: byte) => { for (let idx = 0; idx < 4; idx++) { this.bits.push((val & 0x08) !== 0); val <<= 1; } }; const shiftBCD = (val: byte) => { shift(Math.floor(val / 10)); shift(val % 10); }; const now = new Date(); const day = now.getDate(); const weekday = now.getDay(); const month = now.getMonth() + 1; const hour = now.getHours(); const minutes = now.getMinutes(); const seconds = now.getSeconds(); this.bits = []; shift(month); shift(weekday); shiftBCD(day); shiftBCD(hour); shiftBCD(minutes); shiftBCD(seconds); } private shift() { if (this.shiftMode) { if (this.bits.pop()) { this.debug('shifting 1'); this.register |= 0x80; } else { this.debug('shifting 0'); this.register &= 0x7f; } } } private access(off: byte, val?: byte) { switch (off & 0x8F) { case LOC.CONTROL: if (val !== undefined) { const strobe = val & FLAGS.STROBE ? true : false; if (strobe !== this.strobe) { this.debug('strobe', this.strobe ? 'high' : 'low'); if (strobe) { this.command = val & COMMANDS.MASK; switch (this.command) { case COMMANDS.TIMED: this.debug('TIMED'); this.calcBits(); break; case COMMANDS.REGSHIFT: this.debug('REGSHIFT'); this.shiftMode = true; this.shift(); break; case COMMANDS.REGHOLD: this.debug('REGHOLD'); this.shiftMode = false; break; default: this.debug('Unknown command', toHex(this.command)); } } } const clock = val & FLAGS.CLOCK ? true : false; if (clock !== this.clock) { this.clock = clock; this.debug('clock', this.clock ? 'high' : 'low'); if (clock) { this.shift(); } } } break; case LOC.AUX: break; } return this.register; } read(page: byte, off: byte) { let result; if (page < 0xc8) { result = rom[off]; } else { result = rom[(page - 0xc8) << 8 | off]; } return result; } write() { } ioSwitch(off: byte, val?: byte) { return this.access(off, val); } getState() { return {}; } setState() {} }