@8bitscript/pet 0.1.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/LICENSE +21 -0
- package/package.json +244 -0
- package/src/blocks.8bs +288 -0
- package/src/geometry.8bs +25 -0
- package/src/geometry.pet.8032.8bs +15 -0
- package/src/index.8bs +48 -0
- package/src/input.8bs +135 -0
- package/src/keyboard.8bs +72 -0
- package/src/keys.8bs +120 -0
- package/src/keys.pet.8032.8bs +100 -0
- package/src/pointer.8bs +39 -0
- package/src/screen.8bs +94 -0
- package/src/text.8bs +165 -0
package/src/input.8bs
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// @8bitscript/pet/input — the PET behind @8bitscript/input.
|
|
2
|
+
//
|
|
3
|
+
// Named by "8bitscript".exports["./input"] in this package's package.json,
|
|
4
|
+
// and by "8bitscript".entry.pet in @8bitscript/input's, so a portable
|
|
5
|
+
// program writes
|
|
6
|
+
//
|
|
7
|
+
// import { input } from "@8bitscript/input";
|
|
8
|
+
//
|
|
9
|
+
// and gets this file on a PET. The matrix itself is ./keyboard.8bs with
|
|
10
|
+
// ./keys.8bs, which stay available to a program that wants a key this file
|
|
11
|
+
// does not map.
|
|
12
|
+
//
|
|
13
|
+
// ---- a keyboard and nothing else -----------------------------------------
|
|
14
|
+
//
|
|
15
|
+
// The PET is the machine with the least to offer this surface, and saying
|
|
16
|
+
// so plainly is the job. There are **no control ports** — no joystick, no
|
|
17
|
+
// paddles, no mouse, nothing to plug in — because the machine predates the
|
|
18
|
+
// idea. `pointer()` is a constant false, and so every `if
|
|
19
|
+
// (input.pointer())` in a component folds away to nothing here.
|
|
20
|
+
//
|
|
21
|
+
// What there is is a keyboard, and it has the two cursor keys the
|
|
22
|
+
// Commodore machines all have rather than four: `CURSOR_RIGHT` and
|
|
23
|
+
// `CURSOR_DOWN`, each reversed by SHIFT. So left is SHIFT plus the right
|
|
24
|
+
// key. That is what the machine has, and this file reports it rather than
|
|
25
|
+
// pretending otherwise.
|
|
26
|
+
//
|
|
27
|
+
// - **left / right / up / down** — the two cursor keys, with SHIFT.
|
|
28
|
+
// - **confirm** — RETURN.
|
|
29
|
+
// - **cancel** — RUN/STOP.
|
|
30
|
+
//
|
|
31
|
+
// ---- edges, not levels ---------------------------------------------------
|
|
32
|
+
//
|
|
33
|
+
// Every answer is **edge-triggered**: true on the one frame the press
|
|
34
|
+
// begins, false while it is held. `poll()` must be called exactly once a
|
|
35
|
+
// frame, right after waitFrame().
|
|
36
|
+
//
|
|
37
|
+
// One thing worth knowing about this keyboard specifically: the PET's
|
|
38
|
+
// matrix is read a row at a time and has the same ghosting every
|
|
39
|
+
// undiodedmatrix does — three keys down can read as four. Two keys are
|
|
40
|
+
// always safe, and SHIFT plus a cursor key is two.
|
|
41
|
+
import { keyboard } from "./keyboard.8bs";
|
|
42
|
+
import { Key } from "./keys.8bs";
|
|
43
|
+
|
|
44
|
+
namespace Edge {
|
|
45
|
+
const LEFT: utinyint = 1;
|
|
46
|
+
const RIGHT: utinyint = 2;
|
|
47
|
+
const UP: utinyint = 4;
|
|
48
|
+
const DOWN: utinyint = 8;
|
|
49
|
+
const CONFIRM: utinyint = 16;
|
|
50
|
+
const CANCEL: utinyint = 32;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let held: utinyint = 0;
|
|
54
|
+
let before: utinyint = 0;
|
|
55
|
+
let began: utinyint = 0;
|
|
56
|
+
|
|
57
|
+
export namespace input {
|
|
58
|
+
|
|
59
|
+
// Nothing to set up: there is no pointer to give a ceiling to and the
|
|
60
|
+
// matrix needs no configuration. Here so a program written for nine
|
|
61
|
+
// machines calls the same thing on all of them.
|
|
62
|
+
function begin(): void {
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Snapshot the matrix and work out what began this frame. Once a
|
|
66
|
+
// frame, right after waitFrame().
|
|
67
|
+
function poll(): void {
|
|
68
|
+
keyboard.scan();
|
|
69
|
+
|
|
70
|
+
before = held;
|
|
71
|
+
held = 0;
|
|
72
|
+
|
|
73
|
+
let shifted: bool = keyboard.pressed(Key.SHIFT_LEFT) || keyboard.pressed(Key.SHIFT_RIGHT);
|
|
74
|
+
if (keyboard.pressed(Key.CURSOR_RIGHT)) {
|
|
75
|
+
if (shifted) {
|
|
76
|
+
held = held | Edge.LEFT;
|
|
77
|
+
} else {
|
|
78
|
+
held = held | Edge.RIGHT;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (keyboard.pressed(Key.CURSOR_DOWN)) {
|
|
82
|
+
if (shifted) {
|
|
83
|
+
held = held | Edge.UP;
|
|
84
|
+
} else {
|
|
85
|
+
held = held | Edge.DOWN;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (keyboard.pressed(Key.RETURN)) {
|
|
89
|
+
held = held | Edge.CONFIRM;
|
|
90
|
+
}
|
|
91
|
+
if (keyboard.pressed(Key.STOP)) {
|
|
92
|
+
held = held | Edge.CANCEL;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
began = held & (before ^ 0xFF);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function left(): bool {
|
|
99
|
+
return (began & Edge.LEFT) != 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function right(): bool {
|
|
103
|
+
return (began & Edge.RIGHT) != 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function up(): bool {
|
|
107
|
+
return (began & Edge.UP) != 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function down(): bool {
|
|
111
|
+
return (began & Edge.DOWN) != 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function confirm(): bool {
|
|
115
|
+
return (began & Edge.CONFIRM) != 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function cancel(): bool {
|
|
119
|
+
return (began & Edge.CANCEL) != 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// No control ports on this machine, so no pointer — a constant, not a
|
|
123
|
+
// measurement.
|
|
124
|
+
function pointer(): bool {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function pointerCell(): usmallint {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pointerButton(): bool {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
package/src/keyboard.8bs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @8bitscript/pet/keyboard — the PET's keyboard matrix, one snapshot a frame.
|
|
2
|
+
//
|
|
3
|
+
// Named by "8bitscript".exports["./keyboard"] in this package's package.json:
|
|
4
|
+
//
|
|
5
|
+
// import { keyboard } from "@8bitscript/pet/keyboard";
|
|
6
|
+
// import { Key } from "@8bitscript/pet/keys";
|
|
7
|
+
//
|
|
8
|
+
// This is hardware-level, PET-only surface — the layer the portable input
|
|
9
|
+
// capability is built on: `@8bitscript/pet/input` reads this file and
|
|
10
|
+
// @8bitscript/input resolves to that, the way ./text.8bs is the layer
|
|
11
|
+
// under @8bitscript/text. Importing it makes a program PET-specific.
|
|
12
|
+
//
|
|
13
|
+
// ---- how the matrix works -----------------------------------------------
|
|
14
|
+
//
|
|
15
|
+
// Ten rows by eight columns, no diodes. Writing a row number 0-9 to PIA1
|
|
16
|
+
// port A ($E810) selects that row; PIA1 port B ($E812) then reads its eight
|
|
17
|
+
// keys, a 0 bit where a key is down. A program that calls waitFrame()
|
|
18
|
+
// anywhere — one scanning once a frame does — runs with interrupts off from
|
|
19
|
+
// start-up (FRAME_SYNC.pet's `sei`, in the frame prologue the backend
|
|
20
|
+
// emits for it), so the KERNAL's own scan is not running against this one.
|
|
21
|
+
// A program with no waitFrame() at all still has the KERNAL IRQ alive and
|
|
22
|
+
// selecting rows of its own sixty times a second; scan() there would read
|
|
23
|
+
// a row it did not select. This package keeps no key buffer and decodes no
|
|
24
|
+
// PETSCII; it answers "is this key down now", which is what a game loop asks.
|
|
25
|
+
//
|
|
26
|
+
// Reading $E812 has a side effect: it clears the retrace flag the frame
|
|
27
|
+
// runtime waits on (see `pia1PortB` in ./index.8bs). So the matrix is read
|
|
28
|
+
// exactly once a frame, all ten rows into `state`, by `keyboard.scan()`
|
|
29
|
+
// — called right after waitFrame() returns, when the edge was just
|
|
30
|
+
// consumed and the next is a whole frame away — and every question after
|
|
31
|
+
// that reads the snapshot, never the port. Ten writes and ten reads, about
|
|
32
|
+
// 200 cycles of a 20000-cycle frame.
|
|
33
|
+
//
|
|
34
|
+
// Ghosting: three keys down can read as four (any three corners of a
|
|
35
|
+
// rectangle in the matrix light the fourth). Two keys are always safe.
|
|
36
|
+
import { pia1PortA, pia1PortB } from "./index.8bs";
|
|
37
|
+
|
|
38
|
+
// The last scan, one byte per row, a 1 bit where a key is down (the port's
|
|
39
|
+
// active-low columns inverted once, here, so nothing else has to think
|
|
40
|
+
// about it). All zero until the first scan.
|
|
41
|
+
let state: array<utinyint, 10>;
|
|
42
|
+
|
|
43
|
+
// Column masks, indexed rather than shifted: `1 << n` with a variable n is
|
|
44
|
+
// a loop on the 6502, a table lookup is one indexed load.
|
|
45
|
+
const COLUMN_BIT: array<utinyint, 8> = [1, 2, 4, 8, 16, 32, 64, 128];
|
|
46
|
+
|
|
47
|
+
export namespace keyboard {
|
|
48
|
+
const ROWS: utinyint = 10;
|
|
49
|
+
const COLUMNS: utinyint = 8;
|
|
50
|
+
|
|
51
|
+
// Read all ten rows into the snapshot. Once a frame, right after
|
|
52
|
+
// waitFrame().
|
|
53
|
+
function scan(): void {
|
|
54
|
+
for (let row: utinyint = 0; row < 10; row++) {
|
|
55
|
+
pia1PortA = row;
|
|
56
|
+
state[row] = pia1PortB ^ 0xFF;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Is this key down in the last scan? `key` is a `Key.*` value from
|
|
61
|
+
// @8bitscript/pet/keys — `row * 8 + column` in the profile's matrix.
|
|
62
|
+
function pressed(key: utinyint): bool {
|
|
63
|
+
return (state[key >> 3] & COLUMN_BIT[key & 7]) != 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The last scan's eight columns of one row, a 1 bit per key down:
|
|
67
|
+
// for code that wants a whole row at once (a matrix ghosting check,
|
|
68
|
+
// an "any key" test over all ten).
|
|
69
|
+
function row(index: utinyint): utinyint {
|
|
70
|
+
return state[index];
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/keys.8bs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// @8bitscript/pet/keys — the keyboard matrix, key by key: graphics keyboard.
|
|
2
|
+
//
|
|
3
|
+
// Named by "8bitscript".exports["./keys"] in this package's package.json, for
|
|
4
|
+
//
|
|
5
|
+
// import { keyboard } from "@8bitscript/pet/keyboard";
|
|
6
|
+
// import { Key } from "@8bitscript/pet/keys";
|
|
7
|
+
//
|
|
8
|
+
// if (keyboard.pressed(Key.SPACE)) { ... }
|
|
9
|
+
//
|
|
10
|
+
// Each value is `row * 8 + column` in PIA1's matrix (see ./keyboard.8bs),
|
|
11
|
+
// so a build for a profile reads that profile's table: this file is the
|
|
12
|
+
// **graphics** keyboard of the 2001, 3xxx and 4xxx (calculator-style
|
|
13
|
+
// keypad digits, the symbol row `! " # $ % & ' ( ) ←` above the letters,
|
|
14
|
+
// every character unshifted), and `keys.pet.8032.8bs` beside it is the
|
|
15
|
+
// **business** keyboard the 8032 has (typewriter layout: digits on the
|
|
16
|
+
// top row, a numeric keypad, TAB, ESC, RPT). The two matrices wire the
|
|
17
|
+
// same letters to different rows, which is exactly why the table is the
|
|
18
|
+
// profile's and a program names keys, never numbers.
|
|
19
|
+
//
|
|
20
|
+
// A key that exists on both keyboards has the same name in both files —
|
|
21
|
+
// letters, `DIGIT_n`, SPACE, RETURN, STOP, HOME, DELETE, the cursor keys,
|
|
22
|
+
// both shifts, REVERSE, the arrows and the shared punctuation — so a
|
|
23
|
+
// program that sticks to those builds for every PET. Names that exist
|
|
24
|
+
// only here (the symbol row: EXCLAMATION, HASH, ... EQUALS) or only on
|
|
25
|
+
// the business keyboard (KEYPAD_n, TAB, ESCAPE, REPEAT) are that
|
|
26
|
+
// keyboard's alone, and an 8032 build of a program using one stops at
|
|
27
|
+
// the import with "no member" — which is the right time to find out.
|
|
28
|
+
//
|
|
29
|
+
// Source: the matrix every PET reference gives for the graphics keyboard,
|
|
30
|
+
// checked here key for key against VICE's own positional map for it,
|
|
31
|
+
// /opt/homebrew/share/vice/PET/gtk3_grus_pos.vkm (each line there is
|
|
32
|
+
// `keysym row column shiftflag`); packages/compiler/test/pet-keys.test.mjs
|
|
33
|
+
// repeats that check when the file is present. Row 0 is `$E810 = 0`,
|
|
34
|
+
// column 0 is PB0. A row's missing columns (1,5 and so on) have no key.
|
|
35
|
+
|
|
36
|
+
export namespace Key {
|
|
37
|
+
// row 0
|
|
38
|
+
const EXCLAMATION: utinyint = 0;
|
|
39
|
+
const HASH: utinyint = 1;
|
|
40
|
+
const PERCENT: utinyint = 2;
|
|
41
|
+
const AMPERSAND: utinyint = 3;
|
|
42
|
+
const PAREN_LEFT: utinyint = 4;
|
|
43
|
+
const LEFT_ARROW: utinyint = 5; // the back-arrow key, top row right
|
|
44
|
+
const HOME: utinyint = 6; // CLR/HOME
|
|
45
|
+
const CURSOR_RIGHT: utinyint = 7; // shifted: cursor left
|
|
46
|
+
// row 1
|
|
47
|
+
const QUOTE: utinyint = 8;
|
|
48
|
+
const DOLLAR: utinyint = 9;
|
|
49
|
+
const APOSTROPHE: utinyint = 10;
|
|
50
|
+
const PAREN_RIGHT: utinyint = 11;
|
|
51
|
+
const BACKSLASH: utinyint = 12;
|
|
52
|
+
const CURSOR_DOWN: utinyint = 14; // shifted: cursor up
|
|
53
|
+
const DELETE: utinyint = 15; // INST/DEL
|
|
54
|
+
// row 2
|
|
55
|
+
const Q: utinyint = 16;
|
|
56
|
+
const E: utinyint = 17;
|
|
57
|
+
const T: utinyint = 18;
|
|
58
|
+
const U: utinyint = 19;
|
|
59
|
+
const O: utinyint = 20;
|
|
60
|
+
const UP_ARROW: utinyint = 21;
|
|
61
|
+
const DIGIT_7: utinyint = 22; // keypad
|
|
62
|
+
const DIGIT_9: utinyint = 23;
|
|
63
|
+
// row 3
|
|
64
|
+
const W: utinyint = 24;
|
|
65
|
+
const R: utinyint = 25;
|
|
66
|
+
const Y: utinyint = 26;
|
|
67
|
+
const I: utinyint = 27;
|
|
68
|
+
const P: utinyint = 28;
|
|
69
|
+
const DIGIT_8: utinyint = 30;
|
|
70
|
+
const SLASH: utinyint = 31;
|
|
71
|
+
// row 4
|
|
72
|
+
const A: utinyint = 32;
|
|
73
|
+
const D: utinyint = 33;
|
|
74
|
+
const G: utinyint = 34;
|
|
75
|
+
const J: utinyint = 35;
|
|
76
|
+
const L: utinyint = 36;
|
|
77
|
+
const DIGIT_4: utinyint = 38;
|
|
78
|
+
const DIGIT_6: utinyint = 39;
|
|
79
|
+
// row 5
|
|
80
|
+
const S: utinyint = 40;
|
|
81
|
+
const F: utinyint = 41;
|
|
82
|
+
const H: utinyint = 42;
|
|
83
|
+
const K: utinyint = 43;
|
|
84
|
+
const COLON: utinyint = 44;
|
|
85
|
+
const DIGIT_5: utinyint = 46;
|
|
86
|
+
const ASTERISK: utinyint = 47;
|
|
87
|
+
// row 6
|
|
88
|
+
const Z: utinyint = 48;
|
|
89
|
+
const C: utinyint = 49;
|
|
90
|
+
const B: utinyint = 50;
|
|
91
|
+
const M: utinyint = 51;
|
|
92
|
+
const SEMICOLON: utinyint = 52;
|
|
93
|
+
const RETURN: utinyint = 53;
|
|
94
|
+
const DIGIT_1: utinyint = 54;
|
|
95
|
+
const DIGIT_3: utinyint = 55;
|
|
96
|
+
// row 7
|
|
97
|
+
const X: utinyint = 56;
|
|
98
|
+
const V: utinyint = 57;
|
|
99
|
+
const N: utinyint = 58;
|
|
100
|
+
const COMMA: utinyint = 59;
|
|
101
|
+
const QUESTION: utinyint = 60;
|
|
102
|
+
const DIGIT_2: utinyint = 62;
|
|
103
|
+
const PLUS: utinyint = 63;
|
|
104
|
+
// row 8
|
|
105
|
+
const SHIFT_LEFT: utinyint = 64;
|
|
106
|
+
const AT: utinyint = 65;
|
|
107
|
+
const BRACKET_RIGHT: utinyint = 66;
|
|
108
|
+
const GREATER: utinyint = 68;
|
|
109
|
+
const SHIFT_RIGHT: utinyint = 69;
|
|
110
|
+
const DIGIT_0: utinyint = 70;
|
|
111
|
+
const MINUS: utinyint = 71;
|
|
112
|
+
// row 9
|
|
113
|
+
const REVERSE: utinyint = 72; // RVS
|
|
114
|
+
const BRACKET_LEFT: utinyint = 73;
|
|
115
|
+
const SPACE: utinyint = 74;
|
|
116
|
+
const LESS: utinyint = 75;
|
|
117
|
+
const STOP: utinyint = 76; // RUN/STOP
|
|
118
|
+
const PERIOD: utinyint = 78;
|
|
119
|
+
const EQUALS: utinyint = 79;
|
|
120
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// @8bitscript/pet/keys — the keyboard matrix, key by key: business keyboard.
|
|
2
|
+
//
|
|
3
|
+
// The 8032 profile's version of keys.8bs (see that file for how the two
|
|
4
|
+
// tables relate and which names both share). This is the **business**
|
|
5
|
+
// keyboard of the 8032 (and the 4032B/3032B, which are not profiles):
|
|
6
|
+
// typewriter layout with the digits on the top row, a numeric keypad of
|
|
7
|
+
// its own, TAB, ESC and RPT, and the symbols `! " # ...` reached with
|
|
8
|
+
// SHIFT rather than as keys of their own — so this table has KEYPAD_n
|
|
9
|
+
// and no EXCLAMATION. Same encoding, `row * 8 + column` in PIA1's matrix.
|
|
10
|
+
//
|
|
11
|
+
// Source: the business matrix the PET references give, checked key for
|
|
12
|
+
// key against VICE's positional map for the 8032, /opt/homebrew/share/
|
|
13
|
+
// vice/PET/gtk3_buuk_pos.vkm — the UK business layout, which is the one
|
|
14
|
+
// xpet's 8032 model loads. The US business keyboard is wired to the same
|
|
15
|
+
// matrix in every reference consulted, but has not been checked here.
|
|
16
|
+
|
|
17
|
+
export namespace Key {
|
|
18
|
+
// row 0
|
|
19
|
+
const DIGIT_2: utinyint = 0;
|
|
20
|
+
const DIGIT_5: utinyint = 1;
|
|
21
|
+
const DIGIT_8: utinyint = 2;
|
|
22
|
+
const MINUS: utinyint = 3; // shifted: =
|
|
23
|
+
const KEYPAD_8: utinyint = 4;
|
|
24
|
+
const CURSOR_RIGHT: utinyint = 5; // shifted: cursor left
|
|
25
|
+
// row 1
|
|
26
|
+
const DIGIT_1: utinyint = 8;
|
|
27
|
+
const DIGIT_4: utinyint = 9;
|
|
28
|
+
const DIGIT_7: utinyint = 10;
|
|
29
|
+
const DIGIT_0: utinyint = 11;
|
|
30
|
+
const KEYPAD_7: utinyint = 12;
|
|
31
|
+
const UP_ARROW: utinyint = 13;
|
|
32
|
+
const KEYPAD_9: utinyint = 15;
|
|
33
|
+
// row 2
|
|
34
|
+
const ESCAPE: utinyint = 16;
|
|
35
|
+
const S: utinyint = 17;
|
|
36
|
+
const F: utinyint = 18;
|
|
37
|
+
const H: utinyint = 19;
|
|
38
|
+
const BRACKET_RIGHT: utinyint = 20;
|
|
39
|
+
const K: utinyint = 21;
|
|
40
|
+
const SEMICOLON: utinyint = 22; // shifted: +
|
|
41
|
+
const KEYPAD_5: utinyint = 23;
|
|
42
|
+
// row 3
|
|
43
|
+
const A: utinyint = 24;
|
|
44
|
+
const D: utinyint = 25;
|
|
45
|
+
const G: utinyint = 26;
|
|
46
|
+
const J: utinyint = 27;
|
|
47
|
+
const RETURN: utinyint = 28;
|
|
48
|
+
const L: utinyint = 29;
|
|
49
|
+
const AT: utinyint = 30;
|
|
50
|
+
const KEYPAD_6: utinyint = 31;
|
|
51
|
+
// row 4
|
|
52
|
+
const TAB: utinyint = 32;
|
|
53
|
+
const W: utinyint = 33;
|
|
54
|
+
const R: utinyint = 34;
|
|
55
|
+
const Y: utinyint = 35;
|
|
56
|
+
const BACKSLASH: utinyint = 36;
|
|
57
|
+
const I: utinyint = 37;
|
|
58
|
+
const P: utinyint = 38;
|
|
59
|
+
const DELETE: utinyint = 39; // INST/DEL
|
|
60
|
+
// row 5
|
|
61
|
+
const Q: utinyint = 40;
|
|
62
|
+
const E: utinyint = 41;
|
|
63
|
+
const T: utinyint = 42;
|
|
64
|
+
const U: utinyint = 43;
|
|
65
|
+
const CURSOR_DOWN: utinyint = 44; // shifted: cursor up
|
|
66
|
+
const O: utinyint = 45;
|
|
67
|
+
const BRACKET_LEFT: utinyint = 46;
|
|
68
|
+
const KEYPAD_4: utinyint = 47;
|
|
69
|
+
// row 6
|
|
70
|
+
const SHIFT_LEFT: utinyint = 48;
|
|
71
|
+
const C: utinyint = 49;
|
|
72
|
+
const B: utinyint = 50;
|
|
73
|
+
const PERIOD: utinyint = 51;
|
|
74
|
+
const KEYPAD_PERIOD: utinyint = 52;
|
|
75
|
+
const SHIFT_RIGHT: utinyint = 54;
|
|
76
|
+
const KEYPAD_3: utinyint = 55;
|
|
77
|
+
// row 7
|
|
78
|
+
const Z: utinyint = 56;
|
|
79
|
+
const V: utinyint = 57;
|
|
80
|
+
const N: utinyint = 58;
|
|
81
|
+
const COMMA: utinyint = 59;
|
|
82
|
+
const KEYPAD_0: utinyint = 60;
|
|
83
|
+
const REPEAT: utinyint = 62; // RPT
|
|
84
|
+
const KEYPAD_2: utinyint = 63;
|
|
85
|
+
// row 8
|
|
86
|
+
const REVERSE: utinyint = 64; // RVS
|
|
87
|
+
const X: utinyint = 65;
|
|
88
|
+
const SPACE: utinyint = 66;
|
|
89
|
+
const M: utinyint = 67;
|
|
90
|
+
const HOME: utinyint = 68; // CLR/HOME
|
|
91
|
+
const SLASH: utinyint = 70; // shifted: ?
|
|
92
|
+
const KEYPAD_1: utinyint = 71;
|
|
93
|
+
// row 9
|
|
94
|
+
const LEFT_ARROW: utinyint = 72; // the back-arrow key, top row left
|
|
95
|
+
const DIGIT_3: utinyint = 73;
|
|
96
|
+
const DIGIT_6: utinyint = 74;
|
|
97
|
+
const DIGIT_9: utinyint = 75;
|
|
98
|
+
const STOP: utinyint = 76; // RUN/STOP
|
|
99
|
+
const COLON: utinyint = 77; // shifted: *
|
|
100
|
+
}
|
package/src/pointer.8bs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @8bitscript/pet/pointer — the PET behind @8bitscript/pointer, and the
|
|
2
|
+
// one machine of the nine where "no cursor" is the final answer rather
|
|
3
|
+
// than a gap.
|
|
4
|
+
//
|
|
5
|
+
// Every call here does nothing and `DRAWS` is false, because **the PET has
|
|
6
|
+
// no control ports at all** — no mouse, no joystick, no paddles, nothing to
|
|
7
|
+
// point with — which is why @8bitscript/pet/input answers `pointer()`
|
|
8
|
+
// false too. There is nothing to draw an arrow for, and a machine with
|
|
9
|
+
// nothing to point cannot be given a pointer by writing more code.
|
|
10
|
+
//
|
|
11
|
+
// It still has to exist: Studio and every other portable program links
|
|
12
|
+
// @8bitscript/pointer, so a missing entry is not a program without a
|
|
13
|
+
// cursor, it is a program that does not build for the PET.
|
|
14
|
+
//
|
|
15
|
+
// It costs the PET nothing. `DRAWS` is a const and every function is
|
|
16
|
+
// empty, so a program's `if (pointer.DRAWS)` folds away and LLVM deletes
|
|
17
|
+
// the calls — the same zero the X16's input layer measures at (see
|
|
18
|
+
// packages/input/AGENTS.md).
|
|
19
|
+
//
|
|
20
|
+
// Were a pointer ever wanted here it would need hardware the machine does
|
|
21
|
+
// not have and this repository has no driver for: the light pen on the
|
|
22
|
+
// 8296's user port, or a KoalaPad. Both would be a new hardware option in
|
|
23
|
+
// packages/pet's catalog first, and neither would be a mouse.
|
|
24
|
+
export namespace pointer {
|
|
25
|
+
|
|
26
|
+
const DRAWS: bool = false;
|
|
27
|
+
|
|
28
|
+
function begin(): void {
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function setColor(color: utinyint): void {
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function update(): void {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function hide(): void {
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/screen.8bs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @8bitscript/pet/screen — the PET's implementation of @8bitscript/screen.
|
|
2
|
+
//
|
|
3
|
+
// Named by "8bitscript".exports["./screen"] in this package's package.json,
|
|
4
|
+
// and what
|
|
5
|
+
//
|
|
6
|
+
// import { screen, BorderColor, BackgroundColor } from "@8bitscript/screen";
|
|
7
|
+
//
|
|
8
|
+
// resolves to when the build is for the PET: @8bitscript/screen's entry is
|
|
9
|
+
// keyed by machine and delegates here. Every machine's screen.8bs exports
|
|
10
|
+
// this same surface, which is what lets a program import it from
|
|
11
|
+
// @8bitscript/screen and never name the hardware.
|
|
12
|
+
//
|
|
13
|
+
// The PET is a monochrome machine — green or white phosphor, whatever the
|
|
14
|
+
// monitor is, with no colour hardware at all. `screen.setColors()` still
|
|
15
|
+
// exists and still links, because the point of the portable surface (see
|
|
16
|
+
// @8bitscript/vic20 and @8bitscript/c64's screen.8bs) is that a single
|
|
17
|
+
// portable program builds for every target without per-target branches in
|
|
18
|
+
// user code — but here it is deliberately inert: there is no register to
|
|
19
|
+
// write, so setColors() does nothing observable. A program that only ever
|
|
20
|
+
// sets colours through the portable surface still builds and runs correctly
|
|
21
|
+
// everywhere; it simply never shows a border or background tint on a PET,
|
|
22
|
+
// because a PET cannot show one.
|
|
23
|
+
//
|
|
24
|
+
// `blank()` is the one thing here that touches the screen, and how much
|
|
25
|
+
// screen there is depends on the model: 1000 cells on the 40-column PETs,
|
|
26
|
+
// 2000 on the 80-column 8032. That number comes from ./geometry.8bs, whose
|
|
27
|
+
// 8032 version is chosen by the build's profile — see that file.
|
|
28
|
+
import { Video } from "./geometry.8bs";
|
|
29
|
+
|
|
30
|
+
export namespace screen {
|
|
31
|
+
function setColors(border: u8, background: u8): void {
|
|
32
|
+
// Deliberately empty — see the file comment above.
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// `blank(border, background)`: every cell blank and both colours set —
|
|
36
|
+
// black when left off, or `BorderColor.KEEP` / `BackgroundColor.KEEP`
|
|
37
|
+
// to leave one as it is. `setBorder` and `setBackground` are one
|
|
38
|
+
// colour each, `setColors` the pair; `text.setColor` owns what the
|
|
39
|
+
// next print looks like.
|
|
40
|
+
function blank(border: utinyint = BorderColor.BLACK, background: utinyint = BackgroundColor.BLACK): void {
|
|
41
|
+
if (border != BorderColor.KEEP) {
|
|
42
|
+
screen.setBorder(border);
|
|
43
|
+
}
|
|
44
|
+
if (background != BackgroundColor.KEEP) {
|
|
45
|
+
screen.setBackground(background);
|
|
46
|
+
}
|
|
47
|
+
for (let cell: usmallint = 0; cell < Video.CELL_COUNT; cell++) {
|
|
48
|
+
memory.write(0x8000 + cell, 32); // 32: the space screen code
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function setBackground(background: u8): void {
|
|
53
|
+
// Deliberately empty — see the file comment above.
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function setBorder(border: u8): void {
|
|
57
|
+
// Deliberately empty — see the file comment above.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---- colour names ----------------------------------------------------------
|
|
62
|
+
//
|
|
63
|
+
// The same eight names every machine's screen.8bs exports, so a program that
|
|
64
|
+
// says `screen.setColors(BorderColor.BLUE, BackgroundColor.BLACK)` through
|
|
65
|
+
// @8bitscript/screen still links here. The values are the C64's numbering,
|
|
66
|
+
// for want of any of the PET's own — nothing reads them.
|
|
67
|
+
|
|
68
|
+
export namespace BorderColor {
|
|
69
|
+
const BLACK: utinyint = 0;
|
|
70
|
+
const WHITE: utinyint = 1;
|
|
71
|
+
const RED: utinyint = 2;
|
|
72
|
+
const CYAN: utinyint = 3;
|
|
73
|
+
const PURPLE: utinyint = 4;
|
|
74
|
+
const GREEN: utinyint = 5;
|
|
75
|
+
const BLUE: utinyint = 6;
|
|
76
|
+
const YELLOW: utinyint = 7;
|
|
77
|
+
// Not a colour: `blank(KEEP, ...)` leaves this one as it is. 255 is
|
|
78
|
+
// a value no register here takes.
|
|
79
|
+
const KEEP: utinyint = 255;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export namespace BackgroundColor {
|
|
83
|
+
const BLACK: utinyint = 0;
|
|
84
|
+
const WHITE: utinyint = 1;
|
|
85
|
+
const RED: utinyint = 2;
|
|
86
|
+
const CYAN: utinyint = 3;
|
|
87
|
+
const PURPLE: utinyint = 4;
|
|
88
|
+
const GREEN: utinyint = 5;
|
|
89
|
+
const BLUE: utinyint = 6;
|
|
90
|
+
const YELLOW: utinyint = 7;
|
|
91
|
+
// Not a colour: `blank(KEEP, ...)` leaves this one as it is. 255 is
|
|
92
|
+
// a value no register here takes.
|
|
93
|
+
const KEEP: utinyint = 255;
|
|
94
|
+
}
|