@henols/c64-re-tools 2.0.0-rc.17 → 2.0.0-rc.18
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 +1 -1
- package/skills/c64-docs/SKILL.md +7 -1
- package/skills/c64-docs/references/style.md +1 -1
- package/skills/c64-docs/scripts/lib/reference.ts +6 -6
- package/skills/c64-knowledge/SKILL.md +1 -0
- package/skills/c64-reverse-engineering/SKILL.md +1 -0
- package/skills/c64-source/scripts/lib/source.ts +1 -1
- package/src/graphics.ts +17 -7
- package/src/knowledge/schema.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henols/c64-re-tools",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.18",
|
|
4
4
|
"description": "Agent-friendly Commodore 64 engineering toolkit for reverse engineering, debugging, development and testing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Henrik Olsson",
|
package/skills/c64-docs/SKILL.md
CHANGED
|
@@ -82,7 +82,13 @@ node <skill>/scripts/link.ts docs --source src --check
|
|
|
82
82
|
- `tables.md`: each table of addresses, jumps and words, entry by entry, with its target. An RTS table holds each target minus 1. The table gives the target.
|
|
83
83
|
- `memory-map.md`: each region in address order.
|
|
84
84
|
- `symbols.md`: each name in alphabetical order.
|
|
85
|
-
- `graphics.md`: each `sprite`, `bitmap` and `screen` region. A sprite or a
|
|
85
|
+
- `graphics.md`: each `sprite`, `bitmap`, `charset` and `screen` region. A sprite, a bitmap or a charset shows in hires and in multicolour, because the knowledge has no graphics mode. A charset shows 16 characters in each row. A screen shows as text.
|
|
86
|
+
|
|
87
|
+
### Images and assets
|
|
88
|
+
|
|
89
|
+
- The reference draws each sprite, bitmap and charset region, and it decodes each table. To get the image of a graphic or the entries of a table, classify its region with c64-knowledge. Do not write a script that draws or decodes them.
|
|
90
|
+
- A script of the project decodes only a format of the program, for example a packed map or a music stream. Put the script in `tools/` of the project. Its output files are the assets, for example in `assets/rooms/`.
|
|
91
|
+
- When the code of the program makes the data, run that code in VICE with a run of c64-testing. Observe the memory, or capture the screen. Do not write a 6502 emulator in the script.
|
|
86
92
|
|
|
87
93
|
In the call graph, a solid arrow is a call, a thick arrow is a table entry, and a dotted arrow is a jump. A table entry goes from each routine that reads the table to each target in code. A table of data addresses gives no arrow.
|
|
88
94
|
|
|
@@ -27,7 +27,7 @@ These rules apply to each page that you write. `link.ts` checks the rules that a
|
|
|
27
27
|
- Use mermaid for the diagrams: `flowchart` for calls and data flow, `stateDiagram-v2` for state machines. Write the name and the address in each node: `"draw_room $09BC"`.
|
|
28
28
|
- A diagram gets no links. Give the links in a table or a list under the diagram.
|
|
29
29
|
- Keep a diagram small: at most about 20 nodes. Make two diagrams before one diagram that a reader cannot read.
|
|
30
|
-
- Embed an image only from the project. It can be an
|
|
30
|
+
- Embed an image only from the project. It can be an image of the reference (`reference/images/`) or a PNG that a skill made. It can also be an asset that a script of the project made from a format of the program, for example `../assets/rooms/room00.png`. `link.ts` checks that each image exists.
|
|
31
31
|
- Give each image one line of text that tells what it shows.
|
|
32
32
|
|
|
33
33
|
## Text
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { posix } from "node:path";
|
|
7
7
|
|
|
8
8
|
import { formatC64Address, POINTER_TABLE_TYPES, pointerTableEntries, type PointerTableType } from "#src/c64.ts";
|
|
9
|
-
import { bitmapFrame, encodePng, PATTERN_PALETTES, scaleFrame, screenText, spriteSheet, type GraphicsMode } from "#src/graphics.ts";
|
|
9
|
+
import { bitmapFrame, charsetFrame, encodePng, PATTERN_PALETTES, scaleFrame, screenText, spriteSheet, type GraphicsMode } from "#src/graphics.ts";
|
|
10
10
|
import { mermaidGraph, ownerOf, routineEdges, routineHeads, type GraphReference, type RoutineHead } from "#src/knowledge/graph.ts";
|
|
11
11
|
import type { CommentRow, ModuleRow, ReferenceRow, RegionRow, SymbolRow } from "#src/knowledge/read.ts";
|
|
12
12
|
import type { RegionType } from "#src/knowledge/schema.ts";
|
|
@@ -37,7 +37,7 @@ export interface ReferenceFile {
|
|
|
37
37
|
/** Files that the source map lists but that hold no code of the program. */
|
|
38
38
|
const NOT_PIECES = new Set(["main.asm", "constants.asm", "macros.asm", "loader.asm"]);
|
|
39
39
|
const TABLE_TYPES: readonly RegionType[] = [...POINTER_TABLE_TYPES, "jumps", "words"];
|
|
40
|
-
const GRAPHICS_TYPES: readonly RegionType[] = ["sprite", "bitmap", "screen"];
|
|
40
|
+
const GRAPHICS_TYPES: readonly RegionType[] = ["sprite", "bitmap", "charset", "screen"];
|
|
41
41
|
/** A list longer than this ends with the number of the rest. */
|
|
42
42
|
const MAX_LISTED = 40;
|
|
43
43
|
/** A call graph with more edges is not readable: then it shows the calls only, and at most this many. */
|
|
@@ -327,7 +327,7 @@ export function buildReference(input: ReferenceInput): { files: ReferenceFile[];
|
|
|
327
327
|
const lines = [
|
|
328
328
|
"# Graphics",
|
|
329
329
|
"",
|
|
330
|
-
`Each sprite, bitmap and screen region of the knowledge. The knowledge gives no display mode and no colours, so a sprite or
|
|
330
|
+
`Each sprite, bitmap, charset and screen region of the knowledge. The knowledge gives no display mode and no colours, so a sprite, bitmap or charset shows in hires and in multicolour, in pattern colours: black is pixel value 0, white is 1 in hires; in multicolour light grey is 1, grey is 2 and yellow is 3. Index: [the reference](${rel(page, posix.join(out, "index.md"))}).`,
|
|
331
331
|
];
|
|
332
332
|
if (input.image === undefined && graphics.length > 0) lines.push("", "Generate the reference with --image to show the graphics.");
|
|
333
333
|
for (const region of graphics) {
|
|
@@ -343,9 +343,9 @@ export function buildReference(input: ReferenceInput): { files: ReferenceFile[];
|
|
|
343
343
|
continue;
|
|
344
344
|
}
|
|
345
345
|
for (const mode of ["hires", "multicolour"] as GraphicsMode[]) {
|
|
346
|
-
// A sheet of sprites, or a bitmap of at most 8 rows of cells, is small: each pixel shows as 2 x 2.
|
|
347
|
-
const raw = region.type === "sprite" ? spriteSheet(bytes, mode, region.element === 63 ? 63 : 64) : bitmapFrame(bytes, mode);
|
|
348
|
-
const frame = region.type === "sprite" || raw.height <= 64 ? scaleFrame(raw, 2) : raw;
|
|
346
|
+
// A sheet of sprites, a charset, or a bitmap of at most 8 rows of cells, is small: each pixel shows as 2 x 2.
|
|
347
|
+
const raw = region.type === "sprite" ? spriteSheet(bytes, mode, region.element === 63 ? 63 : 64) : region.type === "charset" ? charsetFrame(bytes, mode) : bitmapFrame(bytes, mode);
|
|
348
|
+
const frame = region.type === "sprite" || region.type === "charset" || raw.height <= 64 ? scaleFrame(raw, 2) : raw;
|
|
349
349
|
const image = posix.join(out, "images", `${anchor}-${mode}.png`);
|
|
350
350
|
files.push({ path: image, bytes: encodePng(frame, PATTERN_PALETTES[mode]) });
|
|
351
351
|
lines.push("", `})`);
|
|
@@ -75,6 +75,7 @@ Region types:
|
|
|
75
75
|
|---|---|
|
|
76
76
|
| `code` | Instructions. |
|
|
77
77
|
| `bytes`, `words`, `petscii`, `screen`, `bitmap`, `sprite` | Data of that form. |
|
|
78
|
+
| `charset` | Characters of a character set, 8 bytes for each character. |
|
|
78
79
|
| `addresses`, `addresses-split` | A table of addresses: one table of 16-bit values, or a low-byte table and then a high-byte table. |
|
|
79
80
|
| `rts-addresses`, `rts-addresses-split` | The same, with each value the target minus 1, for a PHA, PHA, RTS dispatch. |
|
|
80
81
|
| `jumps` | A table of `JMP abs` instructions. Each 3-byte entry is an entry point. It is code. |
|
|
@@ -36,6 +36,7 @@ One repository can hold several programs, with one project folder for each progr
|
|
|
36
36
|
- Put a run file whose results are the evidence for a finding in `runs/`, with its results. Then git keeps the run file and the results, and another person can do the run again.
|
|
37
37
|
- A subagent can have no emulator tools. Give it its experiments as run files of the c64-testing skill. Each run starts its own emulator, so many runs can go at the same time. The emulator of the emulator tools is one machine, for the main session.
|
|
38
38
|
- To name a large program with more agents at the same time, give each agent one range of addresses. Each agent writes a batch with its owner range. Then join the batches with `join` of the c64-knowledge skill, and apply the result one time.
|
|
39
|
+
- Do not write a build script or a merge script. c64-source writes and assembles the loader with `--loader`, and `join` of c64-knowledge joins the batches.
|
|
39
40
|
- Write the knowledge only with the c64-knowledge script. Do not write or edit the files in `.c64-re-tools/knowledge/` yourself.
|
|
40
41
|
|
|
41
42
|
## Workflow
|
|
@@ -735,7 +735,7 @@ function dataLines(item: DataItem, layout: Layout, breaks: Set<number>): Array<{
|
|
|
735
735
|
const cells = cellsOf(item, layout, breaks);
|
|
736
736
|
const element = item.region?.element;
|
|
737
737
|
const perLine = (directive: Directive) =>
|
|
738
|
-
directive === "!word" ? WORDS_PER_LINE : directive === "!pet" || directive === "!scr" ? CHARS_PER_LINE : item.type === "sprite" ? 3 : element !== undefined && element <= 16 ? element : BYTES_PER_LINE;
|
|
738
|
+
directive === "!word" ? WORDS_PER_LINE : directive === "!pet" || directive === "!scr" ? CHARS_PER_LINE : item.type === "sprite" ? 3 : item.type === "charset" ? 8 : element !== undefined && element <= 16 ? element : BYTES_PER_LINE;
|
|
739
739
|
const lines: Array<{ address: number; text: string }> = [];
|
|
740
740
|
let group: Cell[] = [];
|
|
741
741
|
const flush = () => {
|
package/src/graphics.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Indexed images: the PNG encoder of emulator frames, and the C64 graphics
|
|
2
2
|
// in memory as images for the documentation (D80). A region of the knowledge
|
|
3
|
-
// gives no display mode and no colours, so a sprite or a
|
|
3
|
+
// gives no display mode and no colours, so a sprite, a bitmap or a charset is read
|
|
4
4
|
// both in hires and in multicolour, with fixed pattern palettes.
|
|
5
5
|
|
|
6
6
|
import { crc32, deflateSync } from "node:zlib";
|
|
@@ -102,15 +102,15 @@ export function spriteSheet(bytes: Uint8Array, mode: GraphicsMode, stride = 64,
|
|
|
102
102
|
return { width, height, pixels };
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
/**
|
|
106
|
-
|
|
105
|
+
/** Cells of 8 bytes as 8x8 pixels, `perRow` cells to a row, cell 0 at the top left. */
|
|
106
|
+
function cellFrame(bytes: Uint8Array, mode: GraphicsMode, perRow: number): IndexedFrame {
|
|
107
107
|
const cells = Math.ceil(bytes.length / 8);
|
|
108
|
-
const width = Math.min(cells,
|
|
109
|
-
const height = Math.ceil(cells /
|
|
108
|
+
const width = Math.min(cells, perRow) * 8;
|
|
109
|
+
const height = Math.ceil(cells / perRow) * 8;
|
|
110
110
|
const pixels = new Uint8Array(width * height);
|
|
111
111
|
for (let cell = 0; cell < cells; cell++) {
|
|
112
|
-
const left = (cell %
|
|
113
|
-
const top = Math.floor(cell /
|
|
112
|
+
const left = (cell % perRow) * 8;
|
|
113
|
+
const top = Math.floor(cell / perRow) * 8;
|
|
114
114
|
for (let row = 0; row < 8; row++) {
|
|
115
115
|
bytePixels(bytes[cell * 8 + row] ?? 0, mode).forEach((value, x) => (pixels[(top + row) * width + left + x] = value));
|
|
116
116
|
}
|
|
@@ -118,6 +118,16 @@ export function bitmapFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame
|
|
|
118
118
|
return { width, height, pixels };
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/** A bitmap of 8x8 cells of 8 bytes, 40 cells to a row, as the VIC reads it. */
|
|
122
|
+
export function bitmapFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame {
|
|
123
|
+
return cellFrame(bytes, mode, 40);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A character set: characters of 8 bytes, 16 to a row, so character N is in row N / 16 and column N % 16. */
|
|
127
|
+
export function charsetFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame {
|
|
128
|
+
return cellFrame(bytes, mode, 16);
|
|
129
|
+
}
|
|
130
|
+
|
|
121
131
|
/** Each pixel as a square of `factor` pixels, so a small image is readable on a page. */
|
|
122
132
|
export function scaleFrame(frame: IndexedFrame, factor: number): IndexedFrame {
|
|
123
133
|
const width = frame.width * factor;
|
package/src/knowledge/schema.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const REGION_TYPES = [
|
|
|
25
25
|
"screen",
|
|
26
26
|
"bitmap",
|
|
27
27
|
"sprite",
|
|
28
|
+
"charset",
|
|
28
29
|
"workspace",
|
|
29
30
|
"unused",
|
|
30
31
|
"dual-use",
|
|
@@ -263,7 +264,7 @@ function createSchema(db: DatabaseSync): void {
|
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
/** The version of the index this build makes. An index with another version is emptied and made again. */
|
|
266
|
-
export const INDEX_VERSION =
|
|
267
|
+
export const INDEX_VERSION = 4;
|
|
267
268
|
|
|
268
269
|
function indexVersion(db: DatabaseSync): number | undefined {
|
|
269
270
|
const hasMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'meta'").get();
|