apple2js/js/cards/thunderclock.ts

153 lines
4.1 KiB
TypeScript

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<ThunderclockState>
{
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() {}
}