diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 11a3c6e..379c305 --- a/README.md +++ b/README.md @@ -7,7 +7,11 @@ The `package.json` can install an HTTP server, just do: npm install -Download [`theloop.2.rom`](https://github.com/kervinck/gigatron-rom/raw/master/theloop.2.rom) into the `src` directory. +Pick and download a [`*.rom`](https://github.com/kervinck/gigatron-rom/) file into the `src/` directory and rename it as `gigatron.rom` + +Direct links: +[`ROM v1`](https://github.com/kervinck/gigatron-rom/raw/master/ROMv1.rom) +[`ROM v2`](https://github.com/kervinck/gigatron-rom/raw/master/ROMv2.rom) Start the HTTP server @@ -19,4 +23,4 @@ Use the cursor keys for the D-pad. ## GT1 Files -GT1 files can be loaded by starting the `Loader` and dropping a `.gt1` file onto the VGA display from File Explorer or Finder. +GT1 files can be loaded by dropping a `.gt1` file onto the VGA display from File Explorer or Finder. diff --git a/src/gamepad.js b/src/gamepad.js index 10da3fc..296c2e7 100644 --- a/src/gamepad.js +++ b/src/gamepad.js @@ -60,6 +60,22 @@ export class Gamepad { this.keyMap[key] = buttonMap[button]; } } + + /* build map of ASCII codes that the Gigatron understands as well */ + this.asciiMap = { + 'Tab': 9, + 'Enter': 10, + 'Escape': 27, + 'Esc': 27, + 'Delete': 127, + 'Backspace': 127, + }; + for (let ascii=32; ascii<127; ascii++) { + this.asciiMap[String.fromCharCode(ascii)] = ascii; + } + for (let fnKey=1; fnKey<=12; fnKey++) { + this.asciiMap['F' + fnKey] = 0xc0 + fnKey; + } } /** start handling key events */ @@ -70,14 +86,28 @@ export class Gamepad { if (bit) { this.pressed |= bit; event.preventDefault(); + } else { + let ascii = this.asciiMap[event.key]; + if (ascii) { + if (event.ctrlKey) { + // Control codes (e.g. Ctrl-C for ETX or BREAK) + if (ascii == 63 /*'?'*/) ascii = 127; + else if (ascii == 32 /*' '*/) ascii = 0; + else ascii &= 31; + } + this.pressed = ascii ^ 0xff; /// will be inverted again in tick() + event.preventDefault(); + } } }) .on('keyup', (event) => { let bit = this.keyMap[event.key]; if (bit) { this.pressed &= ~bit; - event.preventDefault(); + } else { + this.pressed = 0; } + event.preventDefault(); }); this.enabled = true; } diff --git a/src/index.html b/src/index.html index ae28eb6..f6884c6 100644 --- a/src/index.html +++ b/src/index.html @@ -54,13 +54,14 @@
- + @@ -97,101 +98,93 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SELECT - - - - - - START - - - - - - - - - A - - - - - - - - - B - - - - - +
+
+ + + + + + + + -
+ + + + + + -
-
- -
-
select
-
Q
- -
start
-
W
-
-
-
A
-
A
- -
B
-
S
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + + + + + + START + + + + + + + + + A + + + + + + + + + B + + + + + + + + + + + + + + +
-
+
@@ -202,19 +195,51 @@
-
-
+

-

-
-
-
- - + + +
+
+ + +
-
- + + +
+ +
+ + Keyboard shortcuts: +

+

+
Up
+
Up arrow
+ +
Down
+
Down arrow
+ +
Right
+
Right arrow
+ +
Left
+
Left arrow
+ +
A button
+
Delete/End key
+ +
B button
+
Insert/Home key
+ +
Select button
+
PageDown key
+ +
Start button
+
PageUp key
+ +
diff --git a/src/main.js b/src/main.js index ae193d4..3afc33f 100644 --- a/src/main.js +++ b/src/main.js @@ -22,7 +22,7 @@ const { } = rxjs.operators; const HZ = 6250000; -const romUrl = 'theloop.2.rom'; +const romUrl = 'gigatron.rom'; $(function() { $('[data-toggle="tooltip"]').tooltip(); @@ -64,10 +64,10 @@ $(function() { }); } - bindKeyToButton($('.gamepad-btn-a'), 'A'); - bindKeyToButton($('.gamepad-btn-b'), 'S'); - bindKeyToButton($('.gamepad-btn-start'), 'W'); - bindKeyToButton($('.gamepad-btn-select'), 'Q'); + bindKeyToButton($('.gamepad-btn-a'), 'Delete'); + bindKeyToButton($('.gamepad-btn-b'), 'Insert'); + bindKeyToButton($('.gamepad-btn-start'), 'PageUp'); + bindKeyToButton($('.gamepad-btn-select'), 'PageDown'); bindKeyToButton($('.gamepad-btn-up'), 'ArrowUp'); bindKeyToButton($('.gamepad-btn-down'), 'ArrowDown'); bindKeyToButton($('.gamepad-btn-left'), 'ArrowLeft'); @@ -136,12 +136,14 @@ $(function() { let vga = new Vga(vgaCanvas.get(0), cpu, { horizontal: { frontPorch: 16, + pulse: 96, backPorch: 48, visible: 640, }, vertical: { - frontPorch: 10, - backPorch: 34, + frontPorch: 6, + pulse: 8, + backPorch: 27, visible: 480, }, }); @@ -155,10 +157,10 @@ $(function() { down: ['ArrowDown'], left: ['ArrowLeft'], right: ['ArrowRight'], - select: ['Q', 'q'], - start: ['W', 'w'], - a: ['A', 'a'], - b: ['S', 's'], + select: ['PageDown'], + start: ['PageUp'], + a: ['Delete', 'Backspace', 'End'], + b: ['Insert', 'Home'], }); let loader = new Loader(cpu); diff --git a/src/vga.js b/src/vga.js index 1751c56..31cb4a7 100644 --- a/src/vga.js +++ b/src/vga.js @@ -21,10 +21,10 @@ export class Vga { this.pixels = this.imageData.data; this.cpu = cpu; this.row = 0; - this.minRow = options.vertical.backPorch; + this.minRow = options.vertical.backPorch + options.vertical.pulse; this.maxRow = this.minRow + options.vertical.visible; this.col = 0; - this.minCol = options.horizontal.backPorch; + this.minCol = options.horizontal.backPorch + options.horizontal.pulse; this.maxCol = this.minCol + options.horizontal.visible; this.pixel = 0; this.out = 0; @@ -46,7 +46,7 @@ export class Vga { let falling = this.out & ~out; if (falling & VSYNC) { - this.row = 0; + this.row = -1; // After 4 more CPU cycles HSYNC increments row to 0 this.pixel = 0; this.render(); } @@ -60,11 +60,6 @@ export class Vga { // if it follows immediately after it, so it got moved down here this.out = out; - if ((out & (VSYNC | HSYNC)) != (VSYNC | HSYNC)) { - // blanking interval - return; - } - if ((this.row >= this.minRow && this.row < this.maxRow) && (this.col >= this.minCol && this.col < this.maxCol)) { let pixels = this.pixels;