From 59384414a254046e269606c8893c6f78f4fe7a99 Mon Sep 17 00:00:00 2001 From: Matt Godbolt Date: Sun, 24 Dec 2023 11:06:37 -0600 Subject: [PATCH] Class-i-fy via --- 6502.js | 4 +- via.js | 1012 ++++++++++++++++++++++++++++--------------------------- 2 files changed, 513 insertions(+), 503 deletions(-) diff --git a/6502.js b/6502.js index 14adc1cd..80be607b 100644 --- a/6502.js +++ b/6502.js @@ -1161,7 +1161,7 @@ export class Cpu6502 extends Base6502 { this.videoDisplayPage = 0; this.scheduler = new Scheduler(); this.soundChip.setScheduler(this.scheduler); - this.sysvia = via.SysVia( + this.sysvia = new via.SysVia( this, this.video, this.soundChip, @@ -1170,7 +1170,7 @@ export class Cpu6502 extends Base6502 { this.config.keyLayout, this.config.getGamepads, ); - this.uservia = via.UserVia(this, this.model.isMaster, this.config.userPort); + this.uservia = new via.UserVia(this, this.model.isMaster, this.config.userPort); if (this.config.printerPort) this.uservia.ca2changecallback = this.config.printerPort.outputStrobe; this.touchScreen = new TouchScreen(this.scheduler); this.acia = new Acia(this, this.soundChip.toneGenerator, this.scheduler, this.touchScreen); diff --git a/via.js b/via.js index 5fa58450..c0e2979b 100644 --- a/via.js +++ b/via.js @@ -24,492 +24,516 @@ const ORB = 0x0, INT_CB1 = 0x10, INT_CB2 = 0x08; -function via(cpu, irq) { - const self = { - ora: 0, - orb: 0, - ira: 0, - irb: 0, - ddra: 0, - ddrb: 0, - sr: 0, - t1l: 0, - t2l: 0, - t1c: 0, - t2c: 0, - acr: 0, - pcr: 0, - ifr: 0, - ier: 0, - t1hit: false, - t2hit: false, - portapins: 0, - portbpins: 0, - ca1: false, - ca2: false, - cb1: false, - cb2: false, - ca2changecallback: null, - cb2changecallback: null, - justhit: 0, - t1_pb7: 0, - - reset: function () { - // http://archive.6502.org/datasheets/mos_6522_preliminary_nov_1977.pdf - // "Reset sets all registers to zero except t1 t2 and sr" - self.ora = self.orb = 0x00; - self.ddra = self.ddrb = 0x00; - self.ifr = self.ier = 0x00; - self.t1c = self.t1l = self.t2c = self.t2l = 0x1fffe; - self.t1hit = self.t2hit = true; - self.acr = self.pcr = 0; - self.t1_pb7 = 1; - }, - - polltime: function (cycles) { - cycles |= 0; - self.justhit = 0; - let newT1c = self.t1c - cycles; - if (newT1c < -2 && self.t1c > -3) { - if (!self.t1hit) { - self.ifr |= TIMER1INT; - self.updateIFR(); - if (newT1c === -3) { - self.justhit |= 1; - } - self.t1_pb7 = !self.t1_pb7; +class Via { + constructor(cpu, irq) { + this.cpu = cpu; + this.irq = irq; + + this.ora = 0; + this.orb = 0; + this.ira = 0; + this.irb = 0; + this.ddra = 0; + this.ddrb = 0; + this.sr = 0; + this.t1l = 0; + this.t2l = 0; + this.t1c = 0; + this.t2c = 0; + this.acr = 0; + this.pcr = 0; + this.ifr = 0; + this.ier = 0; + this.t1hit = false; + this.t2hit = false; + this.portapins = 0; + this.portbpins = 0; + this.ca1 = false; + this.ca2 = false; + this.cb1 = false; + this.cb2 = false; + this.ca2changecallback = null; + this.cb2changecallback = null; + this.justhit = 0; + this.t1_pb7 = 0; + } + + reset() { + // http://archive.6502.org/datasheets/mos_6522_preliminary_nov_1977.pdf + // "Reset sets all registers to zero except t1 t2 and sr" + this.ora = this.orb = 0x00; + this.ddra = this.ddrb = 0x00; + this.ifr = this.ier = 0x00; + this.t1c = this.t1l = this.t2c = this.t2l = 0x1fffe; + this.t1hit = this.t2hit = true; + this.acr = this.pcr = 0; + this.t1_pb7 = 1; + } + + polltime(cycles) { + cycles |= 0; + this.justhit = 0; + let newT1c = this.t1c - cycles; + if (newT1c < -2 && this.t1c > -3) { + if (!this.t1hit) { + this.ifr |= TIMER1INT; + this.updateIFR(); + if (newT1c === -3) { + this.justhit |= 1; } - if (!(this.acr & 0x40)) self.t1hit = true; + this.t1_pb7 = !this.t1_pb7; } - while (newT1c < -3) newT1c += self.t1l + 4; - self.t1c = newT1c; - - if (!(self.acr & 0x20)) { - let newT2c = self.t2c - cycles; - if (newT2c < -2) { - if (!self.t2hit) { - self.ifr |= TIMER2INT; - self.updateIFR(); - if (newT2c === -3) { - self.justhit |= 2; - } - self.t2hit = true; + if (!(this.acr & 0x40)) this.t1hit = true; + } + while (newT1c < -3) newT1c += this.t1l + 4; + this.t1c = newT1c; + + if (!(this.acr & 0x20)) { + let newT2c = this.t2c - cycles; + if (newT2c < -2) { + if (!this.t2hit) { + this.ifr |= TIMER2INT; + this.updateIFR(); + if (newT2c === -3) { + this.justhit |= 2; } - newT2c += 0x20000; + this.t2hit = true; } - self.t2c = newT2c; - } - }, - - updateIFR: function () { - if (self.ifr & self.ier & 0x7f) { - self.ifr |= 0x80; - cpu.interrupt |= irq; - } else { - self.ifr &= ~0x80; - cpu.interrupt &= ~irq; + newT2c += 0x20000; } - }, - - write: function (addr, val) { - let mode; - val |= 0; - switch (addr & 0xf) { - case ORA: - self.ifr &= ~INT_CA1; - if ((self.pcr & 0x0a) !== 0x02) { - // b-em: Not independent interrupt for CA2 - self.ifr &= ~INT_CA2; - } - self.updateIFR(); - - mode = self.pcr & 0x0e; - if (mode === 8) { - // Handshake mode - self.setca2(false); - } else if (mode === 0x0a) { - // Pulse mode - self.setca2(false); - self.setca2(true); - } - /* falls through */ - case ORAnh: - self.ora = val; - self.recalculatePortAPins(); - break; - - case ORB: - self.ifr &= ~INT_CB1; - if ((self.pcr & 0xa0) !== 0x20) { - // b-em: Not independent interrupt for CB2 - self.ifr &= ~INT_CB2; - } - self.updateIFR(); - - self.orb = val; - self.recalculatePortBPins(); - - mode = (self.pcr & 0xe0) >>> 4; - if (mode === 8) { - // Handshake mode - self.setcb2(false); - } else if (mode === 0x0a) { - // Pulse mode - self.setcb2(false); - self.setcb2(true); - } - break; - - case DDRA: - self.ddra = val; - self.recalculatePortAPins(); - break; - - case DDRB: - self.ddrb = val; - self.recalculatePortBPins(); - break; - - case ACR: - self.acr = val; - if (self.justhit & 1 && !(val & 0x40)) self.t1hit = true; - break; - - case PCR: - self.pcr = val; - if ((val & 0xe) === 0xc) self.setca2(false); - else if (val & 0x08) self.setca2(true); - if ((val & 0xe0) === 0xc0) self.setcb2(false); - else if (val & 0x80) self.setcb2(true); - break; - - case SR: - self.sr = val; - break; - - case T1LL: - case T1CL: - self.t1l &= 0x1fe00; - self.t1l |= val << 1; - break; - - case T1LH: - self.t1l &= 0x1fe; - self.t1l |= val << 9; - if (!(self.justhit & 1)) { - self.ifr &= ~TIMER1INT; - self.updateIFR(); - } - break; - - case T1CH: - self.t1l &= 0x1fe; - self.t1l |= val << 9; - self.t1c = self.t1l + 1; - self.t1hit = false; - if (!(self.justhit & 1)) { - self.ifr &= ~TIMER1INT; - self.updateIFR(); - } - self.t1_pb7 = 0; - break; - - case T2CL: - self.t2l &= 0x1fe00; - self.t2l |= val << 1; - break; - - case T2CH: - self.t2l &= 0x1fe; - self.t2l |= val << 9; - self.t2c = self.t2l + 1; - if (self.acr & 0x20) self.t2c -= 2; - if (!(self.justhit & 2)) { - self.ifr &= ~TIMER2INT; - self.updateIFR(); - } - self.t2hit = false; - break; - - case IER: - if (val & 0x80) self.ier |= val & 0x7f; - else self.ier &= ~(val & 0x7f); - self.updateIFR(); - break; - - case IFR: - self.ifr &= ~(val & 0x7f); - if (self.justhit & 1) self.ifr |= TIMER1INT; - if (self.justhit & 2) self.ifr |= TIMER2INT; - self.updateIFR(); - break; - } - }, - - read: function (addr) { - switch (addr & 0xf) { - case ORA: - self.ifr &= ~INT_CA1; - if ((self.pcr & 0xa) !== 0x2) self.ifr &= ~INT_CA2; - self.updateIFR(); - /* falls through */ - case ORAnh: - // Reading ORA reads pin levels regardless of DDRA. - // Of the various 6522 datasheets, this one is clear: - // http://archive.6502.org/datasheets/wdc_w65c22s_mar_2004.pdf - if (self.acr & 1) { - return self.ira; - } - self.recalculatePortAPins(); - return self.portapins; - - case ORB: { - self.ifr &= ~INT_CB1; - if ((self.pcr & 0xa0) !== 0x20) self.ifr &= ~INT_CB2; - self.updateIFR(); - - self.recalculatePortBPins(); - let temp = self.orb & self.ddrb; - if (self.acr & 2) temp |= self.irb & ~self.ddrb; - else temp |= self.portbpins & ~self.ddrb; - // If PB7 is active, it is mixed in regardless of - // whether bit 7 is an input or output. - if (self.acr & 0x80) { - temp &= 0x7f; - temp |= self.t1_pb7 << 7; - } - - const buttons = this.getJoysticks(); + this.t2c = newT2c; + } + } - // clear PB4 and PB5 - temp = temp & 0xcf; // 11001111 + updateIFR() { + if (this.ifr & this.ier & 0x7f) { + this.ifr |= 0x80; + this.cpu.interrupt |= this.irq; + } else { + this.ifr &= ~0x80; + this.cpu.interrupt &= ~this.irq; + } + } - // AUG p418 - // PB4 and PB5 inputs - // These are the inputs from the joystick FIRE buttons. They are - // normally at logic 1 with no button pressed and change to 0 - // when a button is pressed - if (!buttons.button1) { - temp |= 1 << 4; - } - if (!buttons.button2) { - temp |= 1 << 5; - } + write(addr, val) { + let mode; + val |= 0; + switch (addr & 0xf) { + case ORA: + this.ifr &= ~INT_CA1; + if ((this.pcr & 0x0a) !== 0x02) { + // b-em: Not independent interrupt for CA2 + this.ifr &= ~INT_CA2; + } + this.updateIFR(); + + mode = this.pcr & 0x0e; + if (mode === 8) { + // Handshake mode + this.setca2(false); + } else if (mode === 0x0a) { + // Pulse mode + this.setca2(false); + this.setca2(true); + } + /* falls through */ + case ORAnh: + this.ora = val; + this.recalculatePortAPins(); + break; + + case ORB: + this.ifr &= ~INT_CB1; + if ((this.pcr & 0xa0) !== 0x20) { + // b-em: Not independent interrupt for CB2 + this.ifr &= ~INT_CB2; + } + this.updateIFR(); + + this.orb = val; + this.recalculatePortBPins(); + + mode = (this.pcr & 0xe0) >>> 4; + if (mode === 8) { + // Handshake mode + this.setcb2(false); + } else if (mode === 0x0a) { + // Pulse mode + this.setcb2(false); + this.setcb2(true); + } + break; + + case DDRA: + this.ddra = val; + this.recalculatePortAPins(); + break; + + case DDRB: + this.ddrb = val; + this.recalculatePortBPins(); + break; + + case ACR: + this.acr = val; + if (this.justhit & 1 && !(val & 0x40)) this.t1hit = true; + break; + + case PCR: + this.pcr = val; + if ((val & 0xe) === 0xc) this.setca2(false); + else if (val & 0x08) this.setca2(true); + if ((val & 0xe0) === 0xc0) this.setcb2(false); + else if (val & 0x80) this.setcb2(true); + break; + + case SR: + this.sr = val; + break; + + case T1LL: + case T1CL: + this.t1l &= 0x1fe00; + this.t1l |= val << 1; + break; + + case T1LH: + this.t1l &= 0x1fe; + this.t1l |= val << 9; + if (!(this.justhit & 1)) { + this.ifr &= ~TIMER1INT; + this.updateIFR(); + } + break; + + case T1CH: + this.t1l &= 0x1fe; + this.t1l |= val << 9; + this.t1c = this.t1l + 1; + this.t1hit = false; + if (!(this.justhit & 1)) { + this.ifr &= ~TIMER1INT; + this.updateIFR(); + } + this.t1_pb7 = 0; + break; + + case T2CL: + this.t2l &= 0x1fe00; + this.t2l |= val << 1; + break; + + case T2CH: + this.t2l &= 0x1fe; + this.t2l |= val << 9; + this.t2c = this.t2l + 1; + if (this.acr & 0x20) this.t2c -= 2; + if (!(this.justhit & 2)) { + this.ifr &= ~TIMER2INT; + this.updateIFR(); + } + this.t2hit = false; + break; + + case IER: + if (val & 0x80) this.ier |= val & 0x7f; + else this.ier &= ~(val & 0x7f); + this.updateIFR(); + break; + + case IFR: + this.ifr &= ~(val & 0x7f); + if (this.justhit & 1) this.ifr |= TIMER1INT; + if (this.justhit & 2) this.ifr |= TIMER2INT; + this.updateIFR(); + break; + } + } - return temp; + read(addr) { + switch (addr & 0xf) { + case ORA: + this.ifr &= ~INT_CA1; + if ((this.pcr & 0xa) !== 0x2) this.ifr &= ~INT_CA2; + this.updateIFR(); + /* falls through */ + case ORAnh: + // Reading ORA reads pin levels regardless of DDRA. + // Of the various 6522 datasheets, this one is clear: + // http://archive.6502.org/datasheets/wdc_w65c22s_mar_2004.pdf + if (this.acr & 1) { + return this.ira; + } + this.recalculatePortAPins(); + return this.portapins; + + case ORB: { + this.ifr &= ~INT_CB1; + if ((this.pcr & 0xa0) !== 0x20) this.ifr &= ~INT_CB2; + this.updateIFR(); + + this.recalculatePortBPins(); + let temp = this.orb & this.ddrb; + if (this.acr & 2) temp |= this.irb & ~this.ddrb; + else temp |= this.portbpins & ~this.ddrb; + // If PB7 is active, it is mixed in regardless of + // whether bit 7 is an input or output. + if (this.acr & 0x80) { + temp &= 0x7f; + temp |= this.t1_pb7 << 7; } - case DDRA: - return self.ddra; - case DDRB: - return self.ddrb; - case T1LL: - return ((self.t1l & 0x1fe) >>> 1) & 0xff; - case T1LH: - return (self.t1l >>> 9) & 0xff; - - case T1CL: - if (!(self.justhit & 1)) { - self.ifr &= ~TIMER1INT; - self.updateIFR(); - } - return ((self.t1c + 1) >>> 1) & 0xff; - case T1CH: - return ((self.t1c + 1) >>> 9) & 0xff; + const buttons = this.getJoysticks(); - case T2CL: - if (!(self.justhit & 2)) { - self.ifr &= ~TIMER2INT; - self.updateIFR(); - } - return ((self.t2c + 1) >>> 1) & 0xff; - - case T2CH: - return ((self.t2c + 1) >>> 9) & 0xff; - - case SR: - return self.sr; - case ACR: - return self.acr; - case PCR: - return self.pcr; - case IER: - return self.ier | 0x80; - case IFR: - return self.ifr; - default: - throw "Unknown VIA read"; - } - }, - - recalculatePortAPins: function () { - self.portapins = self.ora & self.ddra; - self.portapins |= ~self.ddra & 0xff; - self.drivePortA(); - self.portAUpdated(); - }, - - recalculatePortBPins: function () { - self.portbpins = self.orb & self.ddrb; - self.portbpins |= ~self.ddrb & 0xff; - self.drivePortB(); - self.portBUpdated(); - }, - - setca1: function (level) { - if (level === self.ca1) return; - const pcrSet = !!(self.pcr & 1); - if (pcrSet === level) { - if (self.acr & 1) self.ira = self.portapins; - self.ifr |= INT_CA1; - self.updateIFR(); - if ((self.pcr & 0xc) === 0x8) { - // handshaking - self.setca2(true); + // clear PB4 and PB5 + temp = temp & 0xcf; // 11001111 + + // AUG p418 + // PB4 and PB5 inputs + // These are the inputs from the joystick FIRE buttons. They are + // normally at logic 1 with no button pressed and change to 0 + // when a button is pressed + if (!buttons.button1) { + temp |= 1 << 4; } - } - self.ca1 = level; - }, - - setca2: function (level) { - if (level === self.ca2) return; - self.ca2 = level; - const output = !!(self.pcr & 0x08); - if (self.ca2changecallback) self.ca2changecallback(level, output); - if (output) return; - const pcrSet = !!(self.pcr & 4); - if (pcrSet === level) { - self.ifr |= INT_CA2; - self.updateIFR(); - } - }, - - setcb1: function (level) { - if (level === self.cb1) return; - const pcrSet = !!(self.pcr & 0x10); - if (pcrSet === level) { - if (self.acr & 2) self.irb = self.portbpins; - self.ifr |= INT_CB1; - self.updateIFR(); - if ((self.pcr & 0xc0) === 0x80) { - // handshaking - self.setcb2(true); + if (!buttons.button2) { + temp |= 1 << 5; } + + return temp; } - self.cb1 = level; - }, - - setcb2: function (level) { - if (level === self.cb2) return; - self.cb2 = level; - const output = !!(self.pcr & 0x80); - if (self.cb2changecallback) self.cb2changecallback(level, output); - if (output) return; - const pcrSet = !!(self.pcr & 0x40); - if (pcrSet === level) { - self.ifr |= INT_CB2; - self.updateIFR(); - } - }, - }; - return self; -} + case DDRA: + return this.ddra; + case DDRB: + return this.ddrb; + case T1LL: + return ((this.t1l & 0x1fe) >>> 1) & 0xff; + case T1LH: + return (this.t1l >>> 9) & 0xff; + + case T1CL: + if (!(this.justhit & 1)) { + this.ifr &= ~TIMER1INT; + this.updateIFR(); + } + return ((this.t1c + 1) >>> 1) & 0xff; -export function SysVia(cpu, video, soundChip, cmos, isMaster, initialLayout, getGamepads) { - const self = via(cpu, 0x01); + case T1CH: + return ((this.t1c + 1) >>> 9) & 0xff; - self.IC32 = 0; - self.capsLockLight = false; - self.shiftLockLight = false; - self.keys = []; - for (let i = 0; i < 16; ++i) { - self.keys[i] = new Uint8Array(16); + case T2CL: + if (!(this.justhit & 2)) { + this.ifr &= ~TIMER2INT; + this.updateIFR(); + } + return ((this.t2c + 1) >>> 1) & 0xff; + + case T2CH: + return ((this.t2c + 1) >>> 9) & 0xff; + + case SR: + return this.sr; + case ACR: + return this.acr; + case PCR: + return this.pcr; + case IER: + return this.ier | 0x80; + case IFR: + return this.ifr; + default: + throw "Unknown VIA read"; + } } - self.setVBlankInt = self.setca1; + // May be overridden in subclasses + drivePortA() {} - self.setKeyLayout = function (map) { - self.keycodeToRowCol = utils.getKeyMap(map); - }; - self.setKeyLayout(initialLayout); + portAUpdated() {} - self.keyboardEnabled = true; + drivePortB() {} + + portBUpdated() {} + + getJoysticks() { + return { button1: false, button2: false }; + } + + recalculatePortAPins() { + this.portapins = this.ora & this.ddra; + this.portapins |= ~this.ddra & 0xff; + this.drivePortA(); + this.portAUpdated(); + } - function clearKeys() { - for (let i = 0; i < self.keys.length; ++i) { - for (let j = 0; j < self.keys[i].length; ++j) { - self.keys[i][j] = false; + recalculatePortBPins() { + this.portbpins = this.orb & this.ddrb; + this.portbpins |= ~this.ddrb & 0xff; + this.drivePortB(); + this.portBUpdated(); + } + + setca1(level) { + if (level === this.ca1) return; + const pcrSet = !!(this.pcr & 1); + if (pcrSet === level) { + if (this.acr & 1) this.ira = this.portapins; + this.ifr |= INT_CA1; + this.updateIFR(); + if ((this.pcr & 0xc) === 0x8) { + // handshaking + this.setca2(true); } } - self.updateKeys(); + this.ca1 = level; } - self.clearKeys = clearKeys; + setca2(level) { + if (level === this.ca2) return; + this.ca2 = level; + const output = !!(this.pcr & 0x08); + if (this.ca2changecallback) this.ca2changecallback(level, output); + if (output) return; + const pcrSet = !!(this.pcr & 4); + if (pcrSet === level) { + this.ifr |= INT_CA2; + this.updateIFR(); + } + } + + setcb1(level) { + if (level === this.cb1) return; + const pcrSet = !!(this.pcr & 0x10); + if (pcrSet === level) { + if (this.acr & 2) this.irb = this.portbpins; + this.ifr |= INT_CB1; + this.updateIFR(); + if ((this.pcr & 0xc0) === 0x80) { + // handshaking + this.setcb2(true); + } + } + this.cb1 = level; + } + + setcb2(level) { + if (level === this.cb2) return; + this.cb2 = level; + const output = !!(this.pcr & 0x80); + if (this.cb2changecallback) this.cb2changecallback(level, output); + if (output) return; + const pcrSet = !!(this.pcr & 0x40); + if (pcrSet === level) { + this.ifr |= INT_CB2; + this.updateIFR(); + } + } +} + +export class SysVia extends Via { + constructor(cpu, video, soundChip, cmos, isMaster, initialLayout, getGamepads) { + super(cpu, 0x01); - self.disableKeyboard = function () { - self.keyboardEnabled = false; - clearKeys(); - }; + this.IC32 = 0; + this.capsLockLight = false; + this.shiftLockLight = false; + this.keys = []; + for (let i = 0; i < 16; ++i) { + this.keys[i] = new Uint8Array(16); + } + this.keyboardEnabled = true; + this.setKeyLayout(initialLayout); + this.video = video; + this.soundChip = soundChip; + this.cmos = cmos; + this.isMaster = isMaster; + this.getGamepadsFunc = getGamepads; + + this.reset(); + } - self.enableKeyboard = function () { - self.keyboardEnabled = true; - clearKeys(); - }; + setKeyLayout(map) { + this.keycodeToRowCol = utils.getKeyMap(map); + } - self.set = function (key, val, shiftDown) { - if (!self.keyboardEnabled) { - return; + setVBlankInt(level) { + this.setca1(level); + } + + clearKeys() { + for (let i = 0; i < this.keys.length; ++i) { + for (let j = 0; j < this.keys[i].length; ++j) { + this.keys[i][j] = false; + } } + this.updateKeys(); + } - const colrow = self.keycodeToRowCol[!!shiftDown][key]; + disableKeyboard() { + this.keyboardEnabled = false; + this.clearKeys(); + } + + enableKeyboard() { + this.keyboardEnabled = true; + this.clearKeys(); + } + + set(key, val, shiftDown) { + if (!this.keyboardEnabled) return; + const colrow = this.keycodeToRowCol[!!shiftDown][key]; if (!colrow) return; + this.keys[colrow[0]][colrow[1]] = val; + this.updateKeys(); + } + + keyDown(key, shiftDown) { + this.set(key, 1, shiftDown); + } - self.keys[colrow[0]][colrow[1]] = val; - self.updateKeys(); - }; - self.keyDown = function (key, shiftDown) { - self.set(key, 1, shiftDown); - }; - self.keyUp = function (key) { + keyUp(key) { // set up for both keymaps // (with and without shift) - self.set(key, 0, true); - self.set(key, 0, false); - }; - - self.keyDownRaw = function (colrow) { - self.keys[colrow[0]][colrow[1]] = 1; - self.updateKeys(); - }; - self.keyUpRaw = function (colrow) { - self.keys[colrow[0]][colrow[1]] = 0; - self.updateKeys(); - }; - self.keyToggleRaw = function (colrow) { - self.keys[colrow[0]][colrow[1]] = 1 - self.keys[colrow[0]][colrow[1]]; - self.updateKeys(); - }; - self.hasAnyKeyDown = function () { + this.set(key, 0, true); + this.set(key, 0, false); + } + + keyDownRaw(colrow) { + this.keys[colrow[0]][colrow[1]] = 1; + this.updateKeys(); + } + + keyUpRaw(colrow) { + this.keys[colrow[0]][colrow[1]] = 0; + this.updateKeys(); + } + + keyToggleRaw(colrow) { + this.keys[colrow[0]][colrow[1]] = 1 - this.keys[colrow[0]][colrow[1]]; + this.updateKeys(); + } + + hasAnyKeyDown() { // 10 for BBC, 13 for Master 128 const numCols = 13; for (let i = 0; i < numCols; ++i) { for (let j = 0; j < 8; ++j) { - if (self.keys[i][j]) { + if (this.keys[i][j]) { return true; } } } return false; - }; + } - self.updateKeys = function () { + updateKeys() { // 10 for BBC, 13 for Master 128 const numCols = 13; - if (self.IC32 & 8) { + if (this.IC32 & 8) { for (let i = 0; i < numCols; ++i) { for (let j = 1; j < 8; ++j) { - if (self.keys[i][j]) { - self.setca2(true); + if (this.keys[i][j]) { + this.setca2(true); return; } } @@ -519,74 +543,74 @@ export function SysVia(cpu, video, soundChip, cmos, isMaster, initialLayout, get // "wins" vs. CMOS. // At 0 also wins against an output pin. - const portapins = self.portapins; + const portapins = this.portapins; const keyrow = (portapins >>> 4) & 7; const keycol = portapins & 0xf; - if (!self.keys[keycol][keyrow]) { - self.portapins &= 0x7f; - } else if (!(self.ddra & 0x80)) { - self.portapins |= 0x80; + if (!this.keys[keycol][keyrow]) { + this.portapins &= 0x7f; + } else if (!(this.ddra & 0x80)) { + this.portapins |= 0x80; } if (keycol < numCols) { for (let j = 1; j < 8; ++j) { - if (self.keys[keycol][j]) { - self.setca2(true); + if (this.keys[keycol][j]) { + this.setca2(true); return; } } } } - self.setca2(false); - }; + this.setca2(false); + } - self.portAUpdated = function () { - self.updateKeys(); - soundChip.updateSlowDataBus(self.portapins, !(self.IC32 & 1)); - }; + portAUpdated() { + this.updateKeys(); + this.soundChip.updateSlowDataBus(this.portapins, !(this.IC32 & 1)); + } - self.portBUpdated = function () { - const portbpins = self.portbpins; - if (portbpins & 8) self.IC32 |= 1 << (portbpins & 7); - else self.IC32 &= ~(1 << (portbpins & 7)); + portBUpdated() { + const portbpins = this.portbpins; + if (portbpins & 8) this.IC32 |= 1 << (portbpins & 7); + else this.IC32 &= ~(1 << (portbpins & 7)); - self.capsLockLight = !(self.IC32 & 0x40); - self.shiftLockLight = !(self.IC32 & 0x80); + this.capsLockLight = !(this.IC32 & 0x40); + this.shiftLockLight = !(this.IC32 & 0x80); - video.setScreenAdd((self.IC32 & 16 ? 2 : 0) | (self.IC32 & 32 ? 1 : 0)); + this.video.setScreenAdd((this.IC32 & 16 ? 2 : 0) | (this.IC32 & 32 ? 1 : 0)); - if (isMaster) cmos.writeControl(portbpins, self.portapins, self.IC32); + if (this.isMaster) this.cmos.writeControl(portbpins, this.portapins, this.IC32); // Updating IC32 may have enabled peripherals attached to port A. - self.recalculatePortAPins(); - }; + this.recalculatePortAPins(); + } - self.drivePortA = function () { + drivePortA() { // For experiments where we tested these behaviors, see: // https://stardot.org.uk/forums/viewtopic.php?f=4&t=17597 // If either keyboard or CMOS pulls a given pin low, it "wins" // vs. via output. let busval = 0xff; - if (isMaster) busval &= cmos.read(self.IC32); - self.portapins &= busval; - self.updateKeys(); - }; + if (this.isMaster) busval &= this.cmos.read(this.IC32); + this.portapins &= busval; + this.updateKeys(); + } - self.drivePortB = function () { + drivePortB() { // Nothing driving here. // Note that if speech were fitted, it drives bit 7 low. - }; + } - self.getGamepads = function () { - if (getGamepads) return getGamepads(); + getGamepads() { + if (this.getGamepadsFunc) return this.getGamepadsFunc(); return null; - }; + } - self.getJoysticks = function () { + getJoysticks() { let button1 = false; let button2 = false; - const pads = self.getGamepads(); + const pads = this.getGamepads(); if (pads && pads[0]) { const pad = pads[0]; const pad2 = pads[1]; @@ -598,36 +622,22 @@ export function SysVia(cpu, video, soundChip, cmos, isMaster, initialLayout, get } return { button1: button1, button2: button2 }; - }; - - self.reset(); - return self; + } } -export function UserVia(cpu, isMaster, userPortPeripheral) { - const self = via(cpu, 0x02); - - // nothing connected to user VIA - self.getJoysticks = function () { - return { button1: false, button2: false }; - }; - - self.portAUpdated = function () { - // Printer port. - }; - - self.portBUpdated = function () { - userPortPeripheral.write(self.portbpins); - }; - - self.drivePortA = function () { - // Printer port. - }; +export class UserVia extends Via { + constructor(cpu, isMaster, userPortPeripheral) { + super(cpu, 0x02); + this.isMaster = isMaster; + this.userPortPeripheral = userPortPeripheral; + this.reset(); + } - self.drivePortB = function () { - self.portbpins &= userPortPeripheral.read(); - }; + portBUpdated() { + this.userPortPeripheral.write(this.portbpins); + } - self.reset(); - return self; + drivePortB() { + this.portbpins &= this.userPortPeripheral.read(); + } }