jsbeeb 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/6502.js +84 -0
- package/src/acia.js +42 -0
- package/src/adc.js +22 -0
- package/src/app/app.js +24 -0
- package/src/app/electron.js +10 -1
- package/src/app/preload.js +1 -0
- package/src/bem-snapshot.js +681 -0
- package/src/machine-session.js +91 -0
- package/src/main.js +137 -1
- package/src/rewind.js +71 -0
- package/src/scheduler.js +28 -0
- package/src/snapshot.js +110 -0
- package/src/soundchip.js +39 -0
- package/src/state-utils.js +66 -0
- package/src/teletext.js +69 -0
- package/src/via.js +91 -0
- package/src/video.js +143 -0
package/src/teletext.js
CHANGED
|
@@ -150,6 +150,75 @@ export class Teletext {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
snapshotState() {
|
|
154
|
+
// Encode glyph table references as strings for serialization
|
|
155
|
+
let nextGlyphsName, curGlyphsName, heldGlyphsName;
|
|
156
|
+
for (const [name, table] of [
|
|
157
|
+
["normal", this.normalGlyphs],
|
|
158
|
+
["graphics", this.graphicsGlyphs],
|
|
159
|
+
["separated", this.separatedGlyphs],
|
|
160
|
+
]) {
|
|
161
|
+
if (this.nextGlyphs === table) nextGlyphsName = name;
|
|
162
|
+
if (this.curGlyphs === table) curGlyphsName = name;
|
|
163
|
+
if (this.heldGlyphs === table) heldGlyphsName = name;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
prevCol: this.prevCol,
|
|
167
|
+
col: this.col,
|
|
168
|
+
bg: this.bg,
|
|
169
|
+
sep: this.sep,
|
|
170
|
+
dbl: this.dbl,
|
|
171
|
+
oldDbl: this.oldDbl,
|
|
172
|
+
secondHalfOfDouble: this.secondHalfOfDouble,
|
|
173
|
+
wasDbl: this.wasDbl,
|
|
174
|
+
gfx: this.gfx,
|
|
175
|
+
flash: this.flash,
|
|
176
|
+
flashOn: this.flashOn,
|
|
177
|
+
flashTime: this.flashTime,
|
|
178
|
+
heldChar: this.heldChar,
|
|
179
|
+
holdChar: this.holdChar,
|
|
180
|
+
dataQueue: [...this.dataQueue],
|
|
181
|
+
scanlineCounter: this.scanlineCounter,
|
|
182
|
+
levelDEW: this.levelDEW,
|
|
183
|
+
levelDISPTMG: this.levelDISPTMG,
|
|
184
|
+
levelRA0: this.levelRA0,
|
|
185
|
+
nextGlyphs: nextGlyphsName,
|
|
186
|
+
curGlyphs: curGlyphsName,
|
|
187
|
+
heldGlyphs: heldGlyphsName,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
restoreState(state) {
|
|
192
|
+
this.prevCol = state.prevCol;
|
|
193
|
+
this.col = state.col;
|
|
194
|
+
this.bg = state.bg;
|
|
195
|
+
this.sep = state.sep;
|
|
196
|
+
this.dbl = state.dbl;
|
|
197
|
+
this.oldDbl = state.oldDbl;
|
|
198
|
+
this.secondHalfOfDouble = state.secondHalfOfDouble;
|
|
199
|
+
this.wasDbl = state.wasDbl;
|
|
200
|
+
this.gfx = state.gfx;
|
|
201
|
+
this.flash = state.flash;
|
|
202
|
+
this.flashOn = state.flashOn;
|
|
203
|
+
this.flashTime = state.flashTime;
|
|
204
|
+
this.heldChar = state.heldChar;
|
|
205
|
+
this.holdChar = state.holdChar;
|
|
206
|
+
this.dataQueue = [...state.dataQueue];
|
|
207
|
+
this.scanlineCounter = state.scanlineCounter;
|
|
208
|
+
this.levelDEW = state.levelDEW;
|
|
209
|
+
this.levelDISPTMG = state.levelDISPTMG;
|
|
210
|
+
this.levelRA0 = state.levelRA0;
|
|
211
|
+
|
|
212
|
+
const glyphMap = {
|
|
213
|
+
normal: this.normalGlyphs,
|
|
214
|
+
graphics: this.graphicsGlyphs,
|
|
215
|
+
separated: this.separatedGlyphs,
|
|
216
|
+
};
|
|
217
|
+
this.nextGlyphs = glyphMap[state.nextGlyphs] || this.normalGlyphs;
|
|
218
|
+
this.curGlyphs = glyphMap[state.curGlyphs] || this.normalGlyphs;
|
|
219
|
+
this.heldGlyphs = glyphMap[state.heldGlyphs] || this.normalGlyphs;
|
|
220
|
+
}
|
|
221
|
+
|
|
153
222
|
setNextChars() {
|
|
154
223
|
if (this.gfx) {
|
|
155
224
|
if (this.sep) {
|
package/src/via.js
CHANGED
|
@@ -402,6 +402,73 @@ class Via {
|
|
|
402
402
|
this.portBUpdated();
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
snapshotState() {
|
|
406
|
+
return {
|
|
407
|
+
ora: this.ora,
|
|
408
|
+
orb: this.orb,
|
|
409
|
+
ira: this.ira,
|
|
410
|
+
irb: this.irb,
|
|
411
|
+
ddra: this.ddra,
|
|
412
|
+
ddrb: this.ddrb,
|
|
413
|
+
sr: this.sr,
|
|
414
|
+
t1l: this.t1l,
|
|
415
|
+
t2l: this.t2l,
|
|
416
|
+
t1c: this.t1c,
|
|
417
|
+
t2c: this.t2c,
|
|
418
|
+
acr: this.acr,
|
|
419
|
+
pcr: this.pcr,
|
|
420
|
+
ifr: this.ifr,
|
|
421
|
+
ier: this.ier,
|
|
422
|
+
t1hit: this.t1hit,
|
|
423
|
+
t2hit: this.t2hit,
|
|
424
|
+
portapins: this.portapins,
|
|
425
|
+
portbpins: this.portbpins,
|
|
426
|
+
ca1: this.ca1,
|
|
427
|
+
ca2: this.ca2,
|
|
428
|
+
cb1: this.cb1,
|
|
429
|
+
cb2: this.cb2,
|
|
430
|
+
justhit: this.justhit,
|
|
431
|
+
t1_pb7: this.t1_pb7,
|
|
432
|
+
lastPolltime: this.lastPolltime,
|
|
433
|
+
taskOffset: this.task.scheduled() ? this.task.expireEpoch - this.scheduler.epoch : null,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
restoreState(state) {
|
|
438
|
+
this.ora = state.ora;
|
|
439
|
+
this.orb = state.orb;
|
|
440
|
+
this.ira = state.ira;
|
|
441
|
+
this.irb = state.irb;
|
|
442
|
+
this.ddra = state.ddra;
|
|
443
|
+
this.ddrb = state.ddrb;
|
|
444
|
+
this.sr = state.sr;
|
|
445
|
+
this.t1l = state.t1l;
|
|
446
|
+
this.t2l = state.t2l;
|
|
447
|
+
this.t1c = state.t1c;
|
|
448
|
+
this.t2c = state.t2c;
|
|
449
|
+
this.acr = state.acr;
|
|
450
|
+
this.pcr = state.pcr;
|
|
451
|
+
this.ifr = state.ifr;
|
|
452
|
+
this.ier = state.ier;
|
|
453
|
+
this.t1hit = state.t1hit;
|
|
454
|
+
this.t2hit = state.t2hit;
|
|
455
|
+
this.portapins = state.portapins;
|
|
456
|
+
this.portbpins = state.portbpins;
|
|
457
|
+
this.ca1 = state.ca1;
|
|
458
|
+
this.ca2 = state.ca2;
|
|
459
|
+
this.cb1 = state.cb1;
|
|
460
|
+
this.cb2 = state.cb2;
|
|
461
|
+
this.justhit = state.justhit;
|
|
462
|
+
this.t1_pb7 = state.t1_pb7;
|
|
463
|
+
this.lastPolltime = state.lastPolltime;
|
|
464
|
+
this.updateIFR();
|
|
465
|
+
if (state.taskOffset !== null) {
|
|
466
|
+
this.task.reschedule(state.taskOffset);
|
|
467
|
+
} else {
|
|
468
|
+
this.task.cancel();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
405
472
|
setca1(level) {
|
|
406
473
|
if (level === this.ca1) return;
|
|
407
474
|
const pcrSet = !!(this.pcr & 1);
|
|
@@ -484,6 +551,30 @@ export class SysVia extends Via {
|
|
|
484
551
|
this.reset();
|
|
485
552
|
}
|
|
486
553
|
|
|
554
|
+
snapshotState() {
|
|
555
|
+
return {
|
|
556
|
+
...super.snapshotState(),
|
|
557
|
+
IC32: this.IC32,
|
|
558
|
+
capsLockLight: this.capsLockLight,
|
|
559
|
+
shiftLockLight: this.shiftLockLight,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
restoreState(state) {
|
|
564
|
+
super.restoreState(state);
|
|
565
|
+
this.IC32 = state.IC32;
|
|
566
|
+
this.capsLockLight = state.capsLockLight;
|
|
567
|
+
this.shiftLockLight = state.shiftLockLight;
|
|
568
|
+
// Re-apply IC32 side effects (sound chip data bus, CMOS, port A)
|
|
569
|
+
// Must call portBUpdated directly (not recalculatePortBPins which
|
|
570
|
+
// overwrites IC32 based on portbpins) and then re-set IC32.
|
|
571
|
+
this.portBUpdated();
|
|
572
|
+
this.IC32 = state.IC32;
|
|
573
|
+
this.capsLockLight = state.capsLockLight;
|
|
574
|
+
this.shiftLockLight = state.shiftLockLight;
|
|
575
|
+
this.recalculatePortAPins();
|
|
576
|
+
}
|
|
577
|
+
|
|
487
578
|
setKeyLayout(map) {
|
|
488
579
|
this.keycodeToRowCol = utils.getKeyMap(map);
|
|
489
580
|
}
|
package/src/video.js
CHANGED
|
@@ -82,6 +82,36 @@ class Ula {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
snapshotState() {
|
|
86
|
+
return {
|
|
87
|
+
collook: this.collook.slice(),
|
|
88
|
+
flash: this.flash.slice(),
|
|
89
|
+
paletteWriteFlag: this.paletteWriteFlag,
|
|
90
|
+
paletteFirstByte: this.paletteFirstByte,
|
|
91
|
+
paletteMode: this.paletteMode,
|
|
92
|
+
horizontalOffset: this.horizontalOffset,
|
|
93
|
+
leftBlank: this.leftBlank,
|
|
94
|
+
disabled: this.disabled,
|
|
95
|
+
attributeMode: this.attributeMode,
|
|
96
|
+
attributeText: this.attributeText,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
restoreState(state) {
|
|
101
|
+
this.collook.set(state.collook);
|
|
102
|
+
this.flash.set(state.flash);
|
|
103
|
+
this.paletteWriteFlag = state.paletteWriteFlag;
|
|
104
|
+
this.paletteFirstByte = state.paletteFirstByte;
|
|
105
|
+
this.paletteMode = state.paletteMode;
|
|
106
|
+
this.horizontalOffset = state.horizontalOffset;
|
|
107
|
+
this.leftBlank = state.leftBlank;
|
|
108
|
+
this.disabled = state.disabled;
|
|
109
|
+
this.attributeMode = state.attributeMode;
|
|
110
|
+
this.attributeText = state.attributeText;
|
|
111
|
+
this._recomputeUlaPal(!!(this.video.ulactrl & 1));
|
|
112
|
+
this.video.teletext.rebuildColours(this.collook);
|
|
113
|
+
}
|
|
114
|
+
|
|
85
115
|
// ULA control register (&FE20).
|
|
86
116
|
_writeControl(val) {
|
|
87
117
|
if ((this.video.ulactrl ^ val) & 1) {
|
|
@@ -208,6 +238,14 @@ class Crtc {
|
|
|
208
238
|
]);
|
|
209
239
|
}
|
|
210
240
|
|
|
241
|
+
snapshotState() {
|
|
242
|
+
return { curReg: this.curReg };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
restoreState(state) {
|
|
246
|
+
this.curReg = state.curReg;
|
|
247
|
+
}
|
|
248
|
+
|
|
211
249
|
read(addr) {
|
|
212
250
|
if (!(addr & 1)) return 0;
|
|
213
251
|
switch (this.curReg) {
|
|
@@ -389,6 +427,111 @@ export class Video {
|
|
|
389
427
|
this.paint();
|
|
390
428
|
}
|
|
391
429
|
|
|
430
|
+
snapshotState() {
|
|
431
|
+
return {
|
|
432
|
+
regs: this.regs.slice(),
|
|
433
|
+
bitmapX: this.bitmapX,
|
|
434
|
+
bitmapY: this.bitmapY,
|
|
435
|
+
oddClock: this.oddClock,
|
|
436
|
+
frameCount: this.frameCount,
|
|
437
|
+
doEvenFrameLogic: this.doEvenFrameLogic,
|
|
438
|
+
isEvenRender: this.isEvenRender,
|
|
439
|
+
lastRenderWasEven: this.lastRenderWasEven,
|
|
440
|
+
firstScanline: this.firstScanline,
|
|
441
|
+
inHSync: this.inHSync,
|
|
442
|
+
inVSync: this.inVSync,
|
|
443
|
+
hadVSyncThisRow: this.hadVSyncThisRow,
|
|
444
|
+
checkVertAdjust: this.checkVertAdjust,
|
|
445
|
+
endOfMainLatched: this.endOfMainLatched,
|
|
446
|
+
endOfVertAdjustLatched: this.endOfVertAdjustLatched,
|
|
447
|
+
endOfFrameLatched: this.endOfFrameLatched,
|
|
448
|
+
inVertAdjust: this.inVertAdjust,
|
|
449
|
+
inDummyRaster: this.inDummyRaster,
|
|
450
|
+
hpulseWidth: this.hpulseWidth,
|
|
451
|
+
vpulseWidth: this.vpulseWidth,
|
|
452
|
+
hpulseCounter: this.hpulseCounter,
|
|
453
|
+
vpulseCounter: this.vpulseCounter,
|
|
454
|
+
dispEnabled: this.dispEnabled,
|
|
455
|
+
horizCounter: this.horizCounter,
|
|
456
|
+
vertCounter: this.vertCounter,
|
|
457
|
+
scanlineCounter: this.scanlineCounter,
|
|
458
|
+
vertAdjustCounter: this.vertAdjustCounter,
|
|
459
|
+
addr: this.addr,
|
|
460
|
+
lineStartAddr: this.lineStartAddr,
|
|
461
|
+
nextLineStartAddr: this.nextLineStartAddr,
|
|
462
|
+
ulactrl: this.ulactrl,
|
|
463
|
+
pixelsPerChar: this.pixelsPerChar,
|
|
464
|
+
halfClock: this.halfClock,
|
|
465
|
+
ulaMode: this.ulaMode,
|
|
466
|
+
teletextMode: this.teletextMode,
|
|
467
|
+
displayEnableSkew: this.displayEnableSkew,
|
|
468
|
+
ulaPal: this.ulaPal.slice(),
|
|
469
|
+
actualPal: this.actualPal.slice(),
|
|
470
|
+
cursorOn: this.cursorOn,
|
|
471
|
+
cursorOff: this.cursorOff,
|
|
472
|
+
cursorOnThisFrame: this.cursorOnThisFrame,
|
|
473
|
+
cursorDrawIndex: this.cursorDrawIndex,
|
|
474
|
+
cursorPos: this.cursorPos,
|
|
475
|
+
interlacedSyncAndVideo: this.interlacedSyncAndVideo,
|
|
476
|
+
screenSubtract: this.screenSubtract,
|
|
477
|
+
ula: this.ula.snapshotState(),
|
|
478
|
+
crtc: this.crtc.snapshotState(),
|
|
479
|
+
teletext: this.teletext.snapshotState(),
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
restoreState(state) {
|
|
484
|
+
this.regs.set(state.regs);
|
|
485
|
+
this.bitmapX = state.bitmapX;
|
|
486
|
+
this.bitmapY = state.bitmapY;
|
|
487
|
+
this.oddClock = state.oddClock;
|
|
488
|
+
this.frameCount = state.frameCount;
|
|
489
|
+
this.doEvenFrameLogic = state.doEvenFrameLogic;
|
|
490
|
+
this.isEvenRender = state.isEvenRender;
|
|
491
|
+
this.lastRenderWasEven = state.lastRenderWasEven;
|
|
492
|
+
this.firstScanline = state.firstScanline;
|
|
493
|
+
this.inHSync = state.inHSync;
|
|
494
|
+
this.inVSync = state.inVSync;
|
|
495
|
+
this.hadVSyncThisRow = state.hadVSyncThisRow;
|
|
496
|
+
this.checkVertAdjust = state.checkVertAdjust;
|
|
497
|
+
this.endOfMainLatched = state.endOfMainLatched;
|
|
498
|
+
this.endOfVertAdjustLatched = state.endOfVertAdjustLatched;
|
|
499
|
+
this.endOfFrameLatched = state.endOfFrameLatched;
|
|
500
|
+
this.inVertAdjust = state.inVertAdjust;
|
|
501
|
+
this.inDummyRaster = state.inDummyRaster;
|
|
502
|
+
this.hpulseWidth = state.hpulseWidth;
|
|
503
|
+
this.vpulseWidth = state.vpulseWidth;
|
|
504
|
+
this.hpulseCounter = state.hpulseCounter;
|
|
505
|
+
this.vpulseCounter = state.vpulseCounter;
|
|
506
|
+
this.dispEnabled = state.dispEnabled;
|
|
507
|
+
this.horizCounter = state.horizCounter;
|
|
508
|
+
this.vertCounter = state.vertCounter;
|
|
509
|
+
this.scanlineCounter = state.scanlineCounter;
|
|
510
|
+
this.vertAdjustCounter = state.vertAdjustCounter;
|
|
511
|
+
this.addr = state.addr;
|
|
512
|
+
this.lineStartAddr = state.lineStartAddr;
|
|
513
|
+
this.nextLineStartAddr = state.nextLineStartAddr;
|
|
514
|
+
this.ulactrl = state.ulactrl;
|
|
515
|
+
this.pixelsPerChar = state.pixelsPerChar;
|
|
516
|
+
this.halfClock = state.halfClock;
|
|
517
|
+
this.ulaMode = state.ulaMode;
|
|
518
|
+
this.teletextMode = state.teletextMode;
|
|
519
|
+
this.displayEnableSkew = state.displayEnableSkew;
|
|
520
|
+
this.actualPal.set(state.actualPal);
|
|
521
|
+
this.cursorOn = state.cursorOn;
|
|
522
|
+
this.cursorOff = state.cursorOff;
|
|
523
|
+
this.cursorOnThisFrame = state.cursorOnThisFrame;
|
|
524
|
+
this.cursorDrawIndex = state.cursorDrawIndex;
|
|
525
|
+
this.cursorPos = state.cursorPos;
|
|
526
|
+
this.interlacedSyncAndVideo = state.interlacedSyncAndVideo;
|
|
527
|
+
this.screenSubtract = state.screenSubtract;
|
|
528
|
+
this.ula.restoreState(state.ula);
|
|
529
|
+
this.crtc.restoreState(state.crtc);
|
|
530
|
+
this.teletext.restoreState(state.teletext);
|
|
531
|
+
// Restore ulaPal after ULA restore, since ULA recomputation may overwrite it
|
|
532
|
+
this.ulaPal.set(state.ulaPal);
|
|
533
|
+
}
|
|
534
|
+
|
|
392
535
|
reset(cpu, via) {
|
|
393
536
|
this.cpu = cpu;
|
|
394
537
|
this.sysvia = via;
|