diff --git a/AGENTS.md b/AGENTS.md index 465037d..5cf961d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,6 +25,7 @@ This repository contains structured skills and reference material for developing | `skills/interrupts/` | IRQ, NMI, CIA timers, raster interrupts | Interrupt-driven code, timing | | `skills/compiler-design/` | Compiler & assembler construction for C64/128 | Writing compilers, assemblers, parsers | | `skills/vice-emulator/` | VICE emulator, autostart, monitor, snapshots, automation | Running, testing, and debugging C64 software in VICE | +| `skills/powerc/` | Power C compiler on the C64 | Writing, compiling, linking, and running C programs (reference/POWERc.d81) | ### Reference Files diff --git a/examples/powerc-build.bat b/examples/powerc-build.bat new file mode 100644 index 0000000..2d52ee6 --- /dev/null +++ b/examples/powerc-build.bat @@ -0,0 +1,8 @@ +@echo off +setlocal +REM Usage: powerc-build.bat "shell commands" (use \n for RETURN) +set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" +set "D81=%~dp0powerc-work.d81" +set "CMDS=%~1" +if "%CMDS%"=="" set "CMDS=cc squares\n" +"%VICE_EXE%" -8 "%D81%" -autostart "%D81%:shell" -autostart-warp -warp -keybuf "%CMDS%" diff --git a/examples/powerc-ced.bat b/examples/powerc-ced.bat new file mode 100644 index 0000000..f8260ee --- /dev/null +++ b/examples/powerc-ced.bat @@ -0,0 +1,14 @@ +@echo off +setlocal + +set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe" +set "D81=%~dp0..\reference\POWERc.d81" + +if not exist "%VICE_EXE%" ( + echo x64sc.exe not found at "%VICE_EXE%" + exit /b 1 +) + +REM Mount POWERc.d81 in drive 8, autostart "shell", then type "ced" at the shell prompt. +REM -keybuf is fed after autostart completes; \n = RETURN (\r is typed literally as "r"). +"%VICE_EXE%" -8 "%D81%" -autostart "%D81%:shell" -autostart-warp -keybuf "ced\n" diff --git a/skills/powerc/SKILL.md b/skills/powerc/SKILL.md new file mode 100644 index 0000000..336c8a4 --- /dev/null +++ b/skills/powerc/SKILL.md @@ -0,0 +1,227 @@ +--- +name: powerc +description: > + Use this skill when writing, compiling, linking, or running C programs with + Spinnaker's Power C on the Commodore 64 (reference/POWERc.d81 under VICE). + Covers the C dialect limits (K&R style, 2-byte int, 5-byte float, no bit + fields), the library, the Shell, the ed/ced editors, the cc/trim/link + toolchain, PETSCII source files, calling machine language with sys(), and + how to automate the whole build in VICE. + Source: Power C Instruction Manual (Spinnaker, 1986); verified in VICE 3.10. +--- + +# Power C on the Commodore 64 + +Power C is a disk-based native-code C compiler. Everything runs from a Shell +(`$` prompt) on the disk image `reference/POWERc.d81`. For emulator mechanics +(keybuf, warp, screenshots) load `skills/vice-emulator/SKILL.md`; this skill is +the C side. + +## Language: what to write + +Power C is **K&R C**, not ANSI C. Write code that way or it will not compile. + +```c +#include + +main() +{ + int i; + for (i = 1; i <= 5; i++) + printf("%d squared is %d\n", i, i * i); +} +``` + +- **K&R function definitions**: parameter types go after the parameter list + (`add3(a, b, c) int a, b, c; { ... }`). No prototypes, no `void`. +- Functions returning anything other than `int` must be declared in the calling + file (`char *malloc(); float atof();`). +- **Type sizes**: `char` 1, `short`/`int`/`long`/`unsigned`/pointer 2, + `float`/`double` 5 (BASIC float format). `short`, `int`, `long` are synonyms; + so are `float` and `double`. +- Integers are stored low byte first. Chars are not sign-extended; `>>` does not + sign-fill (vacated bits are always 0). +- Identifiers are significant to **8 characters** (goto labels to 7). +- **Not supported**: bit fields (use shifts and masks), static pointer + initialisers other than strings (`static char *s = "x";` is fine, + `static int *p = &x;` is not), multi-character constants (only the first + char is used). +- Conditional operator does not unify operand types: cast explicitly + (`x = a == b ? f : (float) i;`). +- `||`, `?:`, `=` and `,` inside subscripts/constant expressions need + parentheses: `a[(i > j ? i : j)]`. +- String constants are limited to 255 characters. +- `register` is ignored; the first 32 bytes of a function's ints/chars/pointers + live in zero page, so **declare hot variables first**. +- Use `unsigned` for values that are never negative (especially array indices); + it is faster. +- `#include ` and `#include "x"` are identical, and the header must be on the + **same disk as the source** (copy `stdio.h`, `math.h` etc. to your work disk). +- Large sources can overflow the compiler: split into several `.c` files, + compile each, and give all the `.o` files to the linker. +- Keyboard EOF is a `.` on an otherwise blank line. + +## Typing special characters (real C64 keyboard / in ced) + +| Char | Keys | +|------|------| +| `{` `}` | Shift+`+`, Shift+`-` | +| `\` | English pound key | +| `~` | Logo+`P` | +| `_` | Logo+`@` | +| `\|` | Logo+`*` | + +## Source file format on disk + +Source files are **SEQ files in PETSCII with CR line endings**. When writing +them from the host (e.g. with `c1541 -write`), convert: + +- ASCII `a-z` -> `$41-$5A`, ASCII `A-Z` -> `$C1-$DA` +- `{` -> `$DB`, `}` -> `$DD` +- newline -> `$0D` + +``` +c1541 work.d81 -write src.c "squares.c,s" +c1541 work.d81 -read "hello.c,s" out.c # inspect an existing source +``` + +Filenames end in `.c`; compiler output ends in `.o`; Shell programs end in `.sh`. + +## The Shell (C64) + +Run it with `load"*",8` + `run` (or autostart `shell`). Prompt is `$`. + +| Command | Meaning | +|---------|---------| +| `bye` | exit to BASIC | +| `l [pat]` / `ls [pat]` | list work disk / system disk | +| `rm file` | scratch a file on the work disk | +| `mv a b` | rename | +| `pr file` / `pr >> file` | list a file to screen / to printer (device 4) | +| `disk cmd` | send a DOS command (`disk n0:name,id` formats) | +| `work [dev drive]` | show or set the work disk | +| `load cmd` | load a command without running it | +| `ed [file]`, `ced [file]` | editor / syntax-checking editor | +| `cc [-p] file.c` | compile | +| `trim file.o` | optimise the object file (3-8% smaller) | +| `link [-s [addr]]` | link | + +Programs named `X.sh` run under the Shell by typing `X` plus arguments; +stdin/stdout can be redirected to disk files like UNIX. Included utilities: +`sort [-n]`, `find [-x] [-n] [-f] pattern [files]`, `wfreq`, `format`, `print`, +`trim`. + +## Editor (ed / ced) + +`ced` = `ed` plus a `CHeck` command that checks C syntax. Start it with +`ced file.c`. + +Edit mode: cursor keys move; F1/F2 page down/up; F3/F4 search down/up; F5 cut +(Shift+Run/Stop starts select mode, cursor keys set the range, Return accepts); +F6 paste; F7/F8 end/start of line; Clr/Home bottom of buffer; Shift+Clr/Home +top; Shift+Return opens a line; **Run/Stop enters command mode**. + +Command mode: `dir`, `disk cmd`, `GEt file`, `PUt file`, `PRint`, `GOto buf`, +`List`, `CHeck` (ced only), `QUIT`, `CLEAR [buf]`, `/str` (search), +`/dog/cat` (replace next), `/dog/cat/` (replace all). Return goes back +to edit mode. + +Saving: Run/Stop, then `PUt filename.c`, Return, then `QUIT`. + +## Build pipeline + +``` +$ cc squares.c -> squares.o (name must end in .c) +$ trim squares.o (optional) +$ link -s (BASIC-runnable) or link (Shell program, name must end .sh) +> squares.o object file(s), one per line +> ^ up-arrow: link stdlib.l + syslib.l automatically +> empty RETURN = done +output file name: squares +``` + +- `cc` prompts "insert source/compiler/object disk": with a single image just + press RETURN. +- `link -s` with no address links at the start of BASIC, so + `load"squares",8` + `run` works. With an address (`link -s $c000` or decimal) + you must `load"x",8,1` and `SYS` the address. +- `link` takes **no filename argument**; names are entered at the `>` prompt. + `x` + RETURN aborts. +- The linker only accepts `.o`, `.obj`, `.l` names. Maths functions need + `math.l` too (`>math.l`) and `#include `. +- **"unresolved external references"** (`printf`, `c$start`, ...) = you forgot + the `^` library step. +- **"63, file exists"** = scratch the old output first (`rm squares`, or + `c1541 -delete`). +- The up-arrow key is `^` in VICE `-keybuf`. + +## Library quick reference + +Link `stdlib.l` and `syslib.l` via `^`; `math.l` by name. + +- **stdio.h**: `printf fprintf sprintf scanf fscanf sscanf getchar putchar gets + puts fgets fputs getc putc fgetc fputc getw putw fopen freopen fclose fread + fwrite feof ferror`. `EOF` is -1, `NULL` 0, `stdin` 1, `stdout` 2; `FILE` is + an int. +- **printf** conversions: `%d %u %o %x %f %s %c %%`, with `-`, width, + `.precision`, `*`. Max width 64. +- **scanf** conversions: `%d %x %o %f %s %c`, `*` suppress, width. +- **fopen** modes `"r"`/`"w"`; program files `"name,p"`; replace with + `"@0:name,p"`. Always check `ferror()` after fopen. +- **BASIC-style I/O**: `open(fileno, device, secaddr, name)`, `close(fileno)`; + use file numbers 5-9 alongside fopen. +- **Strings** (`#include `): `strcat strncat strcmp strncmp strcpy + strncpy strlen index rindex`. +- **Memory**: `malloc calloc realloc free` (declare `char *malloc();`); + `bcopy bcmp bzero ffs`. +- **Classification**: `isalpha isupper islower isdigit isalnum isspace ispunct + isprint iscntrl isascii`. +- **Convert**: `atoi atof abs fabs`. **Random**: `random()`, `srandom(seed)`. + **Sort**: `qsort(base, n, size, cmp)`. +- **Control**: `exit() abort() setjmp longjmp` (`#include `), + `highmem(addr)`. +- **Directory** (`#include `): `opendir readdir rewinddir closedir`. +- **Math** (`#include `, link `math.l`): `sin cos tan asin acos atan + atan2 sinh cosh tanh exp log log10 pow sqrt floor ceil modf frexp ldexp hypot + cabs`. + +## Memory and machine-language interface (C64) + +- Compiled programs and the heap/stack end at `$D000` by default (highest usable + address `$CFFF`). `highmem(0xffff)` lets a program use all RAM; call + `highmem(0xc000)` if you place machine code at `$C000`. +- Zero page `20-33`, `43-74`, `253-254` (decimal) are reserved by the system; + `34-42`, `75-96`, `251-252` are scratch and safe for your ML routines. +- The tape buffer `$033C-$03FB` is used by the system; do not touch it. +- Call machine code with `sys(bank, address, &a, &x, &y)` (the manual's example + uses bank 15); A/X/Y are loaded from the pointers and written back afterwards; + it returns the carry flag. Example: + `while (*s++) sys(15, 0xffd2, s, &x, &y);` prints via KERNAL CHROUT. +- Pass multiple args to an ML routine by declaring one `float args;` parameter + so they stay contiguous, and hand the routine the address of that block in X/Y. + +## Running and testing under VICE + +```bat +rem compile +powerc-build.bat "cc squares.c\n" +rem link (delete any old output first: c1541 work.d81 -delete squares) +powerc-build.bat "link -s\nsquares.o\n^\n\nsquares\n" +rem run +x64sc -autostart "work.d81:squares" -autostart-warp +``` + +Work on a **copy** of `reference/POWERc.d81` (`examples/powerc-work.d81`); VICE +writes persist. Scripts: `examples/powerc-ced.bat`, `examples/powerc-build.bat`. +Full emulator details are in `skills/vice-emulator/SKILL.md` +("Automating Disk-Based Tools"). + +## Pitfalls checklist + +1. ANSI syntax (prototypes, `void`, `//` comments) will fail; use K&R and `/* */`. +2. `cc` needs the `.c` extension; `link` needs the `^` step and a fresh output name. +3. `\r` in `-keybuf` types a literal `r`; use `\n`. +4. Headers missing from the work disk give compile errors. +5. The 8-character identifier limit can make two long names collide. +6. `int` is 16-bit and right shifts do not sign-fill; do not rely on sign extension. +7. Source files are PETSCII, so host text files must be converted before use. diff --git a/skills/vice-emulator/SKILL.md b/skills/vice-emulator/SKILL.md index 941a03b..14ac5fe 100644 --- a/skills/vice-emulator/SKILL.md +++ b/skills/vice-emulator/SKILL.md @@ -363,3 +363,71 @@ For this repository: 5. Stepping without choosing between `step`, `next`, and `return` can waste a lot of time in KERNAL code 6. Forgetting `device 8:` vs `device c:` can make you debug the wrong CPU when true drive emulation is involved 7. Snapshots are convenient, but the manual warns against treating them as stable long-term artifacts + +--- + +## Automating Disk-Based Tools (Power C example) + +Learned while driving Power C (`reference/POWERc.d81`) end to end: write source, compile, link, run. Scripts: `examples/powerc-ced.bat`, `examples/powerc-build.bat`. Manual: `reference/Power_C_Instruction_Manual.pdf`. + +### Mounting and autostarting a disk + +```bat +x64sc.exe -8 "disk.d81" -autostart "disk.d81:shell" -autostart-warp -warp -keybuf "ced\n" +``` + +- `-8 ` mounts in drive 8; `-autostart ":"` loads and runs a named file from the image. +- `-autostart-warp` only lasts until autostart finishes. Add `-warp` to stay in warp for slow compile/link steps (about 14x, shown as ~1400% cpu in the status bar). +- Run a built program with `-autostart "work.d81:squares"`. + +### `-keybuf` rules + +- `\n` is RETURN. `\r` is typed as a literal `r` (you get `cedr`). +- `^` produces the C64 up-arrow character (PETSCII `$5E`), needed by the Power C linker. +- Keybuf is fed after autostart completes, so it works for the first shell command and for follow-up input queued in the same string (e.g. linker prompts). +- Pass the string as one quoted argument. From PowerShell use `Start-Process cmd -ArgumentList '/c',$bat,'"link\nfoo.o\n"'`; nested quoting through `cmd //c` from bash silently failed to launch. +- Do not edit these strings with `sed` (`\n` becomes a real newline); rewrite the file instead. + +### Do not type into the window with SendKeys + +`SendKeys` to the VICE window drops and repeats characters (`link squares` became `lk quaes`), especially in warp. Use `-keybuf` for anything scripted. SendKeys with a 250-400 ms delay per character is only a fallback for answering a prompt that arrives mid-run, such as the final Return or a file name. + +### Seeing what happened + +There is no console output from the emulated machine. Capture the VICE window with a PowerShell `CopyFromScreen` of its `GetWindowRect`, then read the PNG. Sleep long enough first (compile about 60 s at 100%, much less in warp). + +### Do not modify the reference image + +Copy it first (`cp reference/POWERc.d81 examples/powerc-work.d81`). Writes made inside VICE persist in the `.d81`. + +### Putting a source file on the disk without the editor + +`ced` is an interactive full-screen editor; scripting it through keybuf is fragile. Instead write the file with `c1541` as a SEQ file in the format Power C expects: + +- PETSCII, CR (`$0D`) line endings. +- ASCII `a-z` -> `$41-$5A`, ASCII `A-Z` -> `$C1-$DA`, `{` -> `$DB`, `}` -> `$DD`. +- `c1541 work.d81 -write src.c "squares.c,s"`. Read existing files back with `-read "hello.c,s" out`. + +### Power C workflow (C64, single drive) + +1. `cc squares.c` - the full name with `.c` is required (`cc squares` gives "not a C source file"). Produces `squares.o`. The compiler asks "insert compiler disk / object disk"; with one image just press RETURN. +2. `link -s` - `-s` links to run from BASIC (no `-s` makes a Shell program, name must end in `.sh`). `link` takes no filename argument. +3. Linker prompts `>`: enter `squares.o`, then `^` (up arrow) to pull in the libraries (`stdlib.l`, `syslib.l`, which are on the same d81), then an empty RETURN to finish. Then answer `output file name:` with `squares`. +4. The linker fails with "63, file exists" if the output name already exists. Scratch it first: `c1541 work.d81 -delete squares`. +5. Unresolved references such as `printf` or `c$start` mean the `^` library step was skipped. +6. Run it: `-autostart "work.d81:squares"`, or `bye` in the Shell, then `load"squares",8` and `run`. + +One-shot build (after deleting any old output): + +```bat +powerc-build.bat "link -s\nsquares.o\n^\n\nsquares\n" +``` + +Compile step: `powerc-build.bat "cc squares.c\n"` (then press RETURN at the disk prompts). + +### Power C quirks worth remembering + +- No bit fields; static pointers can only be initialised with strings. +- `int`, `short`, `long` are all 2 bytes; `float` is 5 bytes (BASIC format); identifiers are significant to 8 characters. +- Standard headers (`stdio.h`) must be on the same disk as the source. +- Shell commands: `l` (list work disk), `ls` (system disk), `rm`, `mv`, `pr`, `disk`, `work`, `bye`; `ed`/`ced` editors, `cc`, `link`, `trim`.