jsbeeb 1.7.0 → 1.8.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 CHANGED
@@ -7,7 +7,7 @@
7
7
  "name": "jsbeeb",
8
8
  "description": "Emulate a BBC Micro",
9
9
  "repository": "git@github.com:mattgodbolt/jsbeeb.git",
10
- "version": "1.7.0",
10
+ "version": "1.8.0",
11
11
  "//": "If you change the version of Node, it must also be updated at the top of the Dockerfile.",
12
12
  "engines": {
13
13
  "node": "22"
@@ -102,6 +102,22 @@ export class TestMachine {
102
102
  this.processor.fdc.loadDisc(0, fdc.discFor(this.processor.fdc, "", data));
103
103
  }
104
104
 
105
+ /**
106
+ * Load a disc image from raw data (Uint8Array or Buffer).
107
+ * @param {Uint8Array|Buffer} data - raw disc image bytes
108
+ */
109
+ loadDiscData(data) {
110
+ this.processor.fdc.loadDisc(0, fdc.discFor(this.processor.fdc, "", data));
111
+ }
112
+
113
+ /**
114
+ * Reset the machine.
115
+ * @param {boolean} hard - true for power-on reset, false for soft reset
116
+ */
117
+ reset(hard) {
118
+ this.processor.reset(hard);
119
+ }
120
+
105
121
  async loadBasic(source) {
106
122
  const tokeniser = await Tokeniser.create();
107
123
  const tokenised = tokeniser.tokenise(source);
@@ -186,8 +202,19 @@ export class TestMachine {
186
202
  return { code: utils.keyCodes.K4, shift: true };
187
203
  case "%":
188
204
  return { code: utils.keyCodes.K5, shift: true };
189
- default:
190
- return { code: ch.toUpperCase().charCodeAt(0), shift: false };
205
+ default: {
206
+ const upper = ch.toUpperCase();
207
+ const isLetter = (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");
208
+ if (isLetter) {
209
+ const wantUpper = ch >= "A" && ch <= "Z";
210
+ const capsOn = this.processor.sysvia.capsLockLight;
211
+ // CAPS LOCK on: unshifted = upper, shifted = lower
212
+ // CAPS LOCK off: unshifted = lower, shifted = upper
213
+ const needShift = capsOn ? !wantUpper : wantUpper;
214
+ return { code: upper.charCodeAt(0), shift: needShift };
215
+ }
216
+ return { code: ch.charCodeAt(0), shift: false };
217
+ }
191
218
  }
192
219
  }
193
220
 
@@ -201,7 +228,7 @@ export class TestMachine {
201
228
  async type(text) {
202
229
  const fullText = text + "\n"; // append RETURN
203
230
  const keys = fullText.split("").map((ch) => this._charToKey(ch));
204
- const holdCycles = 40000; // cycles between key events, matching old type() timing
231
+ const holdCycles = 40000;
205
232
  let index = 0;
206
233
  let phase = "idle"; // "idle" → "down" → "idle"
207
234
  let nextEventCycle = 0;
@@ -246,6 +273,34 @@ export class TestMachine {
246
273
  }
247
274
  }
248
275
 
276
+ /**
277
+ * Press a key on the BBC keyboard.
278
+ * @param {number} code - BBC key code (e.g. utils.keyCodes.SPACE)
279
+ */
280
+ keyDown(code) {
281
+ this.processor.sysvia.keyDown(code);
282
+ }
283
+
284
+ /**
285
+ * Release a key on the BBC keyboard.
286
+ * @param {number} code - BBC key code
287
+ */
288
+ keyUp(code) {
289
+ this.processor.sysvia.keyUp(code);
290
+ }
291
+
292
+ /**
293
+ * Load a ROM image directly into a sideways RAM slot.
294
+ * @param {number} slot - slot number (0-15, typically 4-7 for SWRAM)
295
+ * @param {Uint8Array|Buffer} data - ROM data (up to 16384 bytes)
296
+ */
297
+ loadSidewaysRam(slot, data) {
298
+ const offset = this.processor.romOffset + slot * 16384;
299
+ for (let i = 0; i < data.length && i < 16384; i++) {
300
+ this.processor.ramRomOs[offset + i] = data[i];
301
+ }
302
+ }
303
+
249
304
  writebyte(addr, val) {
250
305
  this.processor.writemem(addr, val);
251
306
  }