@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henols/c64-re-tools",
3
- "version": "2.0.0-rc.17",
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",
@@ -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 bitmap shows in hires and in multicolour, because the knowledge has no graphics mode. A screen shows as text.
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 extracted asset (for example `../assets/sprites/bruce.png`), an image of the reference (`reference/images/`), or a PNG that a skill made. `link.ts` checks that each image exists.
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 bitmap 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"))}).`,
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("", `![${anchor} in ${mode}](${rel(page, image)})`);
@@ -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 bitmap is read
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
- /** A bitmap of 8x8 cells of 8 bytes, 40 cells to a row, as the VIC reads it. */
106
- export function bitmapFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame {
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, 40) * 8;
109
- const height = Math.ceil(cells / 40) * 8;
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 % 40) * 8;
113
- const top = Math.floor(cell / 40) * 8;
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;
@@ -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 = 3;
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();