/* vscreen.c - screen and keyboard for the c64 vim clone. * * The editor writes straight into the screen matrix at $0400 and colour ram at * $d800 rather than going through the kernal, and it selects the lower/upper * case character set at $1800 so that petscii $41-$5a draws as real lowercase. * * Everything here returns int, so the other compilation unit can call these * without declaring them (power c is K&R C). */ char *scr; /* screen matrix, $0400 */ char *colr; /* colour ram, $d800 */ /* petscii byte -> screen code */ s2v(p) int p; { if (p >= 0xc0) return p - 0x80; if (p >= 0xa0) return p - 0x40; if (p >= 0x60) return p - 0x20; if (p >= 0x40) return p - 0x40; return p; } /* raw screen code at row r, column c */ sraw(r, c, v) int r, c, v; { int i; if (r < 0) return 0; if (r > 24) return 0; if (c < 0) return 0; if (c > 39) return 0; i = r * 40 + c; scr[i] = v; colr[i] = 1; return 0; } /* one petscii character */ spc(r, c, p) int r, c, p; { sraw(r, c, s2v(p)); return 0; } /* one petscii character, inverted: the cursor and the status row */ srpc(r, c, p) int r, c, p; { sraw(r, c, s2v(p) + 128); return 0; } /* a reversed space, for the cursor sitting past the end of a line */ sblk(r, c) int r, c; { sraw(r, c, 160); return 0; } /* a zero terminated petscii string */ sps(r, c, s) int r, c; char *s; { while (*s) spc(r, c++, *s++); return 0; } /* n blanks from (r,c) */ sclr(r, c, n) int r, c, n; { while (n-- > 0) sraw(r, c++, 32); return 0; } /* take over the machine: black on blue, lower case, no blinking cursor */ sinit() { int i; char *p; scr = (char *) 0x0400; colr = (char *) 0xd800; p = (char *) 0xd018; *p = 0x17; p = (char *) 0xd020; *p = 0; p = (char *) 0xd021; *p = 6; p = (char *) 0x00cc; /* kernal cursor blink: non-zero = off */ *p = 1; p = (char *) 0x0091; /* run/stop flag: drop whatever boot left */ *p = 0; p = (char *) 0x028a; /* key repeat: c64 default, cursor keys */ *p = 0; /* only (the power c shell leaves all on) */ for (i = 0; i < 1000; i++) { scr[i] = 32; colr[i] = 1; } return 0; } /* give the machine back the way basic expects it */ sdone() { char *p; char a, x, y; p = (char *) 0xd018; *p = 0x15; p = (char *) 0xd020; *p = 14; p = (char *) 0xd021; *p = 6; /* do not return to basic: power c owns zero page 43-74, which is where basic keeps its program and variable pointers, so basic carries on with garbage and prints a mangled error instead of READY. restart the machine through the kernal reset instead - banner and READY, like switching on. the file has been saved (or abandoned with :q!) by now. */ a = 0; x = 0; y = 0; sys(0xfce2, &a, &x, &y); /* never comes back */ return 0; } /* ---------------------------------------------------------------- keyboard * * Keys come straight out of the kernal's keyboard buffer: $c6 says how many * are waiting and they queue from $0277. The kernal's own irq handler scans * the keyboard matrix and fills that buffer, so this gets each press as it * happens - and it also catches anything typed before the program started. * * (GETIN through sys() once "crashed" - but that was sys() called with the * c128 bank argument; on the c64 it is sys(addr, &a, &x, &y).) And the library's getchar() is line buffered - it * echoes through the screen editor and only hands characters over after RETURN. */ /* next key, or 0 if none is waiting. the pop runs in machine code with irqs masked, so the kernal cannot append a key half way through the shift */ skeyp() { return mlkey(); } /* wait for a key */ skey() { int k; for (;;) { k = skeyp(); if (k != 0) return k; } }