1
0

shell.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // Max length of a single command
  2. #define SHELL_CMD_MAX_LENGTH 128
  3. #define SHELL_PATH "/bin/"
  4. word shell_cmd[SHELL_CMD_MAX_LENGTH];
  5. word shell_cmd_idx = 0;
  6. word* shell_tokens[SHELL_CMD_MAX_LENGTH >> 1]; // Array of tokens in the command
  7. word shell_num_tokens = 0;
  8. word shell_path[MAX_PATH_LENGTH];
  9. /**
  10. * Print the shell prompt
  11. */
  12. void shell_print_prompt()
  13. {
  14. GFX_PrintConsole(shell_path);
  15. GFX_PrintConsole("> ");
  16. }
  17. /**
  18. * Clear the current command buffer
  19. */
  20. void shell_clear_command()
  21. {
  22. shell_cmd[0] = 0;
  23. shell_cmd_idx = 0;
  24. shell_tokens[0] = 0;
  25. shell_num_tokens = 0;
  26. }
  27. /**
  28. * Append character to command buffer
  29. */
  30. void shell_append_command(char c)
  31. {
  32. if (shell_cmd_idx < SHELL_CMD_MAX_LENGTH)
  33. {
  34. shell_cmd[shell_cmd_idx] = c;
  35. shell_cmd_idx++;
  36. shell_cmd[shell_cmd_idx] = 0; // Terminate
  37. }
  38. }
  39. /**
  40. * Parse the command buffer into tokens
  41. */
  42. void shell_parse_command()
  43. {
  44. // Strip trailing spaces
  45. word i = shell_cmd_idx - 1;
  46. while (i >= 0 && shell_cmd[i] == ' ')
  47. {
  48. shell_cmd[i] = 0;
  49. i--;
  50. }
  51. shell_num_tokens = 0;
  52. shell_tokens[shell_num_tokens] = strtok(shell_cmd, " ");
  53. while((word)shell_tokens[shell_num_tokens] != -1)
  54. {
  55. shell_num_tokens++;
  56. shell_tokens[shell_num_tokens] = strtok(-1, " ");
  57. }
  58. }
  59. /**
  60. * Print help
  61. */
  62. void shell_print_help()
  63. {
  64. GFX_PrintConsole("Available commands:\n");
  65. GFX_PrintConsole("cd\n");
  66. GFX_PrintConsole("clear\n");
  67. GFX_PrintConsole("help\n");
  68. }
  69. /**
  70. * Attempt to run program from FS
  71. * If run_from_path is 1, the program is run from the SHELL_PATH
  72. * Returns 1 if program was found, 0 otherwise
  73. */
  74. word shell_run_program(word run_from_path)
  75. {
  76. // Create absolute path to program
  77. char absolute_path[MAX_PATH_LENGTH];
  78. // Check if program is relative or absolute
  79. if (shell_tokens[0][0] == '/')
  80. {
  81. strcpy(absolute_path, shell_tokens[0]);
  82. }
  83. else
  84. {
  85. if (run_from_path)
  86. {
  87. strcpy(absolute_path, SHELL_PATH);
  88. strcat(absolute_path, shell_tokens[0]);
  89. }
  90. else
  91. {
  92. // Append to current path
  93. strcpy(absolute_path, shell_path);
  94. if (strlen(absolute_path) + strlen(shell_tokens[0]) < MAX_PATH_LENGTH)
  95. {
  96. // If not root, append slash
  97. if (strcmp(shell_path, "/") != 0)
  98. {
  99. strcat(absolute_path, "/");
  100. }
  101. strcat(absolute_path, shell_tokens[0]);
  102. }
  103. else
  104. {
  105. GFX_PrintConsole("Path too long\n");
  106. return 0;
  107. }
  108. }
  109. }
  110. // Get filesize of the program
  111. struct brfs_dir_entry* dir = brfs_stat(absolute_path);
  112. if ((word)dir == -1)
  113. {
  114. //GFX_PrintConsole("Program not found\n");
  115. return 0;
  116. }
  117. word filesize = dir->filesize;
  118. // Attempt to open file
  119. word fp = brfs_open_file(absolute_path);
  120. if (fp == -1)
  121. {
  122. GFX_PrintConsole("Could not open file\n");
  123. return 0;
  124. }
  125. // Set cursor to start of file
  126. brfs_set_cursor(fp, 0);
  127. // Read program into memory
  128. word* program = (word*) RUN_ADDR;
  129. if (!brfs_read(fp, program, filesize))
  130. {
  131. GFX_PrintConsole("Could not read file\n");
  132. return 0;
  133. }
  134. // Run program
  135. // Indicate that a user program is running
  136. bdos_userprogram_running = 1;
  137. uprintln("Running program...");
  138. // jump to the program
  139. asm(
  140. "; backup registers\n"
  141. "push r1\n"
  142. "push r2\n"
  143. "push r3\n"
  144. "push r4\n"
  145. "push r5\n"
  146. "push r6\n"
  147. "push r7\n"
  148. "push r8\n"
  149. "push r9\n"
  150. "push r10\n"
  151. "push r11\n"
  152. "push r12\n"
  153. "push r13\n"
  154. "push r14\n"
  155. "push r15\n"
  156. //"ccache\n"
  157. "savpc r1\n"
  158. "push r1\n"
  159. "jump 0x400000\n"
  160. "; restore registers\n"
  161. "pop r15\n"
  162. "pop r14\n"
  163. "pop r13\n"
  164. "pop r12\n"
  165. "pop r11\n"
  166. "pop r10\n"
  167. "pop r9\n"
  168. "pop r8\n"
  169. "pop r7\n"
  170. "pop r6\n"
  171. "pop r5\n"
  172. "pop r4\n"
  173. "pop r3\n"
  174. "pop r2\n"
  175. "pop r1\n"
  176. );
  177. // Indicate that no user program is running anymore
  178. bdos_userprogram_running = 0;
  179. bdos_restore();
  180. // Close file
  181. brfs_close_file(fp);
  182. return 1;
  183. }
  184. /**
  185. * List the contents of a directory (TMP function until separate ls program is implemented)
  186. * dir_path: full path of the directory
  187. */
  188. void shell_list_directory(char* dir_path)
  189. {
  190. // Find data block address of parent directory path
  191. word dir_fat_idx = brfs_get_fat_idx_of_dir(dir_path);
  192. if (dir_fat_idx == -1)
  193. {
  194. GFX_PrintConsole("Directory not found\n");
  195. return;
  196. }
  197. struct brfs_superblock* superblock = (struct brfs_superblock*) brfs_ram_storage;
  198. word* dir_addr = brfs_ram_storage + SUPERBLOCK_SIZE + superblock->total_blocks + (dir_fat_idx * superblock->words_per_block);
  199. word dir_entries_max = superblock->words_per_block / sizeof(struct brfs_dir_entry);
  200. word i;
  201. for (i = 0; i < dir_entries_max; i++)
  202. {
  203. struct brfs_dir_entry* dir_entry = (struct brfs_dir_entry*) (dir_addr + (i * sizeof(struct brfs_dir_entry)));
  204. if (dir_entry->filename[0] != 0)
  205. {
  206. char decompressed_filename[16];
  207. strdecompress(decompressed_filename, (char*)&(dir_entry->filename));
  208. GFX_PrintConsole(decompressed_filename);
  209. GFX_PrintConsole(" FAT: ");
  210. GFX_PrintDecConsole((dir_entry->fat_idx));
  211. GFX_PrintConsole(" Flg: ");
  212. GFX_PrintDecConsole((dir_entry->flags));
  213. GFX_PrintConsole(" Size: ");
  214. GFX_PrintDecConsole((dir_entry->filesize));
  215. GFX_PrintConsole("\n");
  216. }
  217. }
  218. }
  219. /**
  220. * Process dots in path so that ".." goes up one directory and "." is skipped
  221. */
  222. void shell_process_dots(char* path)
  223. {
  224. // Create a copy of the path
  225. char path_copy[MAX_PATH_LENGTH];
  226. strcpy(path_copy, path);
  227. // Split path by '/' and traverse directories
  228. word num_tokens = 0;
  229. char* tokens[MAX_PATH_LENGTH >> 1];
  230. tokens[num_tokens] = strtok(path_copy+1, "/");
  231. while((word)tokens[num_tokens] != -1)
  232. {
  233. num_tokens++;
  234. tokens[num_tokens] = strtok(-1, "/");
  235. }
  236. if (num_tokens == 0)
  237. {
  238. return;
  239. }
  240. strcpy(path, "/");
  241. // Traverse path
  242. word i = 0;
  243. while (i < num_tokens)
  244. {
  245. if (strcmp(tokens[i], ".") == 0)
  246. {
  247. // Skip
  248. }
  249. else if (strcmp(tokens[i], "..") == 0)
  250. {
  251. // Remove all characters after last slash
  252. word j = strlen(path) - 1;
  253. while (j > 0 && path[j] != '/')
  254. {
  255. path[j] = 0;
  256. j--;
  257. }
  258. }
  259. else
  260. {
  261. // Copy token
  262. if (strlen(path) > 1)
  263. {
  264. strcat(path, "/");
  265. }
  266. strcat(path, tokens[i]);
  267. }
  268. i++;
  269. }
  270. }
  271. /**
  272. * Change directory
  273. */
  274. void shell_change_directory()
  275. {
  276. if (shell_num_tokens != 2)
  277. {
  278. GFX_PrintConsole("Usage: cd <path>\n");
  279. return;
  280. }
  281. // Remove trailing slashes, skipping the first character
  282. word i = strlen(shell_tokens[1]) - 1;
  283. while (i >= 1 && shell_tokens[1][i] == '/')
  284. {
  285. shell_tokens[1][i] = 0;
  286. i--;
  287. }
  288. // Check if path is root
  289. if (strcmp(shell_tokens[1], "/") == 0)
  290. {
  291. strcpy(shell_path, "/");
  292. return;
  293. }
  294. char absolute_path[MAX_PATH_LENGTH];
  295. // Create absolute path
  296. // Check if argument is relative or absolute
  297. if (shell_tokens[1][0] == '/')
  298. {
  299. strcpy(absolute_path, shell_tokens[1]);
  300. }
  301. else
  302. {
  303. // Append to current path
  304. strcpy(absolute_path, shell_path);
  305. if (strlen(absolute_path) + strlen(shell_tokens[1]) < MAX_PATH_LENGTH)
  306. {
  307. // If not root, append slash
  308. if (strcmp(shell_path, "/") != 0)
  309. {
  310. strcat(absolute_path, "/");
  311. }
  312. strcat(absolute_path, shell_tokens[1]);
  313. }
  314. else
  315. {
  316. GFX_PrintConsole("Path too long\n");
  317. }
  318. }
  319. uprintln(absolute_path);
  320. // Get dir entry of the path
  321. struct brfs_dir_entry* dir = brfs_stat(absolute_path);
  322. // Check if valid and is a directory
  323. if ((word)dir != -1 && dir->flags & (1 << 0))
  324. {
  325. // Set new path
  326. strcpy(shell_path, absolute_path);
  327. // Process dots in path
  328. shell_process_dots(shell_path);
  329. }
  330. else
  331. {
  332. GFX_PrintConsole("Directory not found\n");
  333. }
  334. }
  335. /**
  336. * Handle the command
  337. */
  338. void shell_handle_command()
  339. {
  340. if (strcmp(shell_tokens[0], "cd") == 0)
  341. {
  342. shell_change_directory();
  343. }
  344. else if (strcmp(shell_tokens[0], "clear") == 0)
  345. {
  346. // clear screen by clearing window tables and resetting the cursor
  347. GFX_clearWindowtileTable();
  348. GFX_clearWindowpaletteTable();
  349. GFX_cursor = 0;
  350. }
  351. else if (strcmp(shell_tokens[0], "ls") == 0)
  352. {
  353. if (shell_num_tokens > 1)
  354. {
  355. shell_list_directory(shell_tokens[1]);
  356. }
  357. else
  358. {
  359. shell_list_directory(shell_path);
  360. }
  361. }
  362. else if (strcmp(shell_tokens[0], "help") == 0)
  363. {
  364. shell_print_help();
  365. }
  366. else if (strcmp(shell_tokens[0], "sync") == 0)
  367. {
  368. brfs_write_to_flash();
  369. }
  370. else if (strcmp(shell_tokens[0], "read") == 0)
  371. {
  372. brfs_read_from_flash();
  373. }
  374. else if (strcmp(shell_tokens[0], "format") == 0)
  375. {
  376. word blocks = BDOS_DEFAULT_BLOCKS;
  377. word words_per_block = BDOS_DEFAULT_BLOCK_SIZE;
  378. word full_format = 1;
  379. brfs_format(blocks, words_per_block, "FPGC5", full_format);
  380. brfs_write_to_flash();
  381. }
  382. // Attempt to run program both from local dir and from SHELL_PATH
  383. else if (!shell_run_program(0))
  384. {
  385. if (!shell_run_program(1))
  386. {
  387. GFX_PrintConsole("Command not found\n");
  388. }
  389. }
  390. }
  391. /**
  392. * Initialize shell
  393. */
  394. void shell_init()
  395. {
  396. shell_clear_command();
  397. // Set path to root
  398. strcpy(shell_path, "/");
  399. shell_print_prompt();
  400. }
  401. /**
  402. * Main shell loop
  403. */
  404. void shell_loop()
  405. {
  406. // Read command from HID FIFO
  407. if (HID_FifoAvailable())
  408. {
  409. char c = HID_FifoRead();
  410. if (c == '\n')
  411. {
  412. GFX_PrintcConsole('\n');
  413. shell_parse_command();
  414. if (shell_num_tokens != 0)
  415. {
  416. shell_handle_command();
  417. }
  418. shell_clear_command();
  419. shell_print_prompt();
  420. }
  421. else if (c == 0x8) // backspace
  422. {
  423. if (shell_cmd_idx > 0)
  424. {
  425. shell_cmd_idx--;
  426. shell_cmd[shell_cmd_idx] = 0;
  427. GFX_PrintcConsole(0x8);
  428. }
  429. }
  430. else
  431. {
  432. // Append character to command buffer and print it
  433. shell_append_command(c);
  434. GFX_PrintcConsole(c);
  435. }
  436. }
  437. }