@henols/c64-re-tools 2.0.1 → 2.1.0-rc.2
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/README.md +8 -4
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +24 -9
- package/skills/c64-assembler/scripts/assembler.ts +95 -0
- package/skills/c64-basic/SKILL.md +17 -9
- package/skills/c64-basic/scripts/basic.ts +26 -36
- package/skills/c64-disk/SKILL.md +17 -7
- package/skills/c64-disk/scripts/disk.ts +130 -104
- package/skills/c64-disk/scripts/lib/containers.ts +10 -12
- package/skills/c64-docs/SKILL.md +47 -28
- package/skills/c64-docs/references/style.md +6 -6
- package/skills/c64-docs/scripts/docs.ts +323 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +1 -1
- package/skills/c64-emulator/SKILL.md +11 -7
- package/skills/c64-knowledge/SKILL.md +34 -21
- package/skills/c64-knowledge/scripts/knowledge.ts +597 -718
- package/skills/c64-knowledge/scripts/lib/listing.ts +5 -7
- package/skills/c64-memory-map/SKILL.md +18 -10
- package/skills/c64-memory-map/scripts/memory-map.ts +81 -0
- package/skills/c64-provenance/SKILL.md +82 -26
- package/skills/c64-provenance/scripts/lib/compare.ts +8 -12
- package/skills/c64-provenance/scripts/lib/leftovers.ts +442 -0
- package/skills/c64-provenance/scripts/lib/library.ts +15 -25
- package/skills/c64-provenance/scripts/lib/runtime.ts +12 -37
- package/skills/c64-provenance/scripts/lib/separate.ts +9 -10
- package/skills/c64-provenance/scripts/provenance.ts +222 -252
- package/skills/c64-reverse-engineering/SKILL.md +18 -15
- package/skills/c64-source/SKILL.md +28 -16
- package/skills/c64-source/scripts/lib/loader.ts +52 -77
- package/skills/c64-source/scripts/lib/source.ts +33 -42
- package/skills/c64-source/scripts/{generate.ts → source.ts} +68 -109
- package/skills/c64-static-analysis/SKILL.md +24 -13
- package/skills/c64-static-analysis/scripts/lib/coverage.ts +3 -4
- package/skills/c64-static-analysis/scripts/lib/findings.ts +2 -2
- package/skills/c64-static-analysis/scripts/lib/instructions.ts +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +6 -8
- package/skills/c64-static-analysis/scripts/{analyze.ts → static-analysis.ts} +58 -118
- package/skills/c64-testing/SKILL.md +94 -19
- package/skills/c64-testing/references/recording-format.md +125 -0
- package/skills/c64-testing/references/scenario-format.md +24 -19
- package/skills/c64-testing/scripts/{diff.ts → lib/diff-files.ts} +18 -69
- package/skills/c64-testing/scripts/lib/diff.ts +5 -3
- package/skills/c64-testing/scripts/lib/play.ts +334 -0
- package/skills/c64-testing/scripts/lib/recording.ts +662 -0
- package/skills/c64-testing/scripts/lib/replay.ts +105 -0
- package/skills/c64-testing/scripts/{run.ts → lib/runs.ts} +36 -59
- package/skills/c64-testing/scripts/lib/scenario.ts +73 -81
- package/skills/c64-testing/scripts/testing.ts +189 -0
- package/skills/c64-unpacker/SKILL.md +42 -25
- package/skills/c64-unpacker/scripts/lib/evidence.ts +3 -4
- package/skills/c64-unpacker/scripts/lib/loads.ts +12 -10
- package/skills/c64-unpacker/scripts/lib/machine.ts +0 -2
- package/skills/c64-unpacker/scripts/lib/stages.ts +14 -19
- package/skills/c64-unpacker/scripts/{unpack.ts → unpacker.ts} +109 -211
- package/src/c64.ts +35 -20
- package/src/cli/main.ts +2 -2
- package/src/errors.ts +38 -0
- package/src/graphics.ts +3 -1
- package/src/host/server.ts +3 -2
- package/src/host/vice/session.ts +4 -4
- package/src/host-client/entry-state.ts +106 -0
- package/src/host-client/image.ts +21 -10
- package/src/host-client/steps.ts +285 -108
- package/src/host-client/transfer.ts +21 -15
- package/src/host-client/vice-session.ts +1 -1
- package/src/knowledge/database.ts +3 -23
- package/src/knowledge/expression.ts +8 -11
- package/src/knowledge/history.ts +2 -2
- package/src/knowledge/import.ts +13 -14
- package/src/knowledge/names.ts +50 -0
- package/src/knowledge/program.ts +11 -10
- package/src/knowledge/read.ts +16 -21
- package/src/knowledge/schema.ts +9 -3
- package/src/knowledge/text.ts +5 -5
- package/src/knowledge/write.ts +93 -137
- package/src/mcp/inputs.ts +105 -0
- package/src/mcp/output.ts +2 -13
- package/src/mcp/server.ts +12 -60
- package/src/mcp/tools/debug.ts +17 -12
- package/src/mcp/tools/execution.ts +5 -32
- package/src/mcp/tools/input.ts +7 -32
- package/src/mcp/tools/media.ts +6 -5
- package/src/mcp/tools/memory.ts +11 -38
- package/src/mcp/tools/video.ts +6 -5
- package/src/native/c64run.ts +162 -0
- package/src/native/discover.ts +8 -0
- package/src/native/dxa.ts +7 -6
- package/src/native/ghidra/analyze.ts +2 -1
- package/src/native/ghidra/index.ts +2 -1
- package/src/native/processes.ts +4 -3
- package/src/native/status.ts +25 -4
- package/src/native/types.ts +3 -7
- package/src/opcodes.ts +13 -13
- package/src/platform.ts +9 -9
- package/src/project.ts +16 -26
- package/src/protocol/messages.ts +3 -21
- package/src/protocol/vice.ts +7 -7
- package/src/script.ts +491 -49
- package/skills/c64-assembler/scripts/assemble.ts +0 -138
- package/skills/c64-docs/scripts/generate.ts +0 -109
- package/skills/c64-docs/scripts/link.ts +0 -239
- package/skills/c64-memory-map/scripts/memmap.ts +0 -92
- package/skills/c64-testing/scripts/checklist.ts +0 -51
- package/skills/c64-testing/scripts/test.ts +0 -65
|
@@ -33,11 +33,12 @@ One repository can hold several programs, with one project folder for each progr
|
|
|
33
33
|
- From the root of the repository, give `--project <folder>` to each skill script, and `project` to each emulator tool with a path.
|
|
34
34
|
- Write all files in the project folder. Do not write files at the root of the repository.
|
|
35
35
|
- Write each file that you make for your work in `.c64-re-tools/local/` of the project. Git ignores that folder. Examples are results, batches, step files, runs, snapshots and memory dumps.
|
|
36
|
-
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence
|
|
36
|
+
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
37
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
37
38
|
- Put a run that is evidence in `runs/<name>/`, with its run file, its results and the snapshot files that it uses. Change the paths in the run file to the new paths. Then git keeps them, and another person can do the run again.
|
|
38
39
|
- 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.
|
|
39
|
-
- 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.
|
|
40
|
-
- Do not write a build script or a merge script. c64-source writes and assembles the loader
|
|
40
|
+
- 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 `knowledge.ts join` of the c64-knowledge skill, and apply the result one time.
|
|
41
|
+
- Do not write a build script or a merge script. `source.ts generate --loader` of c64-source writes and assembles the loader, and `knowledge.ts join` of c64-knowledge joins the batches.
|
|
41
42
|
- Do not write a script that makes a deliverable again, for example a capture, copy or replay script. Commit the deliverable.
|
|
42
43
|
- Write the knowledge only with the c64-knowledge script. Do not write or edit the files in `.c64-re-tools/knowledge/` yourself.
|
|
43
44
|
|
|
@@ -62,7 +63,8 @@ Select the path for the scope:
|
|
|
62
63
|
| What the program does at run time: values, input, timing, screen | c64-emulator |
|
|
63
64
|
| The routines, code, data and references of the program | c64-static-analysis |
|
|
64
65
|
| The platform meaning of an address or a register | c64-memory-map |
|
|
65
|
-
| Differences between releases of the program | c64-provenance |
|
|
66
|
+
| Differences between releases of the program | c64-provenance `compare` |
|
|
67
|
+
| Leftovers of the development, a crack or a recrack: texts, old copies, unused bytes, dead code | c64-provenance `leftovers` |
|
|
66
68
|
| A routine that another program of the workspace has, for example a music driver | c64-provenance, with the library |
|
|
67
69
|
| Source code from the knowledge | c64-source |
|
|
68
70
|
| Source code to build | c64-assembler |
|
|
@@ -78,21 +80,21 @@ Do the stages in this sequence. Go to the next stage only when its exit check is
|
|
|
78
80
|
|
|
79
81
|
| Stage | Work | Exit check |
|
|
80
82
|
|---|---|---|
|
|
81
|
-
| 1. Acceptance | Ask the user for the criteria of the rebuild. Offer these: identical source, a verified loader, passed c64-testing scenarios, and a list of the code that no run executed. Also offer documentation of the flows, tables, formats and graphics. | The user gave the criteria. |
|
|
83
|
+
| 1. Acceptance | Ask the user for the criteria of the rebuild. Offer these: identical source, a verified loader, passed c64-testing scenarios, passed replays of the recordings, and a list of the code that no run executed. Also offer documentation of the flows, tables, formats and graphics. | The user gave the criteria. |
|
|
82
84
|
| 2. Get | c64-disk: list the disk. Extract the start file into `original/`. | The start file is in the project. |
|
|
83
85
|
| 3. Start | c64-basic: find the SYS address of the BASIC start. | You have the entry address. |
|
|
84
|
-
| 4. Unpack | c64-unpacker: examine the program. Capture a packed program into `.c64-re-tools/local/`. Then c64-knowledge: put the capture, or the PRG of a program without a packer, into the knowledge with `image`. | `program` of c64-knowledge shows the segments and the entry. |
|
|
85
|
-
| 5. Run | c64-emulator and
|
|
86
|
-
| 6. Analyze | c64-static-analysis. It reads the program and the coverage from the knowledge. | No coverage conflict is open. |
|
|
87
|
-
| 7. Name | c64-provenance: if the workspace has a library, find its routines with `match` and apply the batch. Then c64-knowledge and c64-memory-map: name each routine in the scope and give it a comment. Record the modules. When the scope has hundreds of routines, ask the user which routines to name. The source gives the other routines an `l_xxxx` label. | Each routine that the user selected has a name and a comment. |
|
|
88
|
-
| 8. Generate | c64-source
|
|
89
|
-
| 9. Build and test | c64-assembler with `--compare` into `build/`. c64-testing scenarios in `tests/`. | The criteria of stage 1 are true. |
|
|
90
|
-
| 10. Document | When the criteria ask for documentation: c64-docs into `README.md` and `docs
|
|
86
|
+
| 4. Unpack | c64-unpacker: examine the program with `unpacker.ts inspect`, `trace` and `stages`. Capture a packed program with `unpacker.ts capture` into `.c64-re-tools/local/`. Then c64-knowledge: put the capture, or the PRG of a program without a packer, into the knowledge with `knowledge.ts image`. | `program` of c64-knowledge shows the segments and the entry. |
|
|
87
|
+
| 5. Run | First record: ask the user to play each part that the scope names, with `testing.ts record --play` of c64-testing. Tell the user which parts. Record a part yourself only when the sessions of the user do not have it. Put the recordings in `tests/recordings/`. Then use c64-emulator and `testing.ts run` of c64-testing for the parts that a recording cannot have. Write a coverage map for each part into `.c64-re-tools/local/`. Then c64-knowledge: add the maps and the `coverage.bin` of each recording with `knowledge.ts coverage`. | `program` of c64-knowledge lists a coverage map for each part that the scope names. |
|
|
88
|
+
| 6. Analyze | c64-static-analysis: `static-analysis.ts analyze`. It reads the program and the coverage from the knowledge. | No coverage conflict is open. |
|
|
89
|
+
| 7. Name | c64-provenance: if the workspace has a library, find its routines with `provenance.ts match` and apply the batch. Then c64-knowledge and c64-memory-map: name each routine in the scope and give it a comment. Record the modules. When the scope has hundreds of routines, ask the user which routines to name. The source gives the other routines an `l_xxxx` label. | Each routine that the user selected has a name and a comment. |
|
|
90
|
+
| 8. Generate | c64-source: `source.ts generate --out src`. For an unpacked program, add `--loader`. | `check.identical` is true. For an unpacked program, `loader.verified` is true. |
|
|
91
|
+
| 9. Build and test | c64-assembler: `assembler.ts build` with `--compare` into `build/`. c64-testing: `testing.ts test` with the scenarios in `tests/`. Replay each recording on the rebuild with `testing.ts replay`: give the checkpoint routine, the state and the random state (`--pin`) with the names of stage 7. | The criteria of stage 1 are true. |
|
|
92
|
+
| 10. Document | When the criteria ask for documentation: c64-docs into `README.md` and `docs/`, with `docs.ts generate` and `docs.ts link`. | `docs.ts link --check` of c64-docs gives `result: "PASS"`. |
|
|
91
93
|
| 11. Hand over | Find each file that you made outside `.c64-re-tools/local/` and that is not a deliverable. Move it into `.c64-re-tools/local/`. Then give the answer (below). | Each file that you made outside `.c64-re-tools/local/` is a deliverable. |
|
|
92
94
|
|
|
93
|
-
In stage 5,
|
|
95
|
+
In stage 5, make the recordings before you change the program. A recording needs no names, and its coverage gives the analysis of stage 6 more code. If you cannot get to a part of the program, ask the user how to get to it. Do stages 7 and 8 again until the source is clear. Each run of c64-source writes the source again.
|
|
94
96
|
|
|
95
|
-
After stage 7, save each routine that other programs can have to the library with c64-provenance
|
|
97
|
+
After stage 7, save each routine that other programs can have to the library with `provenance.ts routine` of c64-provenance. Examples are a music driver, a decruncher and a loader. Ask the user before you make a library.
|
|
96
98
|
|
|
97
99
|
### Deliverables of a full rebuild
|
|
98
100
|
|
|
@@ -100,6 +102,7 @@ After stage 7, save each routine that other programs can have to the library wit
|
|
|
100
102
|
- the generated source
|
|
101
103
|
- the built program and the loader
|
|
102
104
|
- the c64-testing scenarios
|
|
105
|
+
- the c64-testing recordings in `tests/recordings/`
|
|
103
106
|
- the documentation in `README.md` and `docs/`, when the criteria ask for it
|
|
104
107
|
- the runs in `runs/` that the knowledge gives as evidence
|
|
105
108
|
- the scripts in `tools/` for the formats of this program, when you wrote them.
|
|
@@ -124,7 +127,7 @@ If the project has no layout, use this one. No script checks it. Each script tak
|
|
|
124
127
|
original/ the disk image or the program as you got it
|
|
125
128
|
src/ the generated source from c64-source
|
|
126
129
|
build/ programs from c64-assembler
|
|
127
|
-
tests/ c64-testing scenarios
|
|
130
|
+
tests/ c64-testing scenarios, and the recordings in tests/recordings/
|
|
128
131
|
runs/ the runs that the knowledge gives as evidence
|
|
129
132
|
docs/ the documentation from c64-docs, with the reference in docs/reference/
|
|
130
133
|
tools/ scripts for the formats of this program only
|
|
@@ -24,27 +24,40 @@ This skill does not change the knowledge.
|
|
|
24
24
|
The script runs Ghidra and ACME on this machine. With `--loader`, it also runs the loader in the emulator through the host runtime.
|
|
25
25
|
|
|
26
26
|
```
|
|
27
|
-
node <skill>/scripts/
|
|
28
|
-
node <skill>/scripts/
|
|
29
|
-
node <skill>/scripts/
|
|
27
|
+
node <skill>/scripts/source.ts generate --out src
|
|
28
|
+
node <skill>/scripts/source.ts generate --out src --entry '$080d'
|
|
29
|
+
node <skill>/scripts/source.ts generate --out src --loader
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
| Option | Meaning |
|
|
33
33
|
|---|---|
|
|
34
34
|
| `--out src` | The directory for the source files. The script writes over the files that it generated before. |
|
|
35
35
|
| `--entry '$080d'` | An entry point for Ghidra. Use it again for more entry points. The entry of the program, the routines and the code regions in the knowledge are entry points too. |
|
|
36
|
-
| `--analyzer-names` | Name an address that has no symbol and no platform name with its analyzer name, for example `FUN_17e2`. The order is heuristic, Ghidra, then DXA. Without it, the source makes its own names, for example `
|
|
36
|
+
| `--analyzer-names` | Name an address that has no symbol and no platform name with its analyzer name, for example `FUN_17e2`. The order is heuristic, Ghidra, then DXA. Without it, the source makes its own names, for example `l_17e2`. |
|
|
37
37
|
| `--space drive8` | Generate the source of drive code or a cartridge bank from the knowledge of that space. The coverage, `--loader` and the platform names are only for `c64`. |
|
|
38
38
|
| `--loader` | Also write `loader.asm` and `loader.prg`. This needs a capture in the knowledge, with its entry and its entry state. |
|
|
39
|
+
| `--max-listed 20` | List at most this number of items in each list. The default is 50. The counts are always in the result. |
|
|
39
40
|
|
|
40
41
|
When the knowledge has coverage, code that no run executed gets the line `; not executed` with its range. `notExecuted` lists it. The executed code is also a seed for Ghidra. Thus the source has the code after a JSR to a routine that Ghidra thinks does not return.
|
|
41
42
|
|
|
43
|
+
### Calling the script
|
|
44
|
+
|
|
45
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
46
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
47
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
48
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
49
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
50
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
51
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
52
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
53
|
+
|
|
42
54
|
### Project folder
|
|
43
55
|
|
|
44
56
|
- Paths are relative to the project folder. The project folder is the nearest folder at or above the working directory that holds `.c64-re-tools/`.
|
|
45
57
|
- If the working directory is not in a project and has projects below it, the script refuses and lists them. Then give `--project <folder>` to each script.
|
|
46
58
|
- Write each file that you make for your work in `.c64-re-tools/local/` of the project. Git ignores that folder. Examples are results, batches, step files, runs, snapshots and memory dumps.
|
|
47
|
-
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence
|
|
59
|
+
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
60
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
48
61
|
- Put a run that is evidence in `runs/<name>/`, with its run file, its results and the snapshot files that it uses. Change the paths in the run file to the new paths. Then git keeps them, and another person can do the run again.
|
|
49
62
|
- A shell redirect (`>`) is relative to the working directory, not to the project folder. From the folder above the project, write `<folder>/.c64-re-tools/local/result.json`.
|
|
50
63
|
|
|
@@ -52,11 +65,11 @@ When the knowledge has coverage, code that no run executed gets the line `; not
|
|
|
52
65
|
|
|
53
66
|
1. Put the program into the knowledge with `image` of the c64-knowledge skill. For a packed program, capture it first with the c64-unpacker skill. For a program without a packer, use the PRG.
|
|
54
67
|
2. Run the script.
|
|
55
|
-
3. Read `check`. `identical: true` tells that the source assembles to the program byte by byte.
|
|
68
|
+
3. Read `result` and `check`. `check.identical: true` tells that the source assembles to the program byte by byte.
|
|
56
69
|
4. Read the source. A label `l_xxxx` is an address that has no name in the knowledge.
|
|
57
70
|
5. Record names, comments, regions and modules with the c64-knowledge skill. Use `apply` for many changes.
|
|
58
71
|
6. Run the script again. Do steps 4 to 6 again until the source is clear.
|
|
59
|
-
7. Build the program with the c64-assembler skill:
|
|
72
|
+
7. Build the program with the c64-assembler skill: `assembler.ts build src main.asm --out <file.prg>`.
|
|
60
73
|
8. For an unpacked program, run the script with `--loader` at the end. Test `loader.prg` against the original with the c64-testing skill.
|
|
61
74
|
|
|
62
75
|
## The source
|
|
@@ -104,15 +117,15 @@ The script writes a macro use only when the use gives the bytes of the program.
|
|
|
104
117
|
1. `10 SYS 2061` starts it.
|
|
105
118
|
2. It stops the interrupts and makes all RAM visible. Then it moves its copier to two free pages. Thus a copier at $a000 or higher is not read from the BASIC ROM.
|
|
106
119
|
3. It copies each segment and the pages $0002-$03ff of the entry state into place. The script selects an order in which no copy writes over the packed bytes of a copy that has not run yet.
|
|
107
|
-
4. It sets the
|
|
120
|
+
4. It sets the screen registers of the VIC-II and the VIC bank from `io` in the entry state. Then it waits for the raster line of the entry.
|
|
108
121
|
5. It sets the CIA timers, their latches and the interrupt masks from `io` and `cia` in the entry state. A running timer gets the value that counts down to the timer value of the entry at the entry.
|
|
109
122
|
6. It sets $00/$01, the stack pointer, A, X, Y and the flags, and jumps to the entry.
|
|
110
123
|
|
|
111
|
-
The copy table, the copier and the jump to the entry use labels, not numbers. `loader_from_xxxx` and `loader_end_xxxx` are around each packed segment, and `loader_to_xxxx` is its first address in place. The jump goes to the label of the entry. Thus a segment can change its length, and the loader still copies it and jumps to the entry that moved. The
|
|
124
|
+
The copy table, the copier and the jump to the entry use labels, not numbers. `loader_from_xxxx` and `loader_end_xxxx` are around each packed segment, and `loader_to_xxxx` is its first address in place. The jump goes to the label of the entry. Thus a segment can change its length, and the loader still copies it and jumps to the entry that moved. The screen values, the raster line and the CIA values stay numbers. They are the entry state, not addresses of the program.
|
|
112
125
|
|
|
113
126
|
The flags go through the free stack byte below the stack pointer (`stackByte`). The loader changes this byte. The loader does not set the SID, the time of day of the CIAs, the CIA interrupt flags or the interrupt registers of the VIC-II. `loader.limits` names each CIA value that the loader cannot set as it was at the entry. A program that reads the SID voice 3 for random numbers can still differ in a scenario. A raster sync does not change a timer or the SID. Then observe after the program sets up its own random numbers. To sync the original and the rebuild in a test, see the sync rules of the c64-testing skill.
|
|
114
127
|
|
|
115
|
-
The script simulates the copies first. Then it runs `loader.prg` in the emulator to its jump to the entry. It compares the memory, $00/$01, the registers, the
|
|
128
|
+
The script simulates the copies first. Then it runs `loader.prg` in the emulator to its jump to the entry. It compares the memory, $00/$01, the registers, the screen registers of the VIC-II, the VIC bank and the raster line with the entry state there. It also compares the CIA timers, latches and interrupt masks. `loader.verified: true` tells that all of them are equal. An interrupt can run right at the entry, as in the original program.
|
|
116
129
|
|
|
117
130
|
## Knowledge
|
|
118
131
|
|
|
@@ -127,9 +140,8 @@ Do not store:
|
|
|
127
140
|
|
|
128
141
|
## Result
|
|
129
142
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- `files`: the files in the output directory.
|
|
143
|
+
- `result`: `PASS` when every check passes, else `FAIL`.
|
|
144
|
+
- `files`: the files that the script wrote in `--out`, each with `out`, `bytes` and `sha256`. `sourceMap` names `source-map.json`.
|
|
133
145
|
- `instructions`, `dataBytes` and `undecodedBytes`.
|
|
134
146
|
- `labels`: `named` labels from the knowledge and `generated` labels `l_xxxx`.
|
|
135
147
|
- `entriesInData`: each `--entry` in a data region of the knowledge, with the `region` and its `origin`. Ghidra does not decode an entry in a data region.
|
|
@@ -138,7 +150,7 @@ One JSON object:
|
|
|
138
150
|
- `operandsAsNumbers`: the operand expressions that the source has as a number with a side comment, each with its `address`, `expression` and `reason`. An expression becomes a number when no instruction with an operand value is at the address, or when a name has no address. An expression that ACME refuses or does not assemble to the program also becomes a number. Then the script assembles the source again, at most six times.
|
|
139
151
|
- `unusedModules`: modules in the knowledge whose start is in no segment of the program. Often this tells that the program does not hold code that the knowledge describes.
|
|
140
152
|
- `entryOutside`: the entry is in no segment of the program in the knowledge, with a `reason`. The capture does not hold the program.
|
|
141
|
-
- `notExecuted`: when the knowledge has coverage, the count of instructions that no run executed, the count of their ranges, and the first ranges. `
|
|
153
|
+
- `notExecuted`: when the knowledge has coverage, the count of instructions that no run executed, the count of their ranges, and the first ranges. `rest` names the file `not_executed.json` in `--out` when the list is not complete. This file has all of the ranges. Each range has its source `file` and the `routine` before it. `files` counts the ranges in each file.
|
|
142
154
|
- `macroUses`: the count of macro uses that the source has.
|
|
143
155
|
- `macrosAsInstructions`: the macro uses that the source has as instructions, each with its `start`, `end`, `macro` and `reason`.
|
|
144
156
|
- `patchesNotWritten`: the patches that the source does not have, each with its range, `switch` and `reason`.
|
|
@@ -146,7 +158,7 @@ One JSON object:
|
|
|
146
158
|
- `switches`: the build of each switch. Each item has the `define` for the c64-assembler skill, the patches and the `result` of the build with the switch on. `fits` tells that the build is good, and `changedBytes` counts the changed bytes. `overflow` tells that the code of a patch is longer than its range. `changes outside` gives the first byte outside the ranges that changed. `does not assemble` gives the diagnostics of ACME. `insideNames` lists the labels inside a patch. Code that writes to one of these labels changes the patch when the switch is on.
|
|
147
159
|
- `loader`: the program, the copies in their order, the copier page, `stackByte`, `verified` and `differences`. `limits` lists the CIA values that the loader cannot set. An entry state from an old capture has no CIA values. Then `limits` tells you to capture the program again. When the script cannot make the loader, `loader` has `verified: false` and a `reason`. The loader includes the source. Thus a source that does not assemble to the program also stops the loader.
|
|
148
160
|
|
|
149
|
-
|
|
161
|
+
`result` is `PASS` when the source is identical and the emulator check of the loader passes. `result` is `FAIL` for a failed check and for an entry in no segment. A patch that the source does not have, or a switch that does not fit, also gives `FAIL`. `asBytes`, `notExecuted`, `operandsAsNumbers`, `macrosAsInstructions` and `patchesNotWritten` are lists of at most `--max-listed` items.
|
|
150
162
|
|
|
151
163
|
## Failure and conflicts
|
|
152
164
|
|
|
@@ -159,7 +171,7 @@ The exit status is 0 when the source is identical and the emulator check of the
|
|
|
159
171
|
- `patchesNotWritten`: read each `reason`. Move the start or the end of the patch to an instruction, or give the switch another name.
|
|
160
172
|
- `macrosAsInstructions`: read each `reason`. Correct the macro or the args, or remove the use with `unuse`. The source stays identical, so this is not a failure.
|
|
161
173
|
- `entryOutside`: the run fails, but the script still writes and checks the source. With `--loader`, `loader.verified` is `false` with the same `reason`. Capture the program again with c64-unpacker `loads` or `capture`. Make sure that a segment holds the entry. Then put it into the knowledge again with `image`.
|
|
162
|
-
- `entriesInData`: an analyzer region of data hides the code at the entry. If the entry is code, record a code region at the entry with the c64-knowledge skill, for example `classify '$0500
|
|
174
|
+
- `entriesInData`: an analyzer region of data hides the code at the entry. If the entry is code, record a code region at the entry with the c64-knowledge skill, for example `classify '$0500-$05ff' code`. A region of the user or the LLM replaces the analyzer region. Then generate again.
|
|
163
175
|
- `loader.verified: false` with a `reason`: the script did not make the loader. The script still writes and checks the source. When no copy order or no free pages exist, or the loader needs too many copies, the reason gives the ranges. Capture the program again with fewer segments, or write the loader by hand.
|
|
164
176
|
- `loader.verified: false` with `differences`: the loader ran, and `differences` gives the bytes and registers that are not equal.
|
|
165
177
|
- `loader.verified: false` with `stop`: the loader did not get to its copier or to its jump. `stop` gives the address where it stopped and `port`, the value of $01. `history` gives the last 16 instructions. `cause` gives the first byte of the copier that is not equal to its byte in the loader file. If the copier ran, `cause` can also give the first byte that a copy did not write correctly.
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
// overwrites the packed bytes of a copy that has not run yet. The plan is
|
|
8
8
|
// simulated here before the emulator runs it.
|
|
9
9
|
|
|
10
|
-
import { formatC64Address } from "#src/c64.ts";
|
|
10
|
+
import { type AddressRange, formatC64Address, formatC64Byte } from "#src/c64.ts";
|
|
11
|
+
import { Refusal } from "#src/errors.ts";
|
|
12
|
+
import { parseEntryState } from "#src/host-client/entry-state.ts";
|
|
11
13
|
import { WireFailure } from "#src/host-client/tools.ts";
|
|
12
|
-
import type { Range } from "./source.ts";
|
|
13
14
|
|
|
14
15
|
export interface EntryState {
|
|
15
16
|
pc: number;
|
|
@@ -59,51 +60,27 @@ export function rasterLine(registers: Uint8Array): number {
|
|
|
59
60
|
return ((registers[0x11]! & 0x80) << 1) | registers[0x12]!;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
const FLAG_BITS
|
|
63
|
+
const FLAG_BITS = { n: 0x80, v: 0x40, d: 0x08, i: 0x04, z: 0x02, c: 0x01 } as const;
|
|
63
64
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The entry state that the loader recreates: the shared entry state of the
|
|
67
|
+
* c64-unpacker capture, with the flags as PLP reads them, the VIC-II and the CIAs.
|
|
68
|
+
*/
|
|
69
|
+
export function loaderState(value: unknown, where: string): EntryState {
|
|
70
|
+
const state = parseEntryState(value, where);
|
|
66
71
|
const fail = (what: string): never => {
|
|
67
|
-
throw new
|
|
72
|
+
throw new Refusal("invalid-input", `${where} is not an entry state of a c64-unpacker capture: ${what}. Capture the program again with c64-unpacker capture or loads.`);
|
|
68
73
|
};
|
|
69
|
-
let value: Record<string, any>;
|
|
70
|
-
try {
|
|
71
|
-
value = JSON.parse(text) as Record<string, any>;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
return fail((error as Error).message);
|
|
74
|
-
}
|
|
75
|
-
const registers = value.registers;
|
|
76
|
-
const byte = (item: unknown, what: string) => (Number.isInteger(item) && (item as number) >= 0 && (item as number) <= 0xff ? (item as number) : fail(`${what} is not a byte`));
|
|
77
|
-
if (typeof registers !== "object" || registers === null) return fail("it has no registers");
|
|
78
|
-
const pc = typeof registers.pc === "string" && /^\$[0-9a-f]{4}$/i.test(registers.pc) ? Number.parseInt(registers.pc.slice(1), 16) : fail("registers.pc is not a $ address");
|
|
79
|
-
if (typeof registers.flags !== "object" || registers.flags === null) fail("registers.flags is missing");
|
|
80
74
|
let flags = 0x20;
|
|
81
|
-
for (const [name, bit] of Object.entries(FLAG_BITS)) if (
|
|
82
|
-
|
|
83
|
-
for (let page = 0; page < 4; page++) {
|
|
84
|
-
const hex = value.pages?.[formatC64Address(page << 8)];
|
|
85
|
-
if (typeof hex !== "string" || !/^[0-9a-f]{512}$/i.test(hex)) fail(`pages.${formatC64Address(page << 8)} is not 256 bytes of hex`);
|
|
86
|
-
pages.set(Buffer.from(hex as string, "hex"), page << 8);
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
pc,
|
|
90
|
-
a: byte(registers.a, "registers.a"),
|
|
91
|
-
x: byte(registers.x, "registers.x"),
|
|
92
|
-
y: byte(registers.y, "registers.y"),
|
|
93
|
-
sp: byte(registers.sp, "registers.sp"),
|
|
94
|
-
flags,
|
|
95
|
-
port: [byte(value.cpuPort?.$00, "cpuPort.$00"), byte(value.cpuPort?.$01, "cpuPort.$01")],
|
|
96
|
-
pages,
|
|
97
|
-
...readVic(value.io, fail),
|
|
98
|
-
...readCia(value, fail),
|
|
99
|
-
};
|
|
75
|
+
for (const [name, bit] of Object.entries(FLAG_BITS)) if (state.flags[name as keyof typeof FLAG_BITS]) flags |= bit;
|
|
76
|
+
return { pc: state.pc, a: state.a, x: state.x, y: state.y, sp: state.sp, flags, port: state.port, pages: state.pages, ...readVic(state.raw.io as Record<string, any> | undefined, fail), ...readCia(state.raw, fail) };
|
|
100
77
|
}
|
|
101
78
|
|
|
102
79
|
export interface Copy {
|
|
103
80
|
/** What the copy writes: a segment or a part of pages 0-3. */
|
|
104
81
|
what: string;
|
|
105
|
-
target:
|
|
106
|
-
source:
|
|
82
|
+
target: AddressRange;
|
|
83
|
+
source: AddressRange;
|
|
107
84
|
/** True when the copy runs from the last byte down: its target is above an overlapping source. */
|
|
108
85
|
backward: boolean;
|
|
109
86
|
}
|
|
@@ -111,7 +88,7 @@ export interface Copy {
|
|
|
111
88
|
/** What the loader holds, in its order: a segment or a part of pages 0-3, packed at an address of the loader. */
|
|
112
89
|
export interface PackedBlock {
|
|
113
90
|
what: string;
|
|
114
|
-
range:
|
|
91
|
+
range: AddressRange;
|
|
115
92
|
packedAt: number;
|
|
116
93
|
bytes: (address: number) => number;
|
|
117
94
|
}
|
|
@@ -161,13 +138,11 @@ export const MAX_COPIES = Math.min(Math.floor((256 - 2) / COPY_ENTRY), Math.floo
|
|
|
161
138
|
const LOAD_LIMIT = 0xcfff;
|
|
162
139
|
const COPIER_PAGES = [...Array.from({ length: 15 }, (_, index) => 0xc000 + index * 0x100), ...Array.from({ length: 0x9b }, (_, index) => 0x9e00 - index * 0x100)];
|
|
163
140
|
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const overlaps = (a: Range, b: Range) => a.start <= b.end && b.start <= a.end;
|
|
167
|
-
const show = (range: Range) => `${hex4(range.start)}-${hex4(range.end)}`;
|
|
141
|
+
const overlaps = (a: AddressRange, b: AddressRange) => a.start <= b.end && b.start <= a.end;
|
|
142
|
+
const show = (range: AddressRange) => `${formatC64Address(range.start)}-${formatC64Address(range.end)}`;
|
|
168
143
|
|
|
169
144
|
/** Parts of `range` that no range in `holes` covers. */
|
|
170
|
-
function subtract(range:
|
|
145
|
+
function subtract(range: AddressRange, holes: AddressRange[]): AddressRange[] {
|
|
171
146
|
let pieces = [range];
|
|
172
147
|
for (const hole of holes) {
|
|
173
148
|
pieces = pieces.flatMap((piece) => {
|
|
@@ -217,13 +192,13 @@ export function stopCause(ram: Uint8Array, plan: LoaderPlan, copierSource: numbe
|
|
|
217
192
|
const got = ram[plan.copier + offset]!;
|
|
218
193
|
const wanted = fileByte(copierSource + offset);
|
|
219
194
|
if (got === wanted) continue;
|
|
220
|
-
const rom = underRom(copierSource + offset) ? `; ${
|
|
221
|
-
return `The copier at ${
|
|
195
|
+
const rom = underRom(copierSource + offset) ? `; ${formatC64Address(copierSource + offset)} is under a ROM, so the move read the ROM` : "";
|
|
196
|
+
return `The copier at ${formatC64Address(plan.copier + offset)} holds ${formatC64Byte(got)}, not ${formatC64Byte(wanted)} from ${formatC64Address(copierSource + offset)} of the loader file${rom}.`;
|
|
222
197
|
}
|
|
223
198
|
if (!copied) return undefined;
|
|
224
199
|
for (const copy of plan.copies) {
|
|
225
200
|
for (let address = copy.target.start; address <= copy.target.end; address++) {
|
|
226
|
-
if (ram[address] !== expected(address)) return `The copy of ${copy.what} wrote ${
|
|
201
|
+
if (ram[address] !== expected(address)) return `The copy of ${copy.what} wrote ${formatC64Byte(ram[address]!)} at ${formatC64Address(address)}, not ${formatC64Byte(expected(address))}.`;
|
|
227
202
|
}
|
|
228
203
|
}
|
|
229
204
|
return undefined;
|
|
@@ -304,8 +279,8 @@ interface Op {
|
|
|
304
279
|
*/
|
|
305
280
|
export function ciaSection(state: EntryState): { ops: Op[]; plan: CiaPlan } | undefined {
|
|
306
281
|
if (state.cia === undefined) return undefined;
|
|
307
|
-
const lda = (value: number): Op => ({ text: `lda #${
|
|
308
|
-
const sta = (address: number): Op => ({ text: `sta ${
|
|
282
|
+
const lda = (value: number): Op => ({ text: `lda #${formatC64Byte(value)}`, bytes: 2, cycles: 2 });
|
|
283
|
+
const sta = (address: number): Op => ({ text: `sta ${formatC64Address(address)}`, bytes: 3, cycles: 4 });
|
|
309
284
|
const limits: string[] = [];
|
|
310
285
|
const timers: CiaTimerPlan[] = [];
|
|
311
286
|
const disable: Op[] = [];
|
|
@@ -363,7 +338,7 @@ export function ciaSection(state: EntryState): { ops: Op[]; plan: CiaPlan } | un
|
|
|
363
338
|
if (mask !== 0) masks.push(lda(0x80 | mask), sta(base + 0x0d));
|
|
364
339
|
touched = true;
|
|
365
340
|
}
|
|
366
|
-
if (touched) acks.push({ text: `lda ${
|
|
341
|
+
if (touched) acks.push({ text: `lda ${formatC64Address(base + 0x0d)}`, bytes: 3, cycles: 4 });
|
|
367
342
|
const flags = chip.registers[0x0d]! & 0x1f;
|
|
368
343
|
if (flags !== 0) limits.push(`CIA ${id} has the interrupt flags $${flags.toString(16).padStart(2, "0")} at the entry: only an event sets a flag, so the loader cannot.`);
|
|
369
344
|
});
|
|
@@ -386,7 +361,7 @@ export function ciaSection(state: EntryState): { ops: Op[]; plan: CiaPlan } | un
|
|
|
386
361
|
|
|
387
362
|
export interface LoaderInput {
|
|
388
363
|
/** The segments with the files that hold them, in address order. */
|
|
389
|
-
segments: Array<
|
|
364
|
+
segments: Array<AddressRange & { files: string[] }>;
|
|
390
365
|
byteAt: (address: number) => number;
|
|
391
366
|
state: EntryState;
|
|
392
367
|
entry: number;
|
|
@@ -407,7 +382,7 @@ export const COPIER_SOURCE = "loader_copier_src";
|
|
|
407
382
|
export function planLoader(input: LoaderInput): LoaderPlan {
|
|
408
383
|
const { state } = input;
|
|
409
384
|
// What the copies write: each segment without $00/$01, which the loader sets last, and pages $0002-$03ff where no segment is.
|
|
410
|
-
const blocks: Array<{ what: string; range:
|
|
385
|
+
const blocks: Array<{ what: string; range: AddressRange; files?: string[]; pseudo: number; bytes: (address: number) => number }> = [];
|
|
411
386
|
for (const segment of input.segments) blocks.push({ what: `segment ${show(segment)}`, range: segment, files: segment.files, pseudo: segment.start, bytes: input.byteAt });
|
|
412
387
|
for (const part of subtract({ start: 0x0002, end: 0x03ff }, input.segments)) {
|
|
413
388
|
blocks.push({ what: `entry state ${show(part)}`, range: part, pseudo: part.start, bytes: (address) => state.pages[address]! });
|
|
@@ -437,7 +412,7 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
437
412
|
const copierImage = at;
|
|
438
413
|
const copierSize = COPIER_CODE_SIZE + (state.vic === undefined ? 0 : VIC_SIZE) + ciaSize + COPY_ENTRY * plain.length + 2;
|
|
439
414
|
const end = copierImage + copierSize - 1;
|
|
440
|
-
if (end > LOAD_LIMIT) throw new WireFailure("invalid-input", `The loader would end at ${
|
|
415
|
+
if (end > LOAD_LIMIT) throw new WireFailure("invalid-input", `The loader would end at ${formatC64Address(end)}, after ${formatC64Address(LOAD_LIMIT)}: loading it would write the I/O area. The image is too large for one loader file.`);
|
|
441
416
|
|
|
442
417
|
const order = copyOrder(plain);
|
|
443
418
|
if (order === undefined) {
|
|
@@ -457,8 +432,8 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
457
432
|
const copyBlocks = order.map((index) => blockOf[index]!);
|
|
458
433
|
|
|
459
434
|
const stackByte = 0x0100 + state.sp;
|
|
460
|
-
const lines: string[] = [...input.header.map((line) => `; ${line}`), "", "!cpu 6510", '!source "constants.asm"', ...(input.macros ? ['!source "macros.asm"'] : []), "", `* = ${
|
|
461
|
-
lines.push(` !byte $0b,$08,$0a,$00,$9e,${[...String(STUB)].map((digit) =>
|
|
435
|
+
const lines: string[] = [...input.header.map((line) => `; ${line}`), "", "!cpu 6510", '!source "constants.asm"', ...(input.macros ? ['!source "macros.asm"'] : []), "", `* = ${formatC64Address(LOAD)}`];
|
|
436
|
+
lines.push(` !byte $0b,$08,$0a,$00,$9e,${[...String(STUB)].map((digit) => formatC64Byte(digit.charCodeAt(0))).join(",")},$00,$00,$00 ; 10 SYS ${STUB}`);
|
|
462
437
|
lines.push(
|
|
463
438
|
"!zone loader_stub {",
|
|
464
439
|
" sei",
|
|
@@ -467,22 +442,22 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
467
442
|
" sta $01",
|
|
468
443
|
" ldx #0",
|
|
469
444
|
`- lda ${COPIER_SOURCE},x`,
|
|
470
|
-
` sta ${
|
|
445
|
+
` sta ${formatC64Address(copier)},x`,
|
|
471
446
|
` lda ${COPIER_SOURCE}+$100,x`,
|
|
472
|
-
` sta ${
|
|
447
|
+
` sta ${formatC64Address(copier + 0x100)},x`,
|
|
473
448
|
" inx",
|
|
474
449
|
" bne -",
|
|
475
|
-
` jmp ${
|
|
450
|
+
` jmp ${formatC64Address(copier)}`,
|
|
476
451
|
"}",
|
|
477
452
|
);
|
|
478
453
|
// The labels around each block give the copy table its numbers, so a block can change its length.
|
|
479
454
|
for (const block of blocks) {
|
|
480
|
-
lines.push("", `; ${block.what}`, blockLabel(block.range.start, "from"), `!pseudopc ${
|
|
455
|
+
lines.push("", `; ${block.what}`, blockLabel(block.range.start, "from"), `!pseudopc ${formatC64Address(block.pseudo)} {`, blockLabel(block.range.start, "to"));
|
|
481
456
|
if (block.files !== undefined) for (const file of block.files) lines.push(`!source "${file}"`);
|
|
482
457
|
else {
|
|
483
458
|
for (let address = block.range.start; address <= block.range.end; address += 16) {
|
|
484
459
|
const last = Math.min(block.range.end, address + 15);
|
|
485
|
-
lines.push(` !byte ${Array.from({ length: last - address + 1 }, (_, offset) =>
|
|
460
|
+
lines.push(` !byte ${Array.from({ length: last - address + 1 }, (_, offset) => formatC64Byte(block.bytes(address + offset))).join(",")}`);
|
|
486
461
|
}
|
|
487
462
|
}
|
|
488
463
|
lines.push("}", blockLabel(block.range.start, "end"));
|
|
@@ -490,9 +465,9 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
490
465
|
const [port0, port1] = state.port;
|
|
491
466
|
lines.push(
|
|
492
467
|
"",
|
|
493
|
-
`; The copier runs at ${
|
|
468
|
+
`; The copier runs at ${formatC64Address(copier)}. The flags pass through ${formatC64Address(stackByte)}, the free stack byte below the stack pointer.`,
|
|
494
469
|
COPIER_SOURCE,
|
|
495
|
-
`!pseudopc ${
|
|
470
|
+
`!pseudopc ${formatC64Address(copier)} {`,
|
|
496
471
|
"!zone loader_copier {",
|
|
497
472
|
" lda #$34",
|
|
498
473
|
" sta $01",
|
|
@@ -550,28 +525,28 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
550
525
|
// The CIA part runs after the raster wait: the wait ends its whole lines earlier, so the entry comes in its line.
|
|
551
526
|
...vicLines(state, Math.floor((cia?.ops.reduce((sum, op) => sum + op.cycles, 0) ?? 0) / LINE_CYCLES)),
|
|
552
527
|
...(cia === undefined ? [] : cia.ops.map((op) => ` ${op.text}`)),
|
|
553
|
-
` lda #${
|
|
528
|
+
` lda #${formatC64Byte(port0)}`,
|
|
554
529
|
" sta $00",
|
|
555
|
-
` lda #${
|
|
530
|
+
` lda #${formatC64Byte(port1)}`,
|
|
556
531
|
" sta $01",
|
|
557
|
-
` ldx #${
|
|
532
|
+
` ldx #${formatC64Byte((state.sp - 1) & 0xff)}`,
|
|
558
533
|
" txs",
|
|
559
|
-
` lda #${
|
|
560
|
-
` sta ${
|
|
561
|
-
` lda #${
|
|
562
|
-
` ldx #${
|
|
563
|
-
` ldy #${
|
|
534
|
+
` lda #${formatC64Byte(state.flags)}`,
|
|
535
|
+
` sta ${formatC64Address(stackByte)}`,
|
|
536
|
+
` lda #${formatC64Byte(state.a)}`,
|
|
537
|
+
` ldx #${formatC64Byte(state.x)}`,
|
|
538
|
+
` ldy #${formatC64Byte(state.y)}`,
|
|
564
539
|
" plp",
|
|
565
540
|
// A global label: the check of the loader stops here, before an interrupt can run.
|
|
566
|
-
`${LOADER_JUMP} jmp ${input.entryName ??
|
|
541
|
+
`${LOADER_JUMP} jmp ${input.entryName ?? formatC64Address(input.entry)}`,
|
|
567
542
|
".count !word 0",
|
|
568
543
|
".index !byte 0",
|
|
569
544
|
".back !byte 0",
|
|
570
545
|
...(state.vic === undefined
|
|
571
546
|
? []
|
|
572
547
|
: [
|
|
573
|
-
`.vicreg !byte ${VIC_DISPLAY_REGISTERS.map((item) =>
|
|
574
|
-
`.vicval !byte ${VIC_DISPLAY_REGISTERS.map((item) =>
|
|
548
|
+
`.vicreg !byte ${VIC_DISPLAY_REGISTERS.map((item) => formatC64Byte(item.register)).join(",")}`,
|
|
549
|
+
`.vicval !byte ${VIC_DISPLAY_REGISTERS.map((item) => formatC64Byte(state.vic!.registers[item.register]!)).join(",")}`,
|
|
575
550
|
]),
|
|
576
551
|
".table",
|
|
577
552
|
);
|
|
@@ -609,7 +584,7 @@ function vicLines(state: EntryState, earlier: number): string[] {
|
|
|
609
584
|
return [
|
|
610
585
|
" lda #$35",
|
|
611
586
|
" sta $01",
|
|
612
|
-
` ldx #${
|
|
587
|
+
` ldx #${formatC64Byte(VIC_DISPLAY_REGISTERS.length - 1)}`,
|
|
613
588
|
".vic ldy .vicreg,x",
|
|
614
589
|
" lda .vicval,x",
|
|
615
590
|
" sta $d000,y",
|
|
@@ -617,14 +592,14 @@ function vicLines(state: EntryState, earlier: number): string[] {
|
|
|
617
592
|
" bpl .vic",
|
|
618
593
|
" lda $dd00",
|
|
619
594
|
" and #$fc",
|
|
620
|
-
` ora #${
|
|
595
|
+
` ora #${formatC64Byte(state.vic.bank)}`,
|
|
621
596
|
" sta $dd00",
|
|
622
597
|
`.raster lda $d012`,
|
|
623
|
-
` cmp #${
|
|
598
|
+
` cmp #${formatC64Byte(line & 0xff)}`,
|
|
624
599
|
" bne .raster",
|
|
625
600
|
" lda $d011",
|
|
626
601
|
" and #$80",
|
|
627
|
-
` cmp #${
|
|
602
|
+
` cmp #${formatC64Byte((line >> 1) & 0x80)}`,
|
|
628
603
|
" bne .raster",
|
|
629
604
|
];
|
|
630
605
|
}
|