1
0

edit.c 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  1. // Simple text editor
  2. /* global structure of program:
  3. - check first argument, exit if none
  4. - read file from first argument into mem
  5. - using a rectangular buffer (uninitialized 2d array, pointer to FILE_MEMORY_ADDR) with a max linewidth
  6. - while reading file, fill line until newline or MAX_LINE_WIDTH (if max, then error and exit)
  7. - add zeros until max_line_width after newline character
  8. - if current line number >= MAX_LINES: error out of mem and exit
  9. - use a >8bit code for indicating end of file, so print can stop
  10. - keep track of cursor x and y, which corresponds to line and pos in mem buffer
  11. - need for fast (asm!) memory move operations for inserting and deleting line based stuff (within the same line does not matter that much)
  12. - need for fast (asm!) print function which prints TEXT_WINDOW_HEIGHT lines with an xscroll offset (with palette indication that a line extends right)
  13. - end of file should have a special palette color
  14. - header line as first line with filename, mode, etc (like nano)
  15. - keep track of window (start) cursor (only y, line number)
  16. - when cursor y < window cursor, window cursor --, if y > window cursor + TEXT_WINDOW_HEIGHT, window cursor++ (check on start and end of file boundaries)
  17. - after each operation, print from window start
  18. - print cursor on top of character, by inverting the palette
  19. - escape for menu
  20. - save
  21. - function to
  22. - exit
  23. */
  24. /* future possible features that are not that hard to implement
  25. - use control key (which should be implemented in hid fifo first!)
  26. - then, ctrl s and other stuff can be implemented
  27. - selection mode (set start and end x,y), use a color palette to indicate selection
  28. - copy or delete (or cut, by copy and then delete) the selection into a separate buffer (check on bounds!)
  29. - paste by inserting from the buffer
  30. - text search which selects the matches one by one (different mode again)
  31. - tab inserts 2 spaces, maybe stops at even x for alignment?
  32. - insert mode that overwrites characters instead of moving them
  33. */
  34. #define word char
  35. #include "lib/math.c"
  36. #include "lib/sys.c"
  37. #include "lib/gfx.c"
  38. #include "lib/stdlib.c"
  39. #include "lib/fs.c"
  40. #define FBUF_ADDR 0x440000 // location of input file buffer
  41. #define FILE_MEMORY_ADDR 0x480000 // location of file content
  42. #define MAX_LINE_WIDTH 240 // max width of line in mem buffer, multiple of 40 (screen width)
  43. #define MAX_LINES 8192 // maximum number of lines to prevent out of memory writes (8192*256*4 = 8MiB)
  44. #define TEXT_WINDOW_HEIGHT 20 // number of lines to print on screen
  45. #define SCREEN_WIDTH 40
  46. #define SCREEN_HEIGHT 25
  47. // Palettes
  48. #define PALETTE_CURSOR 1
  49. #define PALETTE_NEWLINE 2
  50. #define PALETTE_EOF 3
  51. #define PALETTE_SELECTED 4
  52. #define PALETTE_HEADER 5
  53. // HID
  54. #define ARROW_LEFT 256
  55. #define ARROW_RIGHT 257
  56. #define ARROW_UP 258
  57. #define ARROW_DOWN 259
  58. #define BTN_INSERT 260
  59. #define BTN_HOME 261
  60. #define BTN_PAGEUP 262
  61. #define BTN_END 263
  62. #define BTN_PAGEDOWN 264
  63. char infilename[96]; // input filename to edit the contents of
  64. char headerText[SCREEN_WIDTH]; // text on header (first line)
  65. // Buffer for file contents in memory
  66. char (*mem)[MAX_LINE_WIDTH] = (char (*)[MAX_LINE_WIDTH]) FILE_MEMORY_ADDR; // 2d array containing all lines of the input file
  67. // Buffer for file reading
  68. // Length of buffer always should be less than 65536, since this is the maximum FS_readFile can do in a single call
  69. #define FBUF_LEN 4096
  70. #define EOF -1
  71. char *inputBuffer = (char*) FBUF_ADDR; //[FBUF_LEN];
  72. word inbufStartPos = 0; // where in the file the buffer starts
  73. word inbufCursor = 0; // where in the buffer we currently are working
  74. word lastLineNumber = 0;
  75. // Cursors
  76. word windowYscroll = 0; // vertical line offset for drawing window
  77. word windowXscroll = 0; // horizontal position offset for drawing window
  78. word userCursorX = 0; // char position within line, of user cursor
  79. word userCursorY = 0; // line of user cursor
  80. // Opens file for reading
  81. // requires full paths
  82. // returns 1 on success
  83. word fopenRead()
  84. {
  85. if (infilename[0] != '/')
  86. {
  87. BDOS_PrintConsole("E: Filename should be a full path\n");
  88. return 0;
  89. }
  90. FS_close(); // to be sure
  91. // convert to uppercase
  92. strToUpper(infilename);
  93. // init read buffer
  94. inbufStartPos = 0; // start at 0
  95. inbufCursor = 0; // start at 0
  96. // if the resulting path is correct (can be file or directory)
  97. if (FS_sendFullPath(infilename) == FS_ANSW_USB_INT_SUCCESS)
  98. {
  99. // if we can successfully open the file (not directory)
  100. if (FS_open() == FS_ANSW_USB_INT_SUCCESS)
  101. {
  102. FS_setCursor(0); // set cursor to start
  103. return 1;
  104. }
  105. else
  106. {
  107. return 0;
  108. }
  109. }
  110. else
  111. {
  112. return 0;
  113. }
  114. return 0;
  115. }
  116. // opens file for writing by recreating it
  117. // should not be called before fopenRead, therefore exits on error
  118. word fopenWrite()
  119. {
  120. if (infilename[0] != '/')
  121. {
  122. BDOS_PrintConsole("E: Filename should be a full path\n");
  123. exit();
  124. }
  125. FS_close(); // to be sure
  126. // convert to uppercase
  127. strToUpper(infilename);
  128. // if current path is correct (can be file or directory)
  129. if (FS_sendFullPath(infilename) == FS_ANSW_USB_INT_SUCCESS)
  130. {
  131. // create the file
  132. if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS)
  133. {
  134. //BDOS_PrintConsole("File created\n");
  135. // open again and start at 0
  136. FS_sendFullPath(infilename);
  137. FS_open();
  138. FS_setCursor(0); // set cursor to start
  139. return 1;
  140. }
  141. else
  142. {
  143. BDOS_PrintConsole("E: Could not create file\n");
  144. exit();
  145. }
  146. }
  147. else
  148. {
  149. BDOS_PrintConsole("E: Invalid path\n");
  150. exit();
  151. }
  152. exit();
  153. return 0;
  154. }
  155. // Writes data of given length to opened file
  156. void fputData(char* datBuf, word lenOfData)
  157. {
  158. if (lenOfData == 0)
  159. {
  160. return;
  161. }
  162. word bytesWritten = 0;
  163. // loop until all bytes are sent
  164. while (bytesWritten != lenOfData)
  165. {
  166. word partToSend = lenOfData - bytesWritten;
  167. // send in parts of 0xFFFF
  168. if (partToSend > 0xFFFF)
  169. partToSend = 0xFFFF;
  170. // write away
  171. if (FS_writeFile((datBuf +bytesWritten), partToSend) != FS_ANSW_USB_INT_SUCCESS)
  172. BDOS_PrintConsole("write error\n");
  173. // Update the amount of bytes sent
  174. bytesWritten += partToSend;
  175. }
  176. }
  177. // Write mem to file
  178. void writeMemToFile()
  179. {
  180. fopenWrite(); // open file for writing
  181. word y;
  182. for (y = 0; y <= lastLineNumber; y++)
  183. {
  184. word lineEndPos = 0;
  185. while (mem[y][lineEndPos] != '\n' && mem[y][lineEndPos] != EOF)
  186. {
  187. lineEndPos++;
  188. if (lineEndPos == MAX_LINE_WIDTH)
  189. {
  190. exitRoutine();
  191. BDOS_PrintConsole("E: could not find end of line\n");
  192. FS_close();
  193. exit();
  194. }
  195. }
  196. if (mem[y][lineEndPos] == EOF)
  197. {
  198. fputData(mem[y], lineEndPos);
  199. FS_close();
  200. return;
  201. }
  202. lineEndPos++; // include the newline token
  203. fputData(mem[y], lineEndPos);
  204. }
  205. FS_close();
  206. }
  207. // returns the current char at cursor within the opened file (EOF if end of file)
  208. // increments the cursor
  209. word fgetc()
  210. {
  211. // workaround for missing compiler check for ALU constants > 11 bit
  212. word stdioBufLen = FBUF_LEN;
  213. if (inbufCursor >= FBUF_LEN || inbufCursor == 0) // we are at the end of the buffer (or it is not initialized yet)
  214. {
  215. // get filesize
  216. word sizeOfFile = FS_getFileSize();
  217. // if we cannot completely fill the buffer:
  218. if (inbufStartPos + stdioBufLen > sizeOfFile)
  219. {
  220. // fill the buffer, and append with EOF token
  221. FS_readFile(inputBuffer, sizeOfFile - inbufStartPos, 0);
  222. inputBuffer[sizeOfFile - inbufStartPos] = EOF;
  223. }
  224. else
  225. {
  226. // fill buffer again
  227. FS_readFile(inputBuffer, FBUF_LEN, 0);
  228. }
  229. inbufStartPos += stdioBufLen; // for the next fill
  230. inbufCursor = 0; // start at the beginning of the buffer again
  231. }
  232. // return from the buffer, and increment
  233. char gotchar = inputBuffer[inbufCursor];
  234. inbufCursor++;
  235. // BDOS_PrintcConsole(gotchar); // useful for debugging
  236. return gotchar;
  237. }
  238. // reads a line from the input file
  239. word readFileLine()
  240. {
  241. char c = fgetc();
  242. char cprev = c;
  243. word currentChar = 0;
  244. // stop on EOF or newline or max line width reached
  245. while (c != EOF && c != '\n' && currentChar < (MAX_LINE_WIDTH-1))
  246. {
  247. mem[lastLineNumber][currentChar] = c;
  248. currentChar++;
  249. cprev = c;
  250. c = fgetc();
  251. }
  252. // error when line was too long
  253. if (c != EOF && c != '\n' && currentChar >= (MAX_LINE_WIDTH-1))
  254. {
  255. BDOS_PrintConsole("E: line is too long\n");
  256. exit();
  257. }
  258. mem[lastLineNumber][currentChar] = c; // add EOF or \n
  259. // append line with zeros
  260. while (currentChar < (MAX_LINE_WIDTH-1))
  261. {
  262. currentChar++;
  263. char fillerChar = 0;
  264. if (c == EOF)
  265. {
  266. fillerChar = EOF;
  267. }
  268. mem[lastLineNumber][currentChar] = fillerChar;
  269. }
  270. return c;
  271. }
  272. void readInputFile()
  273. {
  274. while (readFileLine() != EOF)
  275. {
  276. if (lastLineNumber >= MAX_LINES)
  277. {
  278. BDOS_PrintConsole("E: File too large\n");
  279. exit();
  280. }
  281. else
  282. {
  283. lastLineNumber++;
  284. }
  285. }
  286. FS_close();
  287. }
  288. // Applies window to mem and prints on screen (using window plane)
  289. void printWindow()
  290. {
  291. printHeader();
  292. char* vramWindowpattern = (char*) 0xC01420;
  293. word vramPaletteOffset = 2048;
  294. word lastLineFound = 0;
  295. word x, y;
  296. for (y = 0; y < SCREEN_HEIGHT-1; y++) // leave first line open
  297. {
  298. for (x = 0; x < SCREEN_WIDTH; x++)
  299. {
  300. word c = mem[y + windowYscroll][x + windowXscroll];
  301. if (c == '\n')
  302. {
  303. word vramOffset = GFX_WindowPosFromXY(x, y+1);
  304. *(vramWindowpattern + vramOffset) = 7;
  305. *(vramWindowpattern + vramOffset + vramPaletteOffset) = PALETTE_NEWLINE;
  306. }
  307. else if (c == EOF)
  308. {
  309. // print empty space if EOF is already drawn
  310. if (lastLineFound)
  311. {
  312. word vramOffset = GFX_WindowPosFromXY(x, y+1);
  313. *(vramWindowpattern + vramOffset) = 0;
  314. *(vramWindowpattern + vramOffset + vramPaletteOffset) = 0;
  315. }
  316. else
  317. {
  318. word vramOffset = GFX_WindowPosFromXY(x, y+1);
  319. *(vramWindowpattern + vramOffset) = 4;
  320. *(vramWindowpattern + vramOffset + vramPaletteOffset) = PALETTE_EOF;
  321. lastLineFound = 1; // notify, but continue rendering the line
  322. }
  323. }
  324. else
  325. {
  326. word vramOffset = GFX_WindowPosFromXY(x, y+1);
  327. *(vramWindowpattern + vramOffset) = c;
  328. *(vramWindowpattern + vramOffset + vramPaletteOffset) = 0;
  329. }
  330. }
  331. if (lastLineFound)
  332. {
  333. return;
  334. }
  335. }
  336. }
  337. // Remove cursor from screen (bg plane)
  338. void clearCursor()
  339. {
  340. word vramCursorOffset = GFX_BackgroundPosFromXY(userCursorX, userCursorY+1);
  341. char* vramBackgroundpalette = (char*) 0xC00C20;
  342. *(vramBackgroundpalette + vramCursorOffset) = 0;
  343. }
  344. // Print the cursor on screen (using background plane)
  345. void printCursor()
  346. {
  347. word vramCursorOffset = GFX_BackgroundPosFromXY(userCursorX, userCursorY+1);
  348. char* vramBackgroundpalette = (char*) 0xC00C20;
  349. *(vramBackgroundpalette + vramCursorOffset) = PALETTE_CURSOR;
  350. }
  351. // Print the header on screen (using window plane)
  352. void printHeader()
  353. {
  354. GFX_printWindowColored(headerText, SCREEN_WIDTH, 0, PALETTE_HEADER);
  355. }
  356. void setPalettes()
  357. {
  358. word* paletteAddress = (word*) 0xC00400;
  359. paletteAddress[PALETTE_CURSOR] = 0x7 << 24;
  360. paletteAddress[PALETTE_NEWLINE] = 12;
  361. paletteAddress[PALETTE_EOF] = 224;
  362. paletteAddress[PALETTE_HEADER] = (0x51 << 24) + 0xDA;
  363. }
  364. // Make sure the cursor stays left from the newline
  365. void fixCursorToNewline()
  366. {
  367. word xpos = userCursorX+windowXscroll;
  368. word ypos = userCursorY+windowYscroll;
  369. word newLinePos = 0;
  370. while (mem[ypos][newLinePos] != '\n' && mem[ypos][newLinePos] != EOF)
  371. {
  372. newLinePos++;
  373. if (newLinePos == MAX_LINE_WIDTH)
  374. {
  375. exitRoutine();
  376. BDOS_PrintConsole("E: could not find newline\n");
  377. exit();
  378. }
  379. }
  380. clearCursor();
  381. word appliedScolling = 0;
  382. while (xpos > newLinePos)
  383. {
  384. if (userCursorX > 0)
  385. {
  386. userCursorX--;
  387. }
  388. else
  389. {
  390. if (windowXscroll > 0)
  391. {
  392. windowXscroll--;
  393. appliedScolling = 1;
  394. }
  395. else
  396. {
  397. exitRoutine();
  398. BDOS_PrintConsole("E: scroll left limit reached\n");
  399. exit();
  400. }
  401. }
  402. xpos = userCursorX+windowXscroll;
  403. }
  404. if (appliedScolling)
  405. {
  406. printWindow();
  407. }
  408. }
  409. // Move the cursor to targetPos of the previous line, from the left
  410. // Assumes targetPos is valid and ypos is not 0
  411. // Does not print window at the end, caller should do that
  412. void gotoPosOfPrevLine(word targetPos)
  413. {
  414. clearCursor();
  415. userCursorX = 0;
  416. windowXscroll = 0;
  417. if (userCursorY > 0)
  418. {
  419. userCursorY--;
  420. }
  421. else
  422. {
  423. if (windowYscroll > 0)
  424. {
  425. windowYscroll--;
  426. }
  427. else
  428. {
  429. exitRoutine();
  430. BDOS_PrintConsole("E: could not move up a line\n");
  431. exit();
  432. }
  433. }
  434. word xpos = 0;
  435. word ypos = userCursorY+windowYscroll;
  436. while (xpos != targetPos)
  437. {
  438. if (userCursorX < SCREEN_WIDTH-1)
  439. {
  440. userCursorX++;
  441. }
  442. else
  443. {
  444. if (windowXscroll < MAX_LINE_WIDTH-SCREEN_WIDTH)
  445. {
  446. windowXscroll++;
  447. }
  448. }
  449. xpos = userCursorX + windowXscroll;
  450. if (xpos == MAX_LINE_WIDTH)
  451. {
  452. exitRoutine();
  453. BDOS_PrintConsole("E: target out of bounds\n");
  454. exit();
  455. }
  456. }
  457. printCursor();
  458. }
  459. // Move the cursor to the newline character of the previous line
  460. void gotoEndOfPrevLine()
  461. {
  462. userCursorX = 0;
  463. windowXscroll = 0;
  464. word appliedScolling = 0;
  465. if (userCursorY > 0)
  466. {
  467. userCursorY--;
  468. }
  469. else
  470. {
  471. if (windowYscroll > 0)
  472. {
  473. windowYscroll--;
  474. appliedScolling = 1;
  475. }
  476. else
  477. {
  478. // don't need to do anything when on the first line
  479. return;
  480. }
  481. }
  482. word xpos = 0;
  483. word ypos = userCursorY+windowYscroll;
  484. while (mem[ypos][xpos] != '\n' && mem[ypos][xpos] != EOF)
  485. {
  486. if (userCursorX < SCREEN_WIDTH-1)
  487. {
  488. userCursorX++;
  489. }
  490. else
  491. {
  492. if (windowXscroll < MAX_LINE_WIDTH-SCREEN_WIDTH)
  493. {
  494. windowXscroll++;
  495. appliedScolling = 1;
  496. }
  497. }
  498. xpos = userCursorX + windowXscroll;
  499. if (xpos == MAX_LINE_WIDTH)
  500. {
  501. exitRoutine();
  502. BDOS_PrintConsole("E: could not find newline\n");
  503. exit();
  504. }
  505. }
  506. if (appliedScolling)
  507. {
  508. printWindow();
  509. }
  510. }
  511. void moveCursorDown()
  512. {
  513. // skip if current line contains EOF
  514. word ypos = userCursorY+windowYscroll;
  515. word i;
  516. for (i = 0; i < MAX_LINE_WIDTH; i++)
  517. {
  518. if (mem[ypos][i] == '\n')
  519. {
  520. break;
  521. }
  522. if (mem[ypos][i] == EOF)
  523. {
  524. return;
  525. }
  526. }
  527. clearCursor();
  528. if (userCursorY < SCREEN_HEIGHT-2)
  529. {
  530. userCursorY++;
  531. }
  532. else
  533. {
  534. if (windowYscroll < lastLineNumber-(SCREEN_HEIGHT-2))
  535. {
  536. windowYscroll++;
  537. printWindow();
  538. }
  539. }
  540. fixCursorToNewline();
  541. printCursor();
  542. }
  543. void moveCursorUp()
  544. {
  545. clearCursor();
  546. if (userCursorY > 0)
  547. {
  548. userCursorY--;
  549. }
  550. else
  551. {
  552. if (windowYscroll > 0)
  553. {
  554. windowYscroll--;
  555. printWindow();
  556. }
  557. }
  558. fixCursorToNewline();
  559. printCursor();
  560. }
  561. void moveCursorLeft()
  562. {
  563. clearCursor();
  564. if (userCursorX > 0)
  565. {
  566. userCursorX--;
  567. }
  568. else
  569. {
  570. if (windowXscroll > 0)
  571. {
  572. windowXscroll--;
  573. printWindow();
  574. }
  575. else
  576. {
  577. // move to previous line
  578. windowXscroll = MAX_LINE_WIDTH - SCREEN_WIDTH;
  579. userCursorX = SCREEN_WIDTH - 1;
  580. gotoEndOfPrevLine();
  581. }
  582. }
  583. fixCursorToNewline();
  584. printCursor();
  585. }
  586. void moveCursorRight()
  587. {
  588. word xpos = userCursorX + windowXscroll;
  589. if (mem[userCursorY+windowYscroll][xpos] == EOF)
  590. {
  591. // do nothing at EOF
  592. return;
  593. }
  594. clearCursor();
  595. if (mem[userCursorY+windowYscroll][xpos] == '\n')
  596. {
  597. // if we are at the end of the line, move to next line
  598. word appliedScolling = windowXscroll;
  599. windowXscroll = 0;
  600. userCursorX = 0;
  601. moveCursorDown();
  602. if (appliedScolling)
  603. {
  604. printWindow();
  605. }
  606. }
  607. else
  608. {
  609. if (userCursorX < SCREEN_WIDTH-1)
  610. {
  611. userCursorX++;
  612. }
  613. else
  614. {
  615. if (windowXscroll < MAX_LINE_WIDTH-SCREEN_WIDTH)
  616. {
  617. windowXscroll++;
  618. printWindow();
  619. }
  620. }
  621. }
  622. printCursor();
  623. }
  624. void pageUp()
  625. {
  626. clearCursor();
  627. word i;
  628. for (i = 0; i < SCREEN_HEIGHT-2; i++)
  629. {
  630. if (userCursorY > 0)
  631. {
  632. userCursorY--;
  633. }
  634. else
  635. {
  636. if (windowYscroll > 0)
  637. {
  638. windowYscroll--;
  639. }
  640. }
  641. }
  642. printWindow();
  643. fixCursorToNewline();
  644. printCursor();
  645. }
  646. void pageDown()
  647. {
  648. clearCursor();
  649. word lastLineFound = 0;
  650. word i;
  651. for (i = 0; i < SCREEN_HEIGHT-2; i++)
  652. {
  653. if (lastLineFound)
  654. {
  655. break;
  656. }
  657. // skip if current line contains EOF
  658. word ypos = userCursorY+windowYscroll;
  659. word i;
  660. for (i = 0; i < MAX_LINE_WIDTH; i++)
  661. {
  662. if (mem[ypos][i] == '\n')
  663. {
  664. break;
  665. }
  666. if (mem[ypos][i] == EOF)
  667. {
  668. lastLineFound = 1;
  669. break;
  670. }
  671. }
  672. if (userCursorY < SCREEN_HEIGHT-2)
  673. {
  674. userCursorY++;
  675. }
  676. else
  677. {
  678. if (windowYscroll < lastLineNumber-(SCREEN_HEIGHT-2))
  679. {
  680. windowYscroll++;
  681. }
  682. }
  683. }
  684. printWindow();
  685. fixCursorToNewline();
  686. printCursor();
  687. }
  688. void home()
  689. {
  690. clearCursor();
  691. userCursorX = 0;
  692. windowXscroll = 0;
  693. printWindow();
  694. printCursor();
  695. }
  696. void end()
  697. {
  698. clearCursor();
  699. userCursorX = 0;
  700. windowXscroll = 0;
  701. word xpos = 0;
  702. word ypos = userCursorY+windowYscroll;
  703. while (mem[ypos][xpos] != '\n' && mem[ypos][xpos] != EOF)
  704. {
  705. if (userCursorX < SCREEN_WIDTH-1)
  706. {
  707. userCursorX++;
  708. }
  709. else
  710. {
  711. if (windowXscroll < MAX_LINE_WIDTH-SCREEN_WIDTH)
  712. {
  713. windowXscroll++;
  714. }
  715. }
  716. xpos = userCursorX + windowXscroll;
  717. if (xpos == MAX_LINE_WIDTH)
  718. {
  719. exitRoutine();
  720. BDOS_PrintConsole("E: could not find newline\n");
  721. exit();
  722. }
  723. }
  724. printWindow();
  725. printCursor();
  726. }
  727. void removeCurrentLine()
  728. {
  729. word ypos = userCursorY+windowYscroll;
  730. // move all lines below ypos up by one, starting at beginning
  731. word i;
  732. for (i = ypos+1; i <= lastLineNumber; i++)
  733. {
  734. memcpy(mem[i-1], mem[i], MAX_LINE_WIDTH);
  735. }
  736. lastLineNumber--;
  737. }
  738. void insertNewLine()
  739. {
  740. word ypos = userCursorY+windowYscroll;
  741. if (lastLineNumber == MAX_LINES-1)
  742. {
  743. // ignore if we reached the memory limit
  744. return;
  745. }
  746. clearCursor();
  747. // move all lines below ypos down by one, starting at the end
  748. word i;
  749. for (i = lastLineNumber; i > ypos; i--)
  750. {
  751. memcpy(mem[i+1], mem[i], MAX_LINE_WIDTH);
  752. }
  753. lastLineNumber++;
  754. // move everything right from cursor to new line
  755. // this should include the newline/EOF automatically
  756. word newLineTmpCursor = 0;
  757. word xpos = userCursorX+windowXscroll;
  758. while(xpos < MAX_LINE_WIDTH)
  759. {
  760. mem[ypos+1][newLineTmpCursor] = mem[ypos][xpos];
  761. mem[ypos][xpos] = 0;
  762. newLineTmpCursor++;
  763. xpos++;
  764. }
  765. // insert newline character at current line
  766. xpos = userCursorX+windowXscroll;
  767. mem[ypos][xpos] = '\n';
  768. // move the cursor down to the start of the new line
  769. windowXscroll = 0;
  770. userCursorX = 0;
  771. moveCursorDown();
  772. printWindow(); // always needed in case of a new line
  773. printCursor();
  774. }
  775. // Insert character c at cursor
  776. void insertCharacter(char c)
  777. {
  778. word xpos = userCursorX+windowXscroll;
  779. word ypos = userCursorY+windowYscroll;
  780. memmove(&mem[ypos][xpos+1], &mem[ypos][xpos], MAX_LINE_WIDTH-(xpos+1));
  781. mem[ypos][xpos] = c;
  782. printWindow();
  783. moveCursorRight();
  784. }
  785. // Remove character at cursor
  786. void removeCharacter()
  787. {
  788. word xpos = userCursorX+windowXscroll;
  789. word ypos = userCursorY+windowYscroll;
  790. if (xpos > 0)
  791. {
  792. memmove(&mem[ypos][xpos-1], &mem[ypos][xpos], MAX_LINE_WIDTH-(xpos-1));
  793. mem[ypos][MAX_LINE_WIDTH-1] = 0; // remove new char at right of line
  794. printWindow();
  795. moveCursorLeft();
  796. }
  797. else
  798. {
  799. if (ypos > 0)
  800. {
  801. // append current line to previous line at newline
  802. word prevLinePasteLocation = 0;
  803. word newLinePos = 0;
  804. while (mem[ypos-1][newLinePos] != '\n')
  805. {
  806. newLinePos++;
  807. if (newLinePos == MAX_LINE_WIDTH)
  808. {
  809. exitRoutine();
  810. BDOS_PrintConsole("E: could not find newline\n");
  811. exit();
  812. }
  813. }
  814. // copy to overwrite the newline
  815. memcpy(&mem[ypos-1][newLinePos], &mem[ypos][0], MAX_LINE_WIDTH-(newLinePos));
  816. prevLinePasteLocation = newLinePos;
  817. // check if resulting line has a newline or EOF, else add to end
  818. newLinePos = 0;
  819. while (mem[ypos-1][newLinePos] != '\n' && mem[ypos-1][newLinePos] != EOF && newLinePos < MAX_LINE_WIDTH)
  820. {
  821. newLinePos++;
  822. if (newLinePos == MAX_LINE_WIDTH)
  823. {
  824. if (ypos == lastLineNumber)
  825. {
  826. mem[ypos-1][MAX_LINE_WIDTH-1] = EOF;
  827. }
  828. else
  829. {
  830. mem[ypos-1][MAX_LINE_WIDTH-1] = '\n';
  831. }
  832. }
  833. }
  834. // remove the current line
  835. removeCurrentLine();
  836. // move cursor to the place we pasted the line
  837. gotoPosOfPrevLine(prevLinePasteLocation);
  838. // update the view
  839. printWindow();
  840. }
  841. }
  842. }
  843. void addEditHeader()
  844. {
  845. headerText[29] = 'Y';
  846. headerText[30] = ':';
  847. itoa(userCursorY+windowYscroll, &headerText[31]);
  848. headerText[35] = 'X';
  849. headerText[36] = ':';
  850. itoa(userCursorX+windowXscroll, &headerText[37]);
  851. }
  852. void exitRoutine()
  853. {
  854. GFX_clearWindowtileTable();
  855. GFX_clearWindowpaletteTable();
  856. GFX_clearBGtileTable();
  857. GFX_clearBGpaletteTable();
  858. }
  859. int main()
  860. {
  861. // input file
  862. BDOS_GetArgN(1, infilename);
  863. // error if none
  864. if (infilename[0] == 0)
  865. {
  866. BDOS_PrintConsole("E: Missing filename\n");
  867. exit();
  868. }
  869. // Make full path if it is not already
  870. if (infilename[0] != '/')
  871. {
  872. char bothPath[96];
  873. bothPath[0] = 0;
  874. strcat(bothPath, BDOS_GetPath());
  875. if (bothPath[strlen(bothPath)-1] != '/')
  876. {
  877. strcat(bothPath, "/");
  878. }
  879. strcat(bothPath, infilename);
  880. strcpy(infilename, bothPath);
  881. }
  882. if (!fopenRead())
  883. {
  884. BDOS_PrintConsole("E: Could not open file\n");
  885. exit();
  886. }
  887. // Open the input file
  888. BDOS_PrintConsole("Opening ");
  889. BDOS_PrintConsole(infilename);
  890. BDOS_PrintConsole("...\n");
  891. readInputFile();
  892. strcpy(headerText, infilename);
  893. // init gfx
  894. GFX_clearWindowtileTable();
  895. GFX_clearWindowpaletteTable();
  896. GFX_clearBGtileTable();
  897. GFX_clearBGpaletteTable();
  898. setPalettes();
  899. addEditHeader();
  900. printWindow();
  901. printCursor();
  902. // main loop
  903. while (1)
  904. {
  905. if (HID_FifoAvailable())
  906. {
  907. word c = HID_FifoRead();
  908. switch (c)
  909. {
  910. case 27: // escape
  911. memcpy(&headerText[24], "Save? Type y/n/c", 16);
  912. printHeader();
  913. // ask for confirmation
  914. while (c != 'y' && c != 'n' && c != 'Y' && c != 'N' && c != 'c' && c != 'C')
  915. {
  916. // wait until a character is pressed
  917. while (HID_FifoAvailable() == 0);
  918. c = HID_FifoRead();
  919. }
  920. if (c == 'y' || c == 'Y')
  921. {
  922. writeMemToFile();
  923. exitRoutine();
  924. return 'q'; // exit
  925. }
  926. if (c == 'n' || c == 'N')
  927. {
  928. exitRoutine();
  929. return 'q'; // exit
  930. }
  931. memcpy(&headerText[24], " ", 16); // clear header text
  932. break;
  933. case ARROW_LEFT:
  934. moveCursorLeft();
  935. break;
  936. case ARROW_RIGHT:
  937. moveCursorRight();
  938. break;
  939. case ARROW_DOWN:
  940. moveCursorDown();
  941. break;
  942. case ARROW_UP:
  943. moveCursorUp();
  944. break;
  945. case BTN_HOME:
  946. home();
  947. break;
  948. case BTN_END:
  949. end();
  950. break;
  951. case BTN_PAGEUP:
  952. pageUp();
  953. break;
  954. case BTN_PAGEDOWN:
  955. pageDown();
  956. break;
  957. case 0x8: // backspace
  958. removeCharacter();
  959. break;
  960. case '\n':
  961. insertNewLine();
  962. break;
  963. case '\t':
  964. insertCharacter(' ');
  965. insertCharacter(' ');
  966. break;
  967. default:
  968. // default to inserting the character as text
  969. insertCharacter(c);
  970. break;
  971. }
  972. addEditHeader();
  973. printHeader();
  974. }
  975. }
  976. return 'q';
  977. }
  978. void interrupt()
  979. {
  980. // Handle all interrupts
  981. word i = getIntID();
  982. switch(i)
  983. {
  984. case INTID_TIMER1:
  985. timer1Value = 1; // Notify ending of timer1
  986. break;
  987. }
  988. }