|
- /* vim.c - a small vi/vim-style screen editor for the Commodore 64
- *
- * Core half of the program: text buffer, cursor motion, key handling
- * and saving. Drawing lives in vscreen.c because Power C's compiler
- * reports "program too big" when the whole editor is one unit. The two
- * halves share functions only - every piece of state stays private to
- * this file and is handed to draw() as arguments - because sharing
- * variables across Power C object files proved unreliable.
- *
- * Modal editing, like vi:
- *
- * COMMAND mode INSERT mode
- * i insert here any key type text
- * a append after char RETURN split the line
- * o open line below DEL backspace
- * O open line above RUN/STOP back to COMMAND mode
- * x delete character cursor keys move
- * h j k l or cursor keys CLR/HOME start of line
- * 0 start of line
- * $ end of line
- * : command line
- *
- * : commands
- * :w write "notes" to disk (SEQ file, drive 8)
- * :wq write and quit
- * :q quit
- *
- * Special C characters, on the same keys the Power C manual gives for ced:
- *
- * { SHIFT + } SHIFT - \ English pound key
- * ~ C= (logo) P _ C= (logo) @ | C= (logo) *
- *
- * The Kernal already produces the codes Power C expects for these
- * ($DB, $DD, $5C, $AF, $A4, $DF - read from the key tables at $EB81),
- * so insert mode only has to accept them. The character ROM has no
- * brace or bar glyphs, so on screen they appear as the nearest glyph.
- *
- * ESCAPE is the C64 RUN/STOP key, which the Kernal reports in the
- * zero-page STOP flag at $91.
- *
- * The text buffer holds 2000 bytes with 13 ($0D) as the line break.
- * Keys come from the Kernal keyboard buffer at $0277, count at $c6.
- *
- * Build with build.bat in this folder:
- *
- * cc vim.c -> vim.o
- * cc vscreen.c -> vscreen.o
- * link -s
- * > vim.o
- * > vscreen.o
- * > ^ (stdlib.l + syslib.l)
- * >
- * output file name: vim
- *
- * Then run with: x64sc -autostart "vim.d81:vim"
- * or from BASIC: LOAD"VIM",8,1 : RUN
- *
- * Note: this Power C build rejects both $7C and $DC as "illegal
- * character" for the | operator, so nested tests are used instead of ||.
- */
-
- #include <stdio.h>
-
- static char text[2000]; /* document, 13 = line break */
- static int tlen; /* bytes in use */
- static int pos; /* cursor, byte offset into text */
- static int mode; /* 0 command, 1 insert, 2 : prompt */
- static char fname[17]; /* file name used by :w */
- static char cmd[9]; /* text after the ':' */
- static int cmdlen;
- static int quit;
- static int msgcode; /* message for draw(): 0 none, 1-4 */
-
- static char *defnam = "notes";
-
- /* ---------- provided by vscreen.c ---------- */
-
- int draw();
- int clrscr();
-
- /* ---------- buffer helpers ---------- */
-
- static lstart(n) /* offset of the first byte of line n */
- int n;
- {
- int i;
- int c;
-
- if (n <= 0) return 0;
- c = 0;
- i = 0;
- while (i < tlen) {
- if (text[i] == 13) {
- c = c + 1;
- if (c == n) return i + 1;
- }
- i = i + 1;
- }
- return tlen;
- }
-
- static lcount() /* number of line breaks */
- {
- int i;
- int c;
-
- c = 0;
- for (i = 0; i < tlen; i++)
- if (text[i] == 13) c = c + 1;
- return c;
- }
-
- static lnof() /* line holding the cursor */
- {
- int i;
- int c;
-
- c = 0;
- for (i = 0; i < pos && i < tlen; i++)
- if (text[i] == 13) c = c + 1;
- return c;
- }
-
- static intend(o) /* offset of the line end */
- int o;
- {
- int e;
-
- e = o;
- while (e < tlen && text[e] != 13) e = e + 1;
- return e;
- }
-
- static inschr(ch)
- int ch;
- {
- int i;
-
- if (tlen >= 1999) {
- msgcode = 1;
- return;
- }
- for (i = tlen; i > pos; i--)
- text[i] = text[i - 1];
- text[pos] = ch;
- tlen = tlen + 1;
- pos = pos + 1;
- }
-
- static delat()
- {
- int i;
-
- if (pos >= tlen) return;
- for (i = pos; i < tlen - 1; i++)
- text[i] = text[i + 1];
- tlen = tlen - 1;
- }
-
- static backsp()
- {
- if (pos <= 0) return;
- pos = pos - 1;
- delat();
- }
-
- /* ---------- cursor motion ---------- */
-
- static movleft()
- {
- if (pos > 0) pos = pos - 1;
- }
-
- static movright()
- {
- if (pos < tlen) pos = pos + 1;
- }
-
- static movdown()
- {
- int ln;
- int cs;
- int o;
- int e;
-
- ln = lnof();
- if (ln + 1 > lcount()) return;
- cs = pos - lstart(ln);
- o = lstart(ln + 1);
- e = intend(o);
- pos = o + cs;
- if (pos > e) pos = e;
- }
-
- static movup()
- {
- int ln;
- int cs;
- int o;
- int e;
-
- ln = lnof();
- if (ln <= 0) return;
- cs = pos - lstart(ln);
- o = lstart(ln - 1);
- e = intend(o);
- pos = o + cs;
- if (pos > e) pos = e;
- }
-
- static linehome()
- {
- pos = lstart(lnof());
- }
-
- static lineend()
- {
- pos = intend(lstart(lnof()));
- }
-
- /* ---------- saving ---------- */
-
- static savfil()
- {
- FILE *f;
- int i;
-
- f = fopen(fname, "w");
- if (!f) {
- msgcode = 2;
- return;
- }
- if (ferror(f)) {
- msgcode = 2;
- fclose(f);
- return;
- }
- for (i = 0; i < tlen; i++) fputc(text[i], f);
- fclose(f);
- msgcode = 3;
- }
-
- /* ---------- : command line ---------- */
-
- static docmd()
- {
- if (cmdlen == 0) return;
- if (cmd[0] == 'w') {
- if (cmdlen > 1 && cmd[1] == 'q') {
- savfil();
- quit = 1;
- return;
- }
- savfil();
- return;
- }
- if (cmd[0] == 'q') {
- quit = 1;
- return;
- }
- msgcode = 4;
- }
-
- /* ---------- key handlers ---------- */
-
- static insk(k)
- int k;
- {
- if (k == 3) { /* ESC: leave insert mode */
- mode = 0;
- if (pos > 0) pos = pos - 1;
- return;
- }
- if (k == 13) { inschr(13); return; }
- if (k == 20) { backsp(); return; }
- if (k == 157) { movleft(); return; }
- if (k == 29) { movright(); return; }
- if (k == 145) { movup(); return; }
- if (k == 17) { movdown(); return; }
- if (k == 19) { linehome(); return; }
- if (k >= 32 && k < 128) { inschr(k); return; }
- if (k == 219) { inschr(k); return; } /* SHIFT + : { */
- if (k == 221) { inschr(k); return; } /* SHIFT - : } */
- if (k == 223) { inschr(k); return; } /* logo * : | */
- if (k == 175) { inschr(k); return; } /* logo P : ~ */
- if (k == 164) { inschr(k); return; } /* logo @ : _ */
- }
-
- static cmdk(k)
- int k;
- {
- if (k >= 65 && k <= 90) k = k + 32; /* letters arrive as PETSCII $41-$5A */
- if (k == ':') {
- mode = 2;
- cmdlen = 0;
- cmd[0] = 0;
- return;
- }
- if (k == 'i') { mode = 1; return; }
- if (k == 'a') {
- if (pos < tlen && text[pos] != 13) pos = pos + 1;
- mode = 1;
- return;
- }
- if (k == 'o') {
- pos = intend(lstart(lnof()));
- inschr(13);
- mode = 1;
- return;
- }
- if (k == 'O') {
- pos = lstart(lnof());
- inschr(13);
- pos = pos - 1;
- mode = 1;
- return;
- }
- if (k == 'x') { delat(); return; }
- if (k == 'h') { movleft(); return; }
- if (k == 157) { movleft(); return; }
- if (k == 'l') { movright(); return; }
- if (k == 29) { movright(); return; }
- if (k == 'j') { movdown(); return; }
- if (k == 17) { movdown(); return; }
- if (k == 'k') { movup(); return; }
- if (k == 145) { movup(); return; }
- if (k == '0') { linehome(); return; }
- if (k == 19) { linehome(); return; }
- if (k == '$') { lineend(); return; }
- }
-
- /* ---------- keyboard ---------- */
-
- static getkey() /* one byte from the Kernal keyboard
- buffer, $0277, count at $c6 */
- {
- char *cnt;
- char *buf;
- int i;
- int k;
-
- cnt = (char *) 198;
- buf = (char *) 631;
- if (*cnt == 0) return 0;
- if (*cnt > 10) {
- *cnt = 0;
- return 0;
- }
- k = buf[0];
- for (i = 0; i < 9; i++) buf[i] = buf[i + 1];
- *cnt = *cnt - 1;
- return k;
- }
-
- static waitkey()
- {
- char *stop;
- int k;
-
- for (;;) {
- stop = (char *) 145; /* $91: RUN/STOP flag */
- if (*stop) {
- *stop = 0;
- return 3;
- }
- k = getkey();
- if (k) return k;
- }
- }
-
- /* ---------- main loop ---------- */
-
- main()
- {
- int k;
- int i;
-
- tlen = 0;
- pos = 0;
- mode = 0;
- cmdlen = 0;
- quit = 0;
- msgcode = 0;
-
- i = 0;
- while (defnam[i]) {
- fname[i] = defnam[i];
- i = i + 1;
- }
- fname[i] = 0;
-
- while (!quit) {
- draw(text, tlen, pos, mode, fname, cmd, cmdlen, msgcode);
- msgcode = 0;
- k = waitkey();
- if (mode == 2) {
- if (k == 13) {
- mode = 0;
- docmd();
- }
- else if (k == 20) {
- if (cmdlen > 0) cmdlen = cmdlen - 1;
- }
- else if (k >= 32 && cmdlen < 8) {
- if (k >= 65 && k <= 90) k = k + 32;
- cmd[cmdlen] = k;
- cmdlen = cmdlen + 1;
- }
- continue;
- }
- if (mode == 1) {
- insk(k);
- continue;
- }
- cmdk(k);
- }
-
- clrscr();
- printf("bye\n");
- }
|