@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/text.8bs
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// @8bitscript/pet/text — the PET's implementation of @8bitscript/text.
|
|
2
|
+
//
|
|
3
|
+
// Named by "8bitscript".exports["./text"] in this package's package.json,
|
|
4
|
+
// and what
|
|
5
|
+
//
|
|
6
|
+
// import { text } from "@8bitscript/text";
|
|
7
|
+
//
|
|
8
|
+
// resolves to when the build is for the PET. Built on the register
|
|
9
|
+
// @8bitscript/pet exports. Every machine's text.8bs exports this same
|
|
10
|
+
// namespace — the same names, the same shapes, ASCII codes, and a cell 0
|
|
11
|
+
// at the top-left corner of the screen — so a program that imports it from
|
|
12
|
+
// @8bitscript/text draws the same thing on every target.
|
|
13
|
+
import { viaPeripheralControl } from "./index.8bs";
|
|
14
|
+
import { Video } from "./geometry.8bs";
|
|
15
|
+
|
|
16
|
+
// PET screen memory is at $8000 on every model: 1000 cells for the 40x25
|
|
17
|
+
// display of the 2001/3xxx/4xxx machines, 2000 for the 80x25 of the 8032 —
|
|
18
|
+
// which is the build's `--profile` to say (PET_PROFILES in
|
|
19
|
+
// packages/backend-6502), and reaches this file as ./geometry.8bs's
|
|
20
|
+
// `Video` namespace, whose 8032 version the profile selects. There is no
|
|
21
|
+
// colour RAM to go with it — a character's screen code alone decides how it
|
|
22
|
+
// looks — so `putColor` exists only so portable code written against every
|
|
23
|
+
// machine's `text` namespace still compiles; it does nothing, the same way
|
|
24
|
+
// this package's `screen.setColors()` does nothing.
|
|
25
|
+
//
|
|
26
|
+
// `text.putChar` takes ASCII on every machine — space (32), '0'-'9'
|
|
27
|
+
// (48-57), 'A'-'Z' (65-90) and the punctuation `! , - . : ?` — upper case
|
|
28
|
+
// only: that is the portable set, the characters every target's character
|
|
29
|
+
// set has (the NES ships its own font, and that is what it has). The PET
|
|
30
|
+
// wants screen codes: codes 32-63 are those same ASCII values, and 'A'-'Z'
|
|
31
|
+
// are 1-26, so asciiToScreenCode() moves 64-95 down by 64 and leaves 32-63
|
|
32
|
+
// alone. Whether 1-26 then LOOK like 'A'-'Z' or 'a'-'z' is the character
|
|
33
|
+
// set's choice, and on the PET that is the editor ROM's boot state: the
|
|
34
|
+
// graphics-keyboard 3032/4032 boot in the upper-case ("graphics") set, the
|
|
35
|
+
// business-keyboard 8032 (xpet's default model) in the lower-case ("text")
|
|
36
|
+
// set. So putChar() selects the graphics set itself, every call, through
|
|
37
|
+
// the VIA's peripheral control register (see @8bitscript/pet): one register
|
|
38
|
+
// write, and "TICK" reads as TICK here on every model the way it does on
|
|
39
|
+
// every other target. (LLVM-MOS's libc used to flip every Commodore machine
|
|
40
|
+
// to lower-case before main() with a hidden CHR$(14) through the KERNAL;
|
|
41
|
+
// packages/backend-6502's commodoreCharsetGuard() keeps that out of the
|
|
42
|
+
// link. See packages/pet/AGENTS.md.)
|
|
43
|
+
namespace CharacterSet {
|
|
44
|
+
const GRAPHICS: utinyint = 0x0C;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function asciiToScreenCode(code: utinyint): utinyint {
|
|
48
|
+
if (code >= 64 && code < 96) {
|
|
49
|
+
return code - 64;
|
|
50
|
+
}
|
|
51
|
+
return code;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---- print: strings and number fields ------------------------------------
|
|
55
|
+
//
|
|
56
|
+
// `text.print(cell, s)` writes a string's characters into consecutive
|
|
57
|
+
// cells from `cell`, and `text.printNumber(cell, value, width)` writes
|
|
58
|
+
// `value` as exactly `width` decimal digits, zero-padded and right-aligned,
|
|
59
|
+
// so a field on a HUD never shifts columns. Both draw in the current colour —
|
|
60
|
+
// white until `text.setColor(TextColor.CYAN)` changes it, and that one
|
|
61
|
+
// call then colours everything printed after it, on the machines that have
|
|
62
|
+
// per-cell colour. They are also the two
|
|
63
|
+
// functions the compiler's template layout targets: `text.print(0,
|
|
64
|
+
// \`TICK ${ticks:1}\`)` is laid out at compile time into these same calls
|
|
65
|
+
// (see packages/compiler/src/ir).
|
|
66
|
+
//
|
|
67
|
+
// Under them: `place()` puts one character at one cell in the current
|
|
68
|
+
// colour, and is all a run of text costs per character, with `prepare()` selecting the
|
|
69
|
+
// graphics set once for the run.
|
|
70
|
+
// `text.putChar` and `text.putColor` stay the one-cell pokes a caller can
|
|
71
|
+
// build anything from.
|
|
72
|
+
let currentColor: utinyint = 1; // white, until text.setColor() says otherwise
|
|
73
|
+
let currentReverse: bool = false; // until text.setReverse() says otherwise
|
|
74
|
+
|
|
75
|
+
// Reverse video is bit 7 of the screen code: 128-255 are the ROM's
|
|
76
|
+
// inverted copies of 0-127. A reverse space is a solid block.
|
|
77
|
+
function toScreen(code: utinyint): utinyint {
|
|
78
|
+
let screen: utinyint = asciiToScreenCode(code);
|
|
79
|
+
if (currentReverse) {
|
|
80
|
+
screen = screen + 128;
|
|
81
|
+
}
|
|
82
|
+
return screen;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// What a run of text needs once, before its first character.
|
|
86
|
+
function prepare(): void {
|
|
87
|
+
viaPeripheralControl = CharacterSet.GRAPHICS;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function place(cell: usmallint, code: utinyint): void {
|
|
91
|
+
memory.write(0x8000 + cell, toScreen(code));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---- digits -------------------------------------------------------------
|
|
95
|
+
//
|
|
96
|
+
// A number is written one place at a time, high to low, by subtracting the
|
|
97
|
+
// place value until it no longer fits: the 6502 has no divide instruction,
|
|
98
|
+
// and `value / 10` would link a 250-byte routine to do it. Places above the
|
|
99
|
+
// field are still taken off, so a field narrower than its number shows the
|
|
100
|
+
// low digits; places the number does not reach print as zeros.
|
|
101
|
+
const DIGIT_PLACES: array<usmallint, 5> = [10000, 1000, 100, 10, 1];
|
|
102
|
+
|
|
103
|
+
export namespace text {
|
|
104
|
+
const CELL_COUNT: usmallint = Video.CELL_COUNT; // 40 x 25, or 80 x 25 on the 8032 profile
|
|
105
|
+
const COLUMNS: utinyint = Video.COLUMNS; // cells per row, so cell = y * text.COLUMNS + x
|
|
106
|
+
|
|
107
|
+
function putChar(cell: usmallint, code: utinyint): void {
|
|
108
|
+
prepare();
|
|
109
|
+
memory.write(0x8000 + cell, toScreen(code));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function putColor(cell: usmallint, color: utinyint): void {
|
|
113
|
+
// Deliberately empty — the PET has no colour RAM.
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function setColor(color: utinyint): void {
|
|
117
|
+
currentColor = color;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function setReverse(on: bool): void {
|
|
121
|
+
currentReverse = on;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function print(cell: usmallint, s: string): void {
|
|
125
|
+
prepare();
|
|
126
|
+
for (let i: utinyint = 0; i < s.length; i++) {
|
|
127
|
+
place(cell + i, s[i]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function printNumber(cell: usmallint, value: usmallint, width: utinyint): void {
|
|
132
|
+
prepare();
|
|
133
|
+
let k: utinyint = width;
|
|
134
|
+
if (k < DIGIT_PLACES.length) {
|
|
135
|
+
k = DIGIT_PLACES.length;
|
|
136
|
+
}
|
|
137
|
+
while (k > 0) {
|
|
138
|
+
let digit: utinyint = 48; // '0'
|
|
139
|
+
if (k <= DIGIT_PLACES.length) {
|
|
140
|
+
while (value >= DIGIT_PLACES[DIGIT_PLACES.length - k]) {
|
|
141
|
+
value = value - DIGIT_PLACES[DIGIT_PLACES.length - k];
|
|
142
|
+
digit++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (k <= width) {
|
|
146
|
+
place(cell + width - k, digit);
|
|
147
|
+
}
|
|
148
|
+
k--;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// The colours `text.setColor()` takes. The PET has no colour RAM, so the
|
|
154
|
+
// names are here for a program that compiles everywhere and their values
|
|
155
|
+
// go nowhere.
|
|
156
|
+
export namespace TextColor {
|
|
157
|
+
const BLACK: utinyint = 0;
|
|
158
|
+
const WHITE: utinyint = 1;
|
|
159
|
+
const RED: utinyint = 2;
|
|
160
|
+
const CYAN: utinyint = 3;
|
|
161
|
+
const PURPLE: utinyint = 4;
|
|
162
|
+
const GREEN: utinyint = 5;
|
|
163
|
+
const BLUE: utinyint = 6;
|
|
164
|
+
const YELLOW: utinyint = 7;
|
|
165
|
+
}
|