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/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;