25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
9.1KB

  1. /* vim.c - a small vi/vim-style screen editor for the Commodore 64
  2. *
  3. * Core half of the program: text buffer, cursor motion, key handling
  4. * and saving. Drawing lives in vscreen.c because Power C's compiler
  5. * reports "program too big" when the whole editor is one unit. The two
  6. * halves share functions only - every piece of state stays private to
  7. * this file and is handed to draw() as arguments - because sharing
  8. * variables across Power C object files proved unreliable.
  9. *
  10. * Modal editing, like vi:
  11. *
  12. * COMMAND mode INSERT mode
  13. * i insert here any key type text
  14. * a append after char RETURN split the line
  15. * o open line below DEL backspace
  16. * O open line above RUN/STOP back to COMMAND mode
  17. * x delete character cursor keys move
  18. * h j k l or cursor keys CLR/HOME start of line
  19. * 0 start of line
  20. * $ end of line
  21. * : command line
  22. *
  23. * : commands
  24. * :w write "notes" to disk (SEQ file, drive 8)
  25. * :wq write and quit
  26. * :q quit
  27. *
  28. * Special C characters, on the same keys the Power C manual gives for ced:
  29. *
  30. * { SHIFT + } SHIFT - \ English pound key
  31. * ~ C= (logo) P _ C= (logo) @ | C= (logo) *
  32. *
  33. * The Kernal already produces the codes Power C expects for these
  34. * ($DB, $DD, $5C, $AF, $A4, $DF - read from the key tables at $EB81),
  35. * so insert mode only has to accept them. The character ROM has no
  36. * brace or bar glyphs, so on screen they appear as the nearest glyph.
  37. *
  38. * ESCAPE is the C64 RUN/STOP key, which the Kernal reports in the
  39. * zero-page STOP flag at $91.
  40. *
  41. * The text buffer holds 2000 bytes with 13 ($0D) as the line break.
  42. * Keys come from the Kernal keyboard buffer at $0277, count at $c6.
  43. *
  44. * Build with build.bat in this folder:
  45. *
  46. * cc vim.c -> vim.o
  47. * cc vscreen.c -> vscreen.o
  48. * link -s
  49. * > vim.o
  50. * > vscreen.o
  51. * > ^ (stdlib.l + syslib.l)
  52. * >
  53. * output file name: vim
  54. *
  55. * Then run with: x64sc -autostart "vim.d81:vim"
  56. * or from BASIC: LOAD"VIM",8,1 : RUN
  57. *
  58. * Note: this Power C build rejects both $7C and $DC as "illegal
  59. * character" for the | operator, so nested tests are used instead of ||.
  60. */
  61. #include <stdio.h>
  62. static char text[2000]; /* document, 13 = line break */
  63. static int tlen; /* bytes in use */
  64. static int pos; /* cursor, byte offset into text */
  65. static int mode; /* 0 command, 1 insert, 2 : prompt */
  66. static char fname[17]; /* file name used by :w */
  67. static char cmd[9]; /* text after the ':' */
  68. static int cmdlen;
  69. static int quit;
  70. static int msgcode; /* message for draw(): 0 none, 1-4 */
  71. static char *defnam = "notes";
  72. /* ---------- provided by vscreen.c ---------- */
  73. int draw();
  74. int clrscr();
  75. /* ---------- buffer helpers ---------- */
  76. static lstart(n) /* offset of the first byte of line n */
  77. int n;
  78. {
  79. int i;
  80. int c;
  81. if (n <= 0) return 0;
  82. c = 0;
  83. i = 0;
  84. while (i < tlen) {
  85. if (text[i] == 13) {
  86. c = c + 1;
  87. if (c == n) return i + 1;
  88. }
  89. i = i + 1;
  90. }
  91. return tlen;
  92. }
  93. static lcount() /* number of line breaks */
  94. {
  95. int i;
  96. int c;
  97. c = 0;
  98. for (i = 0; i < tlen; i++)
  99. if (text[i] == 13) c = c + 1;
  100. return c;
  101. }
  102. static lnof() /* line holding the cursor */
  103. {
  104. int i;
  105. int c;
  106. c = 0;
  107. for (i = 0; i < pos && i < tlen; i++)
  108. if (text[i] == 13) c = c + 1;
  109. return c;
  110. }
  111. static intend(o) /* offset of the line end */
  112. int o;
  113. {
  114. int e;
  115. e = o;
  116. while (e < tlen && text[e] != 13) e = e + 1;
  117. return e;
  118. }
  119. static inschr(ch)
  120. int ch;
  121. {
  122. int i;
  123. if (tlen >= 1999) {
  124. msgcode = 1;
  125. return;
  126. }
  127. for (i = tlen; i > pos; i--)
  128. text[i] = text[i - 1];
  129. text[pos] = ch;
  130. tlen = tlen + 1;
  131. pos = pos + 1;
  132. }
  133. static delat()
  134. {
  135. int i;
  136. if (pos >= tlen) return;
  137. for (i = pos; i < tlen - 1; i++)
  138. text[i] = text[i + 1];
  139. tlen = tlen - 1;
  140. }
  141. static backsp()
  142. {
  143. if (pos <= 0) return;
  144. pos = pos - 1;
  145. delat();
  146. }
  147. /* ---------- cursor motion ---------- */
  148. static movleft()
  149. {
  150. if (pos > 0) pos = pos - 1;
  151. }
  152. static movright()
  153. {
  154. if (pos < tlen) pos = pos + 1;
  155. }
  156. static movdown()
  157. {
  158. int ln;
  159. int cs;
  160. int o;
  161. int e;
  162. ln = lnof();
  163. if (ln + 1 > lcount()) return;
  164. cs = pos - lstart(ln);
  165. o = lstart(ln + 1);
  166. e = intend(o);
  167. pos = o + cs;
  168. if (pos > e) pos = e;
  169. }
  170. static movup()
  171. {
  172. int ln;
  173. int cs;
  174. int o;
  175. int e;
  176. ln = lnof();
  177. if (ln <= 0) return;
  178. cs = pos - lstart(ln);
  179. o = lstart(ln - 1);
  180. e = intend(o);
  181. pos = o + cs;
  182. if (pos > e) pos = e;
  183. }
  184. static linehome()
  185. {
  186. pos = lstart(lnof());
  187. }
  188. static lineend()
  189. {
  190. pos = intend(lstart(lnof()));
  191. }
  192. /* ---------- saving ---------- */
  193. static savfil()
  194. {
  195. FILE *f;
  196. int i;
  197. f = fopen(fname, "w");
  198. if (!f) {
  199. msgcode = 2;
  200. return;
  201. }
  202. if (ferror(f)) {
  203. msgcode = 2;
  204. fclose(f);
  205. return;
  206. }
  207. for (i = 0; i < tlen; i++) fputc(text[i], f);
  208. fclose(f);
  209. msgcode = 3;
  210. }
  211. /* ---------- : command line ---------- */
  212. static docmd()
  213. {
  214. if (cmdlen == 0) return;
  215. if (cmd[0] == 'w') {
  216. if (cmdlen > 1 && cmd[1] == 'q') {
  217. savfil();
  218. quit = 1;
  219. return;
  220. }
  221. savfil();
  222. return;
  223. }
  224. if (cmd[0] == 'q') {
  225. quit = 1;
  226. return;
  227. }
  228. msgcode = 4;
  229. }
  230. /* ---------- key handlers ---------- */
  231. static insk(k)
  232. int k;
  233. {
  234. if (k == 3) { /* ESC: leave insert mode */
  235. mode = 0;
  236. if (pos > 0) pos = pos - 1;
  237. return;
  238. }
  239. if (k == 13) { inschr(13); return; }
  240. if (k == 20) { backsp(); return; }
  241. if (k == 157) { movleft(); return; }
  242. if (k == 29) { movright(); return; }
  243. if (k == 145) { movup(); return; }
  244. if (k == 17) { movdown(); return; }
  245. if (k == 19) { linehome(); return; }
  246. if (k >= 32 && k < 128) { inschr(k); return; }
  247. if (k == 219) { inschr(k); return; } /* SHIFT + : { */
  248. if (k == 221) { inschr(k); return; } /* SHIFT - : } */
  249. if (k == 223) { inschr(k); return; } /* logo * : | */
  250. if (k == 175) { inschr(k); return; } /* logo P : ~ */
  251. if (k == 164) { inschr(k); return; } /* logo @ : _ */
  252. }
  253. static cmdk(k)
  254. int k;
  255. {
  256. if (k >= 65 && k <= 90) k = k + 32; /* letters arrive as PETSCII $41-$5A */
  257. if (k == ':') {
  258. mode = 2;
  259. cmdlen = 0;
  260. cmd[0] = 0;
  261. return;
  262. }
  263. if (k == 'i') { mode = 1; return; }
  264. if (k == 'a') {
  265. if (pos < tlen && text[pos] != 13) pos = pos + 1;
  266. mode = 1;
  267. return;
  268. }
  269. if (k == 'o') {
  270. pos = intend(lstart(lnof()));
  271. inschr(13);
  272. mode = 1;
  273. return;
  274. }
  275. if (k == 'O') {
  276. pos = lstart(lnof());
  277. inschr(13);
  278. pos = pos - 1;
  279. mode = 1;
  280. return;
  281. }
  282. if (k == 'x') { delat(); return; }
  283. if (k == 'h') { movleft(); return; }
  284. if (k == 157) { movleft(); return; }
  285. if (k == 'l') { movright(); return; }
  286. if (k == 29) { movright(); return; }
  287. if (k == 'j') { movdown(); return; }
  288. if (k == 17) { movdown(); return; }
  289. if (k == 'k') { movup(); return; }
  290. if (k == 145) { movup(); return; }
  291. if (k == '0') { linehome(); return; }
  292. if (k == 19) { linehome(); return; }
  293. if (k == '$') { lineend(); return; }
  294. }
  295. /* ---------- keyboard ---------- */
  296. static getkey() /* one byte from the Kernal keyboard
  297. buffer, $0277, count at $c6 */
  298. {
  299. char *cnt;
  300. char *buf;
  301. int i;
  302. int k;
  303. cnt = (char *) 198;
  304. buf = (char *) 631;
  305. if (*cnt == 0) return 0;
  306. if (*cnt > 10) {
  307. *cnt = 0;
  308. return 0;
  309. }
  310. k = buf[0];
  311. for (i = 0; i < 9; i++) buf[i] = buf[i + 1];
  312. *cnt = *cnt - 1;
  313. return k;
  314. }
  315. static waitkey()
  316. {
  317. char *stop;
  318. int k;
  319. for (;;) {
  320. stop = (char *) 145; /* $91: RUN/STOP flag */
  321. if (*stop) {
  322. *stop = 0;
  323. return 3;
  324. }
  325. k = getkey();
  326. if (k) return k;
  327. }
  328. }
  329. /* ---------- main loop ---------- */
  330. main()
  331. {
  332. int k;
  333. int i;
  334. tlen = 0;
  335. pos = 0;
  336. mode = 0;
  337. cmdlen = 0;
  338. quit = 0;
  339. msgcode = 0;
  340. i = 0;
  341. while (defnam[i]) {
  342. fname[i] = defnam[i];
  343. i = i + 1;
  344. }
  345. fname[i] = 0;
  346. while (!quit) {
  347. draw(text, tlen, pos, mode, fname, cmd, cmdlen, msgcode);
  348. msgcode = 0;
  349. k = waitkey();
  350. if (mode == 2) {
  351. if (k == 13) {
  352. mode = 0;
  353. docmd();
  354. }
  355. else if (k == 20) {
  356. if (cmdlen > 0) cmdlen = cmdlen - 1;
  357. }
  358. else if (k >= 32 && cmdlen < 8) {
  359. if (k >= 65 && k <= 90) k = k + 32;
  360. cmd[cmdlen] = k;
  361. cmdlen = cmdlen + 1;
  362. }
  363. continue;
  364. }
  365. if (mode == 1) {
  366. insk(k);
  367. continue;
  368. }
  369. cmdk(k);
  370. }
  371. clrscr();
  372. printf("bye\n");
  373. }

Powered by TurnKey Linux.