|
|
@@ -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 <stdio.h> |
|
|
|
|
|
|
|
|
|
|
|
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 <x>` 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<F3>` (search), |
|
|
|
|
|
`/dog/cat<F3>` (replace next), `/dog/cat/<F3>` (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 <math.h>`. |
|
|
|
|
|
- **"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 <strings.h>`): `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 <setjmp.h>`), |
|
|
|
|
|
`highmem(addr)`. |
|
|
|
|
|
- **Directory** (`#include <dir.h>`): `opendir readdir rewinddir closedir`. |
|
|
|
|
|
- **Math** (`#include <math.h>`, 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. |