shell.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. /*
  2. * Shell library
  3. * Contains shell functions
  4. */
  5. // uses fs.c
  6. // uses hidfifo.c
  7. // uses gfx.c
  8. // uses stdlib.c
  9. // Max length of a single command
  10. #define SHELL_CMD_MAX_LENGTH 128
  11. // The number of commands to remember
  12. #define SHELL_CMD_HISTORY_LENGTH 32
  13. // Address of user program (already defined in BDOS.c)
  14. //#define RUN_ADDR 0x400000
  15. // Chunk size for reading files and programs
  16. // NOTE: must be dividable by 4
  17. #define SHELL_FILE_READ_CHUNK_SIZE 512
  18. #define SHELL_PROGRAM_READ_CHUNK_SIZE 32768
  19. // The current command that is being typed
  20. char SHELL_command[SHELL_CMD_MAX_LENGTH];
  21. word SHELL_commandIdx = 0; // index in current command
  22. word SHELL_promptCursorPos = 0;
  23. // History of commands
  24. char SHELL_history[SHELL_CMD_HISTORY_LENGTH][SHELL_CMD_MAX_LENGTH];
  25. word SHELL_historyPtr = 0; // index of next entry in history
  26. word SHELL_historySelectIdx = 0; // index of selected entry in history
  27. word SHELL_historyMovedBackwards = 0; // number of times that the user moved backwards in history
  28. word SHELL_historyLength = 0; // number of filled entries in history
  29. // Appends current CMD to history, ignores empty commands
  30. void SHELL_historyAppend()
  31. {
  32. // ignore empty command
  33. if (SHELL_command[0] == 0)
  34. {
  35. return;
  36. }
  37. // wrap around if full, overwriting the oldest entries
  38. if (SHELL_historyPtr == SHELL_CMD_HISTORY_LENGTH)
  39. {
  40. SHELL_historyPtr = 0;
  41. }
  42. strcpy(SHELL_history[SHELL_historyPtr], SHELL_command);
  43. SHELL_historyPtr++;
  44. // sync currently selected with the latest command
  45. SHELL_historySelectIdx = SHELL_historyPtr;
  46. // no need to update length if the history is already full
  47. if (SHELL_historyLength < SHELL_CMD_HISTORY_LENGTH)
  48. {
  49. SHELL_historyLength++;
  50. }
  51. }
  52. void SHELL_historyGoBack()
  53. {
  54. // ignore if we have gone fully back in time
  55. if (SHELL_historyMovedBackwards < SHELL_historyLength)
  56. {
  57. SHELL_historyMovedBackwards++;
  58. // go back in history, wrap around select idx
  59. if (SHELL_historySelectIdx == 0)
  60. {
  61. SHELL_historySelectIdx = SHELL_CMD_HISTORY_LENGTH;
  62. }
  63. else
  64. {
  65. SHELL_historySelectIdx--;
  66. }
  67. SHELL_setCommand(SHELL_history[SHELL_historySelectIdx]);
  68. }
  69. }
  70. void SHELL_historyGoForwards()
  71. {
  72. // only if we are in the past
  73. if (SHELL_historyMovedBackwards > 0)
  74. {
  75. SHELL_historyMovedBackwards--;
  76. // go forward in history, wrap around select idx
  77. if (SHELL_historySelectIdx == SHELL_CMD_HISTORY_LENGTH)
  78. {
  79. SHELL_historySelectIdx = 0;
  80. }
  81. else
  82. {
  83. SHELL_historySelectIdx++;
  84. }
  85. SHELL_setCommand(SHELL_history[SHELL_historySelectIdx]);
  86. // clear command if back in present
  87. if (SHELL_historyMovedBackwards == 0)
  88. {
  89. // use backspaces to clear the text on screen
  90. while (SHELL_commandIdx > 0)
  91. {
  92. GFX_PrintcConsole(0x8);
  93. SHELL_commandIdx--;
  94. }
  95. SHELL_clearCommand();
  96. }
  97. }
  98. }
  99. // Clears the current command in memory
  100. void SHELL_clearCommand()
  101. {
  102. SHELL_command[0] = 0;
  103. SHELL_commandIdx = 0;
  104. }
  105. // Clears current command and replaces it with cmd
  106. void SHELL_setCommand(char* cmd)
  107. {
  108. if (cmd[0] == 0)
  109. {
  110. return;
  111. }
  112. // clear current command on screen by doing backspaces
  113. while (SHELL_commandIdx > 0)
  114. {
  115. GFX_PrintcConsole(0x8);
  116. SHELL_commandIdx--;
  117. }
  118. strcpy(SHELL_command, cmd);
  119. // set new shell cmd index
  120. SHELL_commandIdx = strlen(SHELL_command);
  121. // print new command
  122. GFX_PrintConsole(SHELL_command);
  123. }
  124. // Prints current display prompt
  125. void SHELL_print_prompt()
  126. {
  127. // TODO: if path > X chars, only show last X-1 chars with some character in front
  128. GFX_PrintConsole(SHELL_path);
  129. GFX_PrintConsole("> ");
  130. SHELL_promptCursorPos = GFX_cursor;
  131. }
  132. // Initialize shell
  133. void SHELL_init()
  134. {
  135. // clear current command
  136. SHELL_clearCommand();
  137. // clear screen
  138. GFX_clearWindowtileTable();
  139. GFX_clearWindowpaletteTable();
  140. GFX_cursor = 0;
  141. SHELL_print_prompt();
  142. }
  143. // Checks if p starts with cmd, followed by a space or a \0
  144. // Returns 1 if true, 0 otherwise
  145. word SHELL_commandCompare(char* p, char* cmd)
  146. {
  147. word similar = 1;
  148. word i = 0;
  149. while (cmd[i] != 0)
  150. {
  151. if (cmd[i] != p[i])
  152. {
  153. similar = 0;
  154. }
  155. i += 1;
  156. }
  157. if (similar)
  158. {
  159. similar = 0;
  160. if (p[i] == 0 || p[i] == ' ')
  161. similar = 1;
  162. }
  163. return similar;
  164. }
  165. // Returns the number of arguments of a command line
  166. // Does this by counting the number times a character is placed
  167. // directly after a space
  168. word SHELL_numberOfArguments(char* p)
  169. {
  170. word args = 0;
  171. word foundSpace = 0;
  172. word i = 0;
  173. while (p[i] != 0)
  174. {
  175. if (p[i] == ' ')
  176. {
  177. foundSpace = 1;
  178. }
  179. else
  180. {
  181. if (foundSpace)
  182. {
  183. // if character after space
  184. if (p[i] != 0)
  185. {
  186. args += 1;
  187. }
  188. foundSpace = 0;
  189. }
  190. }
  191. i += 1;
  192. }
  193. return args;
  194. }
  195. // Implementation of ldir command
  196. // Lists directory given in arg
  197. // Argument is passed in arg and should end with a \0 (not space)
  198. void SHELL_ldir(char* arg)
  199. {
  200. // backup current path
  201. strcpy(SHELL_pathBackup, SHELL_path);
  202. // add arg to path
  203. if (FS_changePath(arg) == FS_ANSW_USB_INT_SUCCESS)
  204. {
  205. // do listdir
  206. char *b = (char *) TEMP_ADDR;
  207. if (FS_listDir(SHELL_path, b) == FS_ANSW_USB_INT_SUCCESS)
  208. {
  209. GFX_PrintConsole(b);
  210. }
  211. // restore path
  212. strcpy(SHELL_path, SHELL_pathBackup);
  213. }
  214. else
  215. {
  216. GFX_PrintConsole("E: Invalid path\n");
  217. }
  218. }
  219. // Implementation of run command
  220. // Loads file to memory at RUN_ADDR and jumps to it
  221. // Argument is passed in arg and should end with a \0 or space
  222. // If useBin is set, it will look for the file in the /BIN folder
  223. void SHELL_runFile(char* arg, word useBin)
  224. {
  225. // backup current path
  226. strcpy(SHELL_pathBackup, SHELL_path);
  227. // replace space with \0
  228. word i = 0;
  229. word putBackSpaceIndex = 0; // and put back later for args
  230. while(*(arg+i) != ' ' && *(arg+i) != 0)
  231. {
  232. i++;
  233. }
  234. if (*(arg+i) == ' ')
  235. {
  236. putBackSpaceIndex = i;
  237. }
  238. *(arg+i) = 0;
  239. if (useBin)
  240. {
  241. strcpy(SHELL_path, "/BIN/");
  242. strcat(SHELL_path, arg);
  243. }
  244. else
  245. {
  246. // create full path using arg
  247. FS_getFullPath(arg);
  248. }
  249. // if the resulting path is correct (can be file or directory)
  250. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  251. {
  252. // if we can successfully open the file (not directory)
  253. if (FS_open() == FS_ANSW_USB_INT_SUCCESS)
  254. {
  255. word fileSize = FS_getFileSize();
  256. if ((unsigned int) fileSize <= (unsigned int) 0x300000)
  257. {
  258. if (FS_setCursor(0) == FS_ANSW_USB_INT_SUCCESS)
  259. {
  260. // read the file in chunks
  261. char *b = (char *) RUN_ADDR;
  262. word bytesSent = 0;
  263. GFX_PrintConsole("Loading");
  264. word loopCount = 0; // counter for animation
  265. // loop until all bytes are sent
  266. while (bytesSent != fileSize)
  267. {
  268. word partToSend = fileSize - bytesSent;
  269. // send in parts of SHELL_PROGRAM_READ_CHUNK_SIZE
  270. if ((unsigned int) partToSend > (unsigned int) SHELL_PROGRAM_READ_CHUNK_SIZE)
  271. partToSend = SHELL_PROGRAM_READ_CHUNK_SIZE;
  272. // read from usb to memory in word mode
  273. if (FS_readFile(b, partToSend, 1) != FS_ANSW_USB_INT_SUCCESS)
  274. uprintln("W: Error reading file\n");
  275. // indicate progress
  276. if (loopCount == 3)
  277. {
  278. GFX_PrintcConsole(0x8); // backspace
  279. GFX_PrintcConsole(0x8); // backspace
  280. GFX_PrintcConsole(0x8); // backspace
  281. loopCount = 0;
  282. }
  283. else
  284. {
  285. GFX_PrintcConsole('.');
  286. loopCount++;
  287. }
  288. // Update the amount of bytes sent
  289. bytesSent += partToSend;
  290. b += (partToSend>>2); // divide by 4 because one address is 4 bytes
  291. }
  292. // remove the dots
  293. for (loopCount; loopCount > 0; loopCount--)
  294. {
  295. GFX_PrintcConsole(0x8); // backspace
  296. }
  297. // close file after done
  298. FS_close();
  299. // remove the loading text
  300. word i;
  301. for (i = 0; i < 7; i++)
  302. {
  303. GFX_PrintcConsole(0x8); // backspace
  304. }
  305. // put back the space in the command
  306. if (putBackSpaceIndex != 0)
  307. {
  308. *(arg+putBackSpaceIndex) = ' ';
  309. }
  310. BDOS_Backup();
  311. // Indicate that a user program is running
  312. BDOS_userprogramRunning = 1;
  313. // jump to the program
  314. asm(
  315. "; backup registers\n"
  316. "push r1\n"
  317. "push r2\n"
  318. "push r3\n"
  319. "push r4\n"
  320. "push r5\n"
  321. "push r6\n"
  322. "push r7\n"
  323. "push r8\n"
  324. "push r9\n"
  325. "push r10\n"
  326. "push r11\n"
  327. "push r12\n"
  328. "push r13\n"
  329. "push r14\n"
  330. "push r15\n"
  331. "savpc r1\n"
  332. "push r1\n"
  333. "jump 0x400000\n"
  334. "; restore registers\n"
  335. "pop r15\n"
  336. "pop r14\n"
  337. "pop r13\n"
  338. "pop r12\n"
  339. "pop r11\n"
  340. "pop r10\n"
  341. "pop r9\n"
  342. "pop r8\n"
  343. "pop r7\n"
  344. "pop r6\n"
  345. "pop r5\n"
  346. "pop r4\n"
  347. "pop r3\n"
  348. "pop r2\n"
  349. "pop r1\n"
  350. );
  351. // Indicate that no user program is running anymore
  352. BDOS_userprogramRunning = 0;
  353. BDOS_Restore();
  354. }
  355. else
  356. GFX_PrintConsole("E: Could not move to start of file\n");
  357. }
  358. else
  359. GFX_PrintConsole("E: Program is too large\n");
  360. }
  361. else
  362. {
  363. if (useBin)
  364. GFX_PrintConsole("E: Unknown command\n");
  365. else
  366. GFX_PrintConsole("E: Could not open file\n");
  367. }
  368. }
  369. else
  370. {
  371. if (useBin)
  372. GFX_PrintConsole("E: Unknown command\n");
  373. else
  374. GFX_PrintConsole("E: Invalid path\n");
  375. }
  376. // restore path
  377. strcpy(SHELL_path, SHELL_pathBackup);
  378. }
  379. // Implementation of print command
  380. // Prints file to screen
  381. // Argument is passed in arg and should end with a \0 (not space)
  382. void SHELL_printFile(char* arg)
  383. {
  384. // backup current path
  385. strcpy(SHELL_pathBackup, SHELL_path);
  386. // create full path using arg
  387. FS_getFullPath(arg);
  388. // if the resulting path is correct (can be file or directory)
  389. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  390. {
  391. // if we can successfully open the file (not directory)
  392. if (FS_open() == FS_ANSW_USB_INT_SUCCESS)
  393. {
  394. word fileSize = FS_getFileSize();
  395. if (FS_setCursor(0) == FS_ANSW_USB_INT_SUCCESS)
  396. {
  397. // read the file in chunks
  398. char *b = (char *) TEMP_ADDR;
  399. word bytesSent = 0;
  400. // loop until all bytes are sent
  401. while (bytesSent != fileSize)
  402. {
  403. word partToSend = fileSize - bytesSent;
  404. // send in parts of SHELL_FILE_READ_CHUNK_SIZE
  405. if ((unsigned int) partToSend > (unsigned int) SHELL_FILE_READ_CHUNK_SIZE)
  406. partToSend = SHELL_FILE_READ_CHUNK_SIZE;
  407. // read from usb to buffer in byte mode
  408. if (FS_readFile(b, partToSend, 0) != FS_ANSW_USB_INT_SUCCESS)
  409. uprintln("W: Error reading file\n");
  410. // append buffer with terminator
  411. *(b+partToSend) = 0;
  412. // print buffer to console
  413. GFX_PrintConsole(b);
  414. // Update the amount of bytes sent
  415. bytesSent += partToSend;
  416. }
  417. // close file after done
  418. FS_close();
  419. // end with a newline for the next shell prompt
  420. GFX_PrintcConsole('\n');
  421. }
  422. else
  423. GFX_PrintConsole("E: Could not move to start of file\n");
  424. }
  425. else
  426. GFX_PrintConsole("E: Could not open file\n");
  427. }
  428. else
  429. GFX_PrintConsole("E: Invalid path\n");
  430. // restore path
  431. strcpy(SHELL_path, SHELL_pathBackup);
  432. }
  433. // Removes file/dir
  434. void SHELL_remove(char* arg)
  435. {
  436. // backup current path
  437. strcpy(SHELL_pathBackup, SHELL_path);
  438. // create full path using arg
  439. FS_getFullPath(arg);
  440. // if the resulting path is correct (can be file or directory)
  441. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  442. {
  443. // if we can successfully open the file (not directory)
  444. word retval = FS_open();
  445. if (retval == FS_ANSW_USB_INT_SUCCESS)
  446. {
  447. if (FS_delete() == FS_ANSW_USB_INT_SUCCESS)
  448. {
  449. GFX_PrintConsole("File removed\n");
  450. }
  451. else
  452. GFX_PrintConsole("E: Could not delete file\n");
  453. }
  454. else if (retval == FS_ANSW_ERR_OPEN_DIR)
  455. {
  456. if (FS_delete() == FS_ANSW_USB_INT_SUCCESS)
  457. {
  458. GFX_PrintConsole("Dir removed\n");
  459. }
  460. else
  461. GFX_PrintConsole("E: Could not delete dir\n");
  462. }
  463. else
  464. GFX_PrintConsole("E: Could not find file or dir\n");
  465. }
  466. else
  467. GFX_PrintConsole("E: Invalid path\n");
  468. // restore path
  469. strcpy(SHELL_path, SHELL_pathBackup);
  470. }
  471. // Creates file in current directory
  472. void SHELL_createFile(char* arg)
  473. {
  474. // backup current path
  475. strcpy(SHELL_pathBackup, SHELL_path);
  476. // if current path is correct (can be file or directory)
  477. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  478. {
  479. word retval = FS_open();
  480. // check that we can open the path
  481. if (retval == FS_ANSW_USB_INT_SUCCESS || retval == FS_ANSW_ERR_OPEN_DIR)
  482. {
  483. // check length of filename
  484. if (strlen(arg) <= 12)
  485. {
  486. // uppercase filename
  487. strToUpper(arg);
  488. // send filename
  489. FS_sendSinglePath(arg);
  490. // create the file
  491. if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS)
  492. {
  493. GFX_PrintConsole("File created\n");
  494. }
  495. else
  496. GFX_PrintConsole("E: Could not create file\n");
  497. }
  498. else
  499. GFX_PrintConsole("E: Filename too long\n");
  500. }
  501. else
  502. GFX_PrintConsole("E: Invalid path\n");
  503. }
  504. else
  505. GFX_PrintConsole("E: Invalid path\n");
  506. // restore path
  507. strcpy(SHELL_path, SHELL_pathBackup);
  508. }
  509. // Creates directory in current directory
  510. void SHELL_createDir(char* arg)
  511. {
  512. // backup current path
  513. strcpy(SHELL_pathBackup, SHELL_path);
  514. // if current path is correct (can be file or directory)
  515. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  516. {
  517. word retval = FS_open();
  518. // check that we can open the path
  519. if (retval == FS_ANSW_USB_INT_SUCCESS || retval == FS_ANSW_ERR_OPEN_DIR)
  520. {
  521. // check length of directory, must be 8
  522. if (strlen(arg) <= 8)
  523. {
  524. // uppercase filename
  525. strToUpper(arg);
  526. // send filename
  527. FS_sendSinglePath(arg);
  528. // create the directory
  529. if (FS_createDir() == FS_ANSW_USB_INT_SUCCESS)
  530. {
  531. GFX_PrintConsole("Dir created\n");
  532. }
  533. else
  534. GFX_PrintConsole("E: Could not create dir\n");
  535. }
  536. else
  537. GFX_PrintConsole("E: Filename too long\n");
  538. }
  539. else
  540. GFX_PrintConsole("E: Invalid path\n");
  541. }
  542. else
  543. GFX_PrintConsole("E: Invalid path\n");
  544. // restore path
  545. strcpy(SHELL_path, SHELL_pathBackup);
  546. }
  547. // Print help text
  548. void SHELL_printHelp()
  549. {
  550. GFX_PrintConsole("BDOS for FPGC\n");
  551. GFX_PrintConsole("Supported OS commands:\n");
  552. GFX_PrintConsole("- ./file [args]\n");
  553. GFX_PrintConsole("- CD [arg1]\n");
  554. GFX_PrintConsole("- LS [arg1]\n");
  555. GFX_PrintConsole("- PRINT [arg1]\n");
  556. GFX_PrintConsole("- MKDIR [arg1]\n");
  557. GFX_PrintConsole("- MKFILE [arg1]\n");
  558. GFX_PrintConsole("- RM [arg1]\n");
  559. GFX_PrintConsole("- CLEAR\n");
  560. GFX_PrintConsole("- HELP\n");
  561. GFX_PrintConsole("\nExtra info:\n");
  562. GFX_PrintConsole("- Programs are executed form /BIN\n");
  563. GFX_PrintConsole("- Run LS /BIN to list all programs\n");
  564. GFX_PrintConsole("- Paths can be relative or absolute\n");
  565. }
  566. // Parses command line buffer and executes command if found
  567. // Commands to parse:
  568. // [x] ./ (RUN)
  569. // [x] CD
  570. // [x] LS
  571. // [x] CLEAR
  572. // [x] PRINT
  573. // [x] MKDIR
  574. // [x] MKFILE
  575. // [x] RM
  576. // [x] HELP
  577. // [] RENAME
  578. void SHELL_parseCommand(char* p)
  579. {
  580. // ./ (RUN)
  581. // No commandCompare, because special case with no space
  582. if (p[0] == '.' && p[1] == '/' && p[2] != 0)
  583. {
  584. SHELL_runFile(p+2, 0); // pointer to start of filename, which ends with \0 or space
  585. }
  586. // LS
  587. else if (SHELL_commandCompare(p, "ls"))
  588. {
  589. word args = SHELL_numberOfArguments(p);
  590. // if incorrect number of arguments
  591. if (args > 1)
  592. {
  593. GFX_PrintConsole("E: Too many arguments\n");
  594. return;
  595. }
  596. else if (args == 1)
  597. {
  598. SHELL_ldir(p+3); // pointer to start of first arg, which ends with \0
  599. }
  600. else // if no args
  601. {
  602. SHELL_ldir("");
  603. }
  604. }
  605. // CD
  606. else if (SHELL_commandCompare(p, "cd"))
  607. {
  608. word args = SHELL_numberOfArguments(p);
  609. // if incorrect number of arguments
  610. if (args > 1)
  611. {
  612. GFX_PrintConsole("E: Too many arguments\n");
  613. return;
  614. }
  615. else if (args == 1)
  616. {
  617. // pointer to start of first arg, which ends with \0
  618. if (FS_changePath(p+3) != FS_ANSW_USB_INT_SUCCESS)
  619. {
  620. GFX_PrintConsole("E: Invalid path\n");
  621. }
  622. }
  623. else // if no args, go to root
  624. {
  625. // reset path variable to /
  626. SHELL_path[0] = '/';
  627. SHELL_path[1] = 0; // terminate string
  628. }
  629. // update path backup
  630. strcpy(SHELL_pathBackup, SHELL_path);
  631. }
  632. // CLEAR
  633. else if (SHELL_commandCompare(p, "clear"))
  634. {
  635. // clear screen by clearing window tables and resetting the cursor
  636. GFX_clearWindowtileTable();
  637. GFX_clearWindowpaletteTable();
  638. GFX_cursor = 0;
  639. }
  640. // PRINT
  641. else if (SHELL_commandCompare(p, "print"))
  642. {
  643. word args = SHELL_numberOfArguments(p);
  644. // if incorrect number of arguments
  645. if (args != 1)
  646. {
  647. GFX_PrintConsole("E: Expected 1 argument\n");
  648. return;
  649. }
  650. else
  651. {
  652. SHELL_printFile(p+6); // pointer to start of first arg, which ends with \0
  653. }
  654. }
  655. // RM
  656. else if (SHELL_commandCompare(p, "rm"))
  657. {
  658. word args = SHELL_numberOfArguments(p);
  659. // if incorrect number of arguments
  660. if (args != 1)
  661. {
  662. GFX_PrintConsole("E: Expected 1 argument\n");
  663. return;
  664. }
  665. else
  666. {
  667. SHELL_remove(p+3); // pointer to start of first arg, which ends with \0
  668. }
  669. }
  670. // MKFILE
  671. else if (SHELL_commandCompare(p, "mkfile"))
  672. {
  673. word args = SHELL_numberOfArguments(p);
  674. // if incorrect number of arguments
  675. if (args != 1)
  676. {
  677. GFX_PrintConsole("E: Expected 1 argument\n");
  678. return;
  679. }
  680. else
  681. {
  682. SHELL_createFile(p+7); // pointer to start of first arg, which ends with \0
  683. }
  684. }
  685. // MKDIR
  686. else if (SHELL_commandCompare(p, "mkdir"))
  687. {
  688. word args = SHELL_numberOfArguments(p);
  689. // if incorrect number of arguments
  690. if (args != 1)
  691. {
  692. GFX_PrintConsole("E: Expected 1 argument\n");
  693. return;
  694. }
  695. else
  696. {
  697. SHELL_createDir(p+6); // pointer to start of first arg, which ends with \0
  698. }
  699. }
  700. // HELP
  701. else if (SHELL_commandCompare(p, "help"))
  702. {
  703. SHELL_printHelp();
  704. }
  705. // No command
  706. else if (p[0] == 0)
  707. {
  708. // do nothing on enter
  709. }
  710. // Check if the command is in /BIN as a file
  711. else
  712. {
  713. // copy p to commandBuf but without the arguments
  714. char commandBuf[16]; // filenames cannot be larger than 12 characters anyways, so this should be enough
  715. word i = 0;
  716. commandBuf[0] = 0;
  717. while (p[i] != 0 && p[i] != ' ' && i < 15)
  718. {
  719. commandBuf[i] = p[i];
  720. i++;
  721. }
  722. commandBuf[i] = 0; // terminate buffer
  723. SHELL_runFile(commandBuf, 1);
  724. return;
  725. }
  726. }
  727. void SHELL_loop()
  728. {
  729. if (HID_FifoAvailable())
  730. {
  731. word c = HID_FifoRead();
  732. // special keys, like arrow keys
  733. if (c > 255)
  734. {
  735. if (c == 258) // arrow up
  736. {
  737. SHELL_historyGoBack();
  738. }
  739. else if (c == 259) // arrow down
  740. {
  741. SHELL_historyGoForwards();
  742. }
  743. }
  744. else if (c == 0x8) // backspace
  745. {
  746. // replace last char in buffer by 0 (if not at start)
  747. if (SHELL_commandIdx != 0)
  748. {
  749. SHELL_commandIdx--;
  750. SHELL_command[SHELL_commandIdx] = 0;
  751. }
  752. // prevent removing characters from the shell prompt
  753. if (GFX_cursor > SHELL_promptCursorPos)
  754. {
  755. // print backspace to console to remove last typed character
  756. GFX_PrintcConsole(c);
  757. }
  758. }
  759. else if (c == 0x9) // tab
  760. {
  761. // do nothing for now when tab
  762. }
  763. else if (c == 0x1b) // escape
  764. {
  765. // do nothing for now when escape
  766. }
  767. else if (c == 0xa) // newline/enter
  768. {
  769. // reset history counter
  770. SHELL_historyMovedBackwards = 0;
  771. // append command to history
  772. SHELL_historyAppend();
  773. // start on new line
  774. GFX_PrintcConsole(c);
  775. // parse/execute command
  776. SHELL_parseCommand(SHELL_command);
  777. // clear buffer
  778. SHELL_clearCommand();
  779. // print shell prompt
  780. SHELL_print_prompt();
  781. }
  782. else
  783. {
  784. if (SHELL_commandIdx < SHELL_CMD_MAX_LENGTH)
  785. {
  786. // add to command buffer and print character
  787. SHELL_command[SHELL_commandIdx] = c;
  788. SHELL_commandIdx++;
  789. SHELL_command[SHELL_commandIdx] = 0; // terminate
  790. GFX_PrintcConsole(c);
  791. }
  792. }
  793. }
  794. }