25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

7.2KB

Building a vi/vim clone for the C64 with Power C — hard-won notes

Everything below was verified on this machine (GTK3VICE 3.10, reference/POWERc.d81), mostly by first getting it wrong. Read this before touching the build or the keyboard code.

The build: one session, start to finish

Compile every unit, link, then bye out of the Power C shell, then load and run from BASIC — all typed into one emulator session from one -keybuf string:

cc vim.c        <RETURN><RETURN><RETURN>  ... padding
cc vscreen.c    <RETURN><RETURN><RETURN>  ... padding
link -s
vim.o
vscreen.o
^                                         up-arrow: stdlib.l + syslib.l
                                          empty RETURN: done
vim                                       output name
bye
load"vim",8,1
run
  • Never run two emulators against the same .d81. Both hold the whole image in memory and write it back on exit, so the second one's exit silently erases what the first wrote. Symptoms: a compile that “produced nothing”, a linker that “found no object”, a directory that changes between two c1541 -list calls.
  • Use Start-Process -Wait (or .NET Process + WaitForExit) so a step cannot overlap the next.
  • -keybuf-delay is wall-clock milliseconds per keystroke while the machine runs at warp, so 900 ms per key is plenty for the machine.
  • cc clears the keyboard buffer when it starts, so its three disk-prompt answers have to arrive after the prompts appear, not before. That is why a 300 ms burst of RETURNs fails: the answers are eaten and cc sits at “insert object disk” forever. 900 ms plus ~20 spare RETURNs after each cc works. Spare RETURNs at the shell prompt are harmless.
  • The linker's > prompts are line-based and it loads each object before asking again, so no padding RETURNs may sit between the object names — an empty line means “end of object list”. ^ is the up-arrow key and pulls in the libraries; forget it and you get “unresolved external references” (Main, SYS, MALLOC, ...).
  • -exitscreenshot will not overwrite an existing file, and -limitcycles is what ends the session (VICE exits with status 1 and prints “cycle limit reached” — that is normal).
  • Keep the disk image a bare filename and run from its folder: VICE splits its image:file autostart argument on a colon, so a full path is ambiguous.

PowerShell traps

  • Start-Process -ArgumentList joins the array with spaces and quotes nothing. "-keybuf", "cc vim.c\n" therefore arrived as two arguments and VICE silently never started (no window, no screenshot). Build the command line yourself and hand it to System.Diagnostics.ProcessStartInfo.Arguments, escaping inner " as \" (see build.ps1).
  • PowerShell does not interpret \n in a double-quoted string, so "cc $u.c\n" really is backslash-n — which is what VICE wants.

Power C specifics

  • cc name.c → name.o; it prompts for source disk / compiler disk / object disk. The Shell prompt is $; bye returns to BASIC.
  • The linker refuses to overwrite — “63, file exists” — and then gives up quietly. Build on a fresh copy of the system disk instead.
  • link -s with no address links at the start of BASIC: the file begins 01 08, holds 10 SYS 2062, and $080E is a JMP to the C runtime, so load"name",8,1 + run works.
  • Source files on disk are SEQ PETSCII with CR line endings: a-z → $41-$5A, A-Z → $C1-$DA, CRLF → CR, { | } ~ → $DB $DC $DD $DE (ASCII $7B-$7F plus $60).
  • Do not write ||. It needs | = LOGO+* = PETSCII $DC, and cc dies with “*** illegal character” part-way through the file. Use nested ifs. (&& is fine.)
  • EOF is not declared unless you #include <stdio.h>; test if (ch < 0) break; instead.
  • K&R only: no prototypes, no void, no // comments, identifiers significant to 8 characters, int is 16 bits, chars are unsigned and not sign-extended. Functions returning int need no declaration; declare char *malloc();.
  • peek()/poke() are C128-only — use pointer casts.
  • malloc() (declared char *malloc();) happily gives 19000 bytes. Compiled programs and the heap end at $D000 by default.
  • Keep the readable .c authoritative and .pet derived. In one bad session the converted PETSCII copy ended up written over the host .c file (every letter uppercased, CR line endings). It still compiled — the conversion round-trips — but the source was gone.

Calling machine language: sys() and the keyboard

  • sys(bank, address, &a, &x, &y) is documented as int bank; unsigned address; char *aptr, *xptr, *yptr; with bank from 0 to 15 (BASIC 7 style). Calling KERNAL GETIN through it crashed the program; the crash warm-starts the C64, which clears the screen, so the only evidence was a blank blue screen and READY..
  • The library's getchar() is line buffered: it echoes through the KERNAL's screen editor and only hands the characters over after RETURN. Useless for an editor.
  • The answer: read the KERNAL keyboard buffer directly. $C6 holds how many characters are queued and they sit from $0277. The KERNAL's IRQ handler scans the matrix and fills that buffer, so this gets every real press as it happens, with no line buffering and no echo. Pop by shifting the rest down and decrementing $C6.
  • Clear $91 at startup. Bit 7 is the RUN/STOP flag and it can already be set when the program starts; treating that as “Esc” made the editor's first keypress a phantom and swallowed the filename.
  • VICE's -keybuf feeds that same buffer, so a program reading $C6 can be driven from the command line — but the keys race the program's start: type a filename and it can be eaten by BASIC before the program runs.

C64 screen facts the editor relies on

  • Screen matrix $0400, colour RAM $D800 (both directly writable).
  • $D018 = $17 → screen at $0400, character set at $1800 (lower/upper case), so PETSCII $41-$5A draws as real lowercase and $C1-$DA as uppercase. Default is $15 (uppercase/graphics at $1000) — restore it on exit.
  • PETSCII → screen code: $20-$3F unchanged, $40-$5F minus $40, $60-$7F minus $20, $A0-$BF minus $40, $C0-$FF minus $80.
  • Reverse video is the high bit: screen code + $80. That is the cursor block and the status row.
  • $00CC = 0 stops the KERNAL cursor blinking over the editor's own cursor.
  • Arrow keys are PETSCII $11 down, $91 up, $1D right, $9D left; RETURN $0D, DEL $14.

Debugging technique that paid off

  • -exitscreenshot + -limitcycles gives one png per run; read it directly.
  • A crash warm-starts the machine: screen cleared, READY., evidence gone. So probe in steps — write a marker to $0400 (p[0] = 2 shows as B) and end with for (;;) { } instead of going on. Whatever is on screen when it hangs is the last step that worked.
  • Prove the toolchain before debugging your own code: a six-line program that pokes the top row and spins.
  • Keep a screenshot of every build step (shots\cc-*.png, link.png) and make the script fail per step (“cc vim.c produced no object”) instead of at the end, or a compile failure looks like a linker failure.

Powered by TurnKey Linux.