netloader.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /*
  2. * Network bootloader library
  3. * Could be / is being extended to handle more network based commands
  4. * Uses wiz5500 library
  5. */
  6. // uses wiz5500.c
  7. // Port for network bootloader
  8. #define NETLOADER_PORT 3220
  9. // Socket to listen to (0-7)
  10. #define NETLOADER_SOCKET 0
  11. // Checks if p starts with cmd
  12. // Returns 1 if true, 0 otherwise
  13. word NETLOADER_frameCompare(char* p, char* cmd)
  14. {
  15. word i = 0;
  16. while (cmd[i] != 0)
  17. {
  18. if (cmd[i] != p[i])
  19. {
  20. return 0;
  21. }
  22. i++;
  23. }
  24. return 1;
  25. }
  26. word NETLOADER_wordPosition = 0;
  27. word NETLOADER_currentByteShift = 24;
  28. // Appends bytes from buffer to words in run address
  29. void NETLOADER_appendBufferToRunAddress(char* b, word len)
  30. {
  31. char* dst = (char*) RUN_ADDR;
  32. word i;
  33. for (i = 0; i < len; i++)
  34. {
  35. char readByte = b[i];
  36. // Read 4 bytes into one word, from left to right
  37. readByte = readByte << NETLOADER_currentByteShift;
  38. dst[NETLOADER_wordPosition] = dst[NETLOADER_wordPosition] + readByte;
  39. if (NETLOADER_currentByteShift == 0)
  40. {
  41. NETLOADER_currentByteShift = 24;
  42. NETLOADER_wordPosition++;
  43. dst[NETLOADER_wordPosition] = 0;
  44. }
  45. else
  46. {
  47. NETLOADER_currentByteShift -= 8;
  48. }
  49. }
  50. }
  51. word NETLOADER_getContentLength(char* rbuf, word rsize)
  52. {
  53. word contentLengthStr[32];
  54. word i = 5; // content length always at 5
  55. char c = rbuf[i];
  56. while (i < rsize && c != ':')
  57. {
  58. contentLengthStr[i-5] = c;
  59. i++;
  60. c = rbuf[i];
  61. }
  62. contentLengthStr[i-5] = 0; // terminate
  63. return strToInt(contentLengthStr);
  64. }
  65. void NETLOADER_getFileName(char* rbuf, word rsize, char* fileNameStr)
  66. {
  67. word i = 0;
  68. char c = rbuf[i];
  69. while (i < rsize && c != ':')
  70. {
  71. i++;
  72. c = rbuf[i];
  73. }
  74. i++; // skip the :
  75. word x = 0;
  76. c = rbuf[i];
  77. while (i <rsize && c != '\n')
  78. {
  79. fileNameStr[x] = c;
  80. i++;
  81. x++;
  82. c = rbuf[i];
  83. }
  84. fileNameStr[x] = 0; // terminate
  85. }
  86. word NETLOADER_getContentStart(char* rbuf, word rsize)
  87. {
  88. word i = 5; // content length always at 5
  89. char c = rbuf[i];
  90. while (i < rsize && c != '\n')
  91. {
  92. i++;
  93. c = rbuf[i];
  94. }
  95. return (i+1);
  96. }
  97. void NETLOADER_runProgramFromMemory()
  98. {
  99. // indicate that a user program is running
  100. bdos_userprogram_running = 1;
  101. // jump to the program
  102. asm(
  103. "; backup registers\n"
  104. "push r1\n"
  105. "push r2\n"
  106. "push r3\n"
  107. "push r4\n"
  108. "push r5\n"
  109. "push r6\n"
  110. "push r7\n"
  111. "push r8\n"
  112. "push r9\n"
  113. "push r10\n"
  114. "push r11\n"
  115. "push r12\n"
  116. "push r13\n"
  117. "push r14\n"
  118. "push r15\n"
  119. //"ccache\n"
  120. "savpc r1\n"
  121. "push r1\n"
  122. "jump 0x400000\n"
  123. "; restore registers\n"
  124. "pop r15\n"
  125. "pop r14\n"
  126. "pop r13\n"
  127. "pop r12\n"
  128. "pop r11\n"
  129. "pop r10\n"
  130. "pop r9\n"
  131. "pop r8\n"
  132. "pop r7\n"
  133. "pop r6\n"
  134. "pop r5\n"
  135. "pop r4\n"
  136. "pop r3\n"
  137. "pop r2\n"
  138. "pop r1\n"
  139. );
  140. // indicate that no user program is running anymore
  141. bdos_userprogram_running = 0;
  142. // clear the shell
  143. shell_clear_command();
  144. // setup the shell again
  145. bdos_restore();
  146. shell_print_prompt();
  147. }
  148. word NETLOADER_percentageDone(word remaining, word full)
  149. {
  150. word x = remaining * 100;
  151. return 100 - MATH_divU(x, full);
  152. }
  153. void NETLOADER_handleSession(word s)
  154. {
  155. word firstResponse = 1;
  156. word contentLengthOrig = 0;
  157. word contentLength = 0;
  158. word currentProgress = 0;
  159. word downloadToFile = 0; // whether we need to download to a file instead
  160. char fileNameStr[32];
  161. char dbuf[10]; // percentage done for progress indication
  162. dbuf[0] = 0; // terminate
  163. word fp = -1;
  164. word shift = 24; // shift for compressing data, kept the same for the whole session
  165. word leftOverData[4];
  166. word leftOverBytes = 0;
  167. while (wiz_get_sock_reg_8(s, WIZNET_SnSR) == WIZNET_SOCK_ESTABLISHED)
  168. {
  169. word rsize = wiz_get_sock_reg_16(s, WIZNET_SnRX_RSR);
  170. if (rsize != 0)
  171. {
  172. char* rbuf = (char *) TEMP_ADDR;
  173. wiz_read_recv_data(s, rbuf, rsize);
  174. if (firstResponse)
  175. {
  176. GFX_PrintConsole("\n");
  177. // save to and run from memory
  178. if (rbuf[0] == 'E' && rbuf[1] == 'X' && rbuf[2] == 'E' && rbuf[3] == 'C')
  179. {
  180. GFX_PrintConsole("Receiving program\n");
  181. downloadToFile = 0;
  182. // reset position counters
  183. NETLOADER_wordPosition = 0;
  184. NETLOADER_currentByteShift = 24;
  185. // clear first address of run addr
  186. char* dst = (char*) RUN_ADDR;
  187. dst[0] = 0;
  188. }
  189. // save to file
  190. else if (rbuf[0] == 'D' && rbuf[1] == 'O' && rbuf[2] == 'W' && rbuf[3] == 'N')
  191. {
  192. GFX_PrintConsole("Receiving file\n");
  193. downloadToFile = 1;
  194. // get the filename
  195. NETLOADER_getFileName(rbuf, rsize, fileNameStr);
  196. word failedToCreateFile = 1;
  197. // check length of filename
  198. if (strlen(fileNameStr) < 16)
  199. {
  200. char new_file_path[MAX_PATH_LENGTH];
  201. strcpy(new_file_path, shell_path);
  202. strcat(new_file_path, "/");
  203. strcat(new_file_path, fileNameStr);
  204. // try to delete file in case it exists
  205. brfs_delete(new_file_path);
  206. if (brfs_create_file(shell_path, fileNameStr))
  207. {
  208. strcpy(new_file_path, shell_path);
  209. strcat(new_file_path, "/");
  210. strcat(new_file_path, fileNameStr);
  211. fp = brfs_open_file(new_file_path);
  212. if (fp != -1)
  213. {
  214. brfs_set_cursor(fp, 0);
  215. failedToCreateFile = 0;
  216. }
  217. }
  218. }
  219. if (failedToCreateFile)
  220. {
  221. wiz_write_data(s, "ERR!", 4);
  222. wiz_send_cmd(s, WIZNET_CR_DISCON);
  223. GFX_PrintConsole("E: Could not create file\n");
  224. // clear the shell
  225. shell_clear_command();
  226. shell_print_prompt();
  227. return;
  228. }
  229. }
  230. // unknown command
  231. else
  232. {
  233. // unsupported command
  234. wiz_write_data(s, "ERR!", 4);
  235. wiz_send_cmd(s, WIZNET_CR_DISCON);
  236. GFX_PrintConsole("E: Unknown netloader cmd\n");
  237. // clear the shell
  238. shell_clear_command();
  239. shell_print_prompt();
  240. return;
  241. }
  242. word dataStart = NETLOADER_getContentStart(rbuf, rsize);
  243. contentLength = NETLOADER_getContentLength(rbuf, rsize);
  244. contentLengthOrig = contentLength;
  245. contentLength -= (rsize - dataStart);
  246. if (downloadToFile)
  247. {
  248. // compress each 4 bytes into a word
  249. word compressed_data[WIZNET_MAX_RBUF];
  250. memset(compressed_data, 0, WIZNET_MAX_RBUF);
  251. word i;
  252. for (i = 0; i < rsize - dataStart; i++)
  253. {
  254. compressed_data[i >> 2] |= rbuf[dataStart + i] << shift;
  255. if (shift == 0)
  256. {
  257. shift = 24;
  258. }
  259. else
  260. {
  261. shift -= 8;
  262. }
  263. }
  264. brfs_write(fp, compressed_data, (rsize - dataStart)>>2);
  265. if (shift != 24)
  266. {
  267. // save the left over data
  268. leftOverBytes = (rsize - dataStart)&3;
  269. switch (leftOverBytes)
  270. {
  271. case 1:
  272. leftOverData[0] = rbuf[dataStart + (rsize - dataStart) - 1];
  273. break;
  274. case 2:
  275. leftOverData[0] = rbuf[dataStart + (rsize - dataStart) - 2];
  276. leftOverData[1] = rbuf[dataStart + (rsize - dataStart) - 1];
  277. break;
  278. case 3:
  279. leftOverData[0] = rbuf[dataStart + (rsize - dataStart) - 3];
  280. leftOverData[1] = rbuf[dataStart + (rsize - dataStart) - 2];
  281. leftOverData[2] = rbuf[dataStart + (rsize - dataStart) - 1];
  282. break;
  283. default:
  284. break;
  285. }
  286. }
  287. else
  288. {
  289. leftOverBytes = 0;
  290. }
  291. }
  292. else
  293. {
  294. NETLOADER_appendBufferToRunAddress(rbuf+dataStart, rsize - dataStart);
  295. }
  296. firstResponse = 0;
  297. // all data downloaded
  298. if (contentLength == 0)
  299. {
  300. wiz_write_data(s, "THX!", 4);
  301. wiz_send_cmd(s, WIZNET_CR_DISCON);
  302. if (downloadToFile)
  303. {
  304. brfs_close_file(fp);
  305. // clear the shell
  306. shell_clear_command();
  307. shell_print_prompt();
  308. }
  309. else
  310. {
  311. NETLOADER_runProgramFromMemory();
  312. }
  313. return;
  314. }
  315. }
  316. // not the first response
  317. else
  318. {
  319. // indicate progress
  320. // remove previous percentage
  321. word i = strlen(dbuf);
  322. while (i > 0)
  323. {
  324. GFX_PrintcConsole(0x8); // backspace
  325. i--;
  326. }
  327. if (strlen(dbuf) != 0)
  328. {
  329. GFX_PrintcConsole(0x8); // backspace
  330. }
  331. itoa(NETLOADER_percentageDone(contentLength, contentLengthOrig), dbuf);
  332. GFX_PrintConsole(dbuf);
  333. GFX_PrintcConsole('%');
  334. contentLength -= rsize;
  335. if (downloadToFile)
  336. {
  337. // compress each 4 bytes into a word
  338. word compressed_data[WIZNET_MAX_RBUF];
  339. memset(compressed_data, 0, WIZNET_MAX_RBUF);
  340. // add the left over data
  341. switch (leftOverBytes)
  342. {
  343. case 1:
  344. compressed_data[0] = leftOverData[0] << 24;
  345. shift = 16;
  346. break;
  347. case 2:
  348. compressed_data[0] = leftOverData[0] << 24;
  349. compressed_data[0] |= leftOverData[1] << 16;
  350. shift = 8;
  351. break;
  352. case 3:
  353. compressed_data[0] = leftOverData[0] << 24;
  354. compressed_data[0] |= leftOverData[1] << 16;
  355. compressed_data[0] |= leftOverData[2] << 8;
  356. shift = 0;
  357. break;
  358. }
  359. word i;
  360. word j = leftOverBytes;
  361. for (i = 0; i < rsize; i++)
  362. {
  363. compressed_data[j >> 2] |= rbuf[i] << shift;
  364. if (shift == 0)
  365. {
  366. shift = 24;
  367. }
  368. else
  369. {
  370. shift -= 8;
  371. }
  372. j++;
  373. }
  374. if (shift != 24)
  375. {
  376. // save the left over data
  377. if (shift == 16)
  378. {
  379. leftOverBytes = 1;
  380. }
  381. else if (shift == 8)
  382. {
  383. leftOverBytes = 2;
  384. }
  385. else if (shift == 0)
  386. {
  387. leftOverBytes = 3;
  388. }
  389. //leftOverBytes = (rsize)&3;
  390. switch (leftOverBytes)
  391. {
  392. case 1:
  393. leftOverData[0] = rbuf[rsize - 1];
  394. break;
  395. case 2:
  396. leftOverData[0] = rbuf[rsize - 2];
  397. leftOverData[1] = rbuf[rsize - 1];
  398. break;
  399. case 3:
  400. leftOverData[0] = rbuf[rsize - 3];
  401. leftOverData[1] = rbuf[rsize - 2];
  402. leftOverData[2] = rbuf[rsize - 1];
  403. break;
  404. default:
  405. break;
  406. }
  407. }
  408. else
  409. {
  410. leftOverBytes = 0;
  411. }
  412. brfs_write(fp, compressed_data, rsize >> 2);
  413. //brfs_write(fp, rbuf, rsize);
  414. }
  415. else
  416. {
  417. NETLOADER_appendBufferToRunAddress(rbuf, rsize);
  418. }
  419. // all data downloaded
  420. if (contentLength == 0)
  421. {
  422. wiz_write_data(s, "THX!", 4);
  423. wiz_send_cmd(s, WIZNET_CR_DISCON);
  424. // remove progress prints
  425. word i = strlen(dbuf);
  426. while (i > 0)
  427. {
  428. GFX_PrintcConsole(0x8); // backspace
  429. i--;
  430. }
  431. GFX_PrintcConsole(0x8); // backspace
  432. if (downloadToFile)
  433. {
  434. brfs_close_file(fp);
  435. // clear the shell
  436. shell_clear_command();
  437. shell_print_prompt();
  438. }
  439. else
  440. {
  441. NETLOADER_runProgramFromMemory();
  442. }
  443. return;
  444. }
  445. }
  446. }
  447. }
  448. }
  449. // Initialize network bootloader on socket s
  450. void NETLOADER_init(word s)
  451. {
  452. // Open socket in TCP Server mode
  453. wiz_init_socket_tcp_host(s, NETLOADER_PORT);
  454. }
  455. // Check for a change in the socket status
  456. // Handles change if exists
  457. word NETLOADER_loop(word s)
  458. {
  459. // Get status for socket s
  460. word sxStatus = wiz_get_sock_reg_8(s, WIZNET_SnSR);
  461. if (sxStatus == WIZNET_SOCK_CLOSED)
  462. {
  463. // Open the socket when closed
  464. wiz_init_socket_tcp_host(s, NETLOADER_PORT);
  465. }
  466. else if (sxStatus == WIZNET_SOCK_ESTABLISHED)
  467. {
  468. // Handle session when a connection is established
  469. NETLOADER_handleSession(s);
  470. }
  471. else if (sxStatus == WIZNET_SOCK_LISTEN)
  472. {
  473. // Keep on listening
  474. return 1;
  475. }
  476. else if (sxStatus == WIZNET_SOCK_SYNSENT || sxStatus == WIZNET_SOCK_SYNRECV || sxStatus == WIZNET_SOCK_FIN_WAIT || sxStatus == WIZNET_SOCK_TIME_WAIT)
  477. {
  478. // Do nothing in these cases
  479. return 2;
  480. }
  481. else
  482. {
  483. // In other cases, reset the socket
  484. wiz_init_socket_tcp_host(s, NETLOADER_PORT);
  485. }
  486. return 0;
  487. }