dragonbane

Blw

Mar 7th, 2019
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include "crc_table.h"
  2.  
  3. #define CTRL_PIN 3
  4. #define CTRL_HIGH DDRD &= ~0x01
  5. #define CTRL_LOW DDRD |= 0x01
  6. #define CTRL_QUERY (PIND & 0x01)
  7.  
  8. #define CNSL_PIN 5
  9. #define CNSL_HIGH DDRC &= ~0x40
  10. #define CNSL_LOW DDRC |= 0x40
  11. #define CNSL_QUERY (PINC & 0x40)
  12.  
  13. //From console to controller
  14. char console_raw_dump[281];
  15. unsigned char console_command;
  16.  
  17. //From controller to console
  18. unsigned char controller_buffer[33];
  19.  
  20. struct state {
  21. char stick_x;
  22. char stick_y;
  23. // bits: 0, 0, 0, start, y, x, b, a
  24. unsigned char data1;
  25. // bits: 1, L, R, Z, Dup, Ddown, Dright, Dleft
  26. unsigned char data2;
  27. } N64_status;
  28.  
  29. void setup()
  30. {
  31. Serial.begin(9600);
  32.  
  33. // Communication with the N64-console on this pin
  34. digitalWrite(CNSL_PIN, LOW);
  35. pinMode(CNSL_PIN, INPUT);
  36.  
  37. // Communication with the N64-controller on this pin
  38. digitalWrite(CTRL_PIN, LOW);
  39. pinMode(CTRL_PIN, INPUT);
  40. }
  41.  
  42. void loop()
  43. {
  44. unsigned char data, addr;
  45. memset(controller_buffer, 0, sizeof(controller_buffer));
  46.  
  47. noInterrupts();
  48. CTRL_get();
  49. interrupts();
  50. translate_raw_data();
  51. print_CTRL_status();
  52.  
  53. noInterrupts();
  54.  
  55. get_console_command();
  56. Serial.println(console_command, HEX);
  57. switch (console_command)
  58. {
  59. case 0x00:
  60. case 0xFF:
  61. // identify
  62. // mutilate the n64_buffer array with our status
  63. // we return 0x050001 to indicate we have a rumble pack
  64. // or 0x050002 to indicate the expansion slot is empty
  65. //
  66. // 0xFF I've seen sent from Mario 64 and Shadows of the Empire.
  67. // I don't know why it's different, but the controllers seem to
  68. // send a set of status bytes afterwards the same as 0x00, and
  69. // it won't work without it.
  70. controller_buffer[0] = 0x05;
  71. controller_buffer[1] = 0x00;
  72. controller_buffer[2] = 0x02;
  73.  
  74. console_send(controller_buffer, 3, 0);
  75.  
  76. //Serial.println("It was 0x00: an identify command");
  77. break;
  78. case 0x01:
  79. // Send to n64 the received data
  80.  
  81. console_send(controller_buffer, 4, 0);
  82. //Serial.println("It was 0x01: the query command");
  83. break;
  84. case 0x02:
  85. // A read. If the address is 0x8000, return 32 bytes of 0x80 bytes,
  86. // and a CRC byte. this tells the system our attached controller
  87. // pack is a rumble pack
  88.  
  89. // Assume it's a read for 0x8000, which is the only thing it should
  90. // be requesting anyways
  91. memset(controller_buffer, 0x80, 32);
  92. controller_buffer[32] = 0xB8; // CRC
  93.  
  94. console_send(controller_buffer, 33, 1);
  95.  
  96. //Serial.println("It was 0x02: the read command");
  97. break;
  98. case 0x03:
  99. // A write. we at least need to respond with a single CRC byte. If
  100. // the write was to address 0xC000 and the data was 0x01, turn on
  101. // rumble! All other write addresses are ignored. (but we still
  102. // need to return a CRC)
  103.  
  104. // decode the first data byte (fourth overall byte), bits indexed
  105. // at 24 through 31
  106. data = 0;
  107. data |= (console_raw_dump[16] != 0) << 7;
  108. data |= (console_raw_dump[17] != 0) << 6;
  109. data |= (console_raw_dump[18] != 0) << 5;
  110. data |= (console_raw_dump[19] != 0) << 4;
  111. data |= (console_raw_dump[20] != 0) << 3;
  112. data |= (console_raw_dump[21] != 0) << 2;
  113. data |= (console_raw_dump[22] != 0) << 1;
  114. data |= (console_raw_dump[23] != 0);
  115.  
  116. // get crc byte, invert it, as per the protocol for
  117. // having a memory card attached
  118. controller_buffer[0] = crc_repeating_table[data] ^ 0xFF;
  119.  
  120. // send it
  121. console_send(controller_buffer, 1, 1);
  122.  
  123. // end of time critical code
  124. // was the address the rumble latch at 0xC000?
  125. // decode the first half of the address, bits
  126. // 8 through 15
  127. addr = 0;
  128. addr |= (console_raw_dump[0] != 0) << 7;
  129. addr |= (console_raw_dump[1] != 0) << 6;
  130. addr |= (console_raw_dump[2] != 0) << 5;
  131. addr |= (console_raw_dump[3] != 0) << 4;
  132. addr |= (console_raw_dump[4] != 0) << 3;
  133. addr |= (console_raw_dump[5] != 0) << 2;
  134. addr |= (console_raw_dump[6] != 0) << 1;
  135. addr |= (console_raw_dump[7] != 0);
  136.  
  137. if (addr == 0xC0) {
  138. // rumble = (data != 0);
  139. }
  140.  
  141. // Serial.println("It was 0x03: the write command");
  142. //Serial.print("Addr was 0x");
  143. //Serial.print(addr, HEX);
  144. //Serial.print(" and data was 0x");
  145. //Serial.println(data, HEX);
  146. break;
  147.  
  148. }
  149.  
  150. interrupts();
  151. }
  152.  
  153. void translate_raw_data()
  154. {
  155. memset(&N64_status, 0, sizeof(N64_status));
  156. for (int i = 0; i < 8; i++) {
  157. N64_status.data1 |= controller_buffer[i] ? (0x80 >> i) : 0;
  158. N64_status.data2 |= controller_buffer[8 + i] ? (0x80 >> i) : 0;
  159. N64_status.stick_x |= controller_buffer[16 + i] ? (0x80 >> i) : 0;
  160. N64_status.stick_y |= controller_buffer[24 + i] ? (0x80 >> i) : 0;
  161. }
  162. }
  163.  
  164. void print_CTRL_status()
  165. {
  166. char out[30];
  167. sprintf(out, "%i%i%i%i%i%i%i%i%i%i%i%i%i%i %i %i",
  168. N64_status.data1&16?1:0,
  169. N64_status.data1&32?1:0,
  170. N64_status.data1&64?1:0,
  171. N64_status.data1&128?1:0,
  172. N64_status.data2&32?1:0,
  173. N64_status.data2&16?1:0,
  174. N64_status.data2&0x08?1:0,
  175. N64_status.data2&0x04?1:0,
  176. N64_status.data2&0x01?1:0,
  177. N64_status.data2&0x02?1:0,
  178. N64_status.data1&0x08?1:0,
  179. N64_status.data1&0x04?1:0,
  180. N64_status.data1&0x01?1:0,
  181. N64_status.data1&0x02?1:0,
  182. N64_status.stick_x,
  183. N64_status.stick_y);
  184. Serial.println(out);
  185. }
  186.  
  187. //Send to console
  188. // completly copied and pasted to not mess up timings
  189. void console_send(unsigned char *buffer, char length, bool wide_stop)
  190. {
  191. asm volatile (";Starting N64 Send Routine");
  192. // Send these bytes
  193. char bits;
  194.  
  195. bool bit;
  196.  
  197. // This routine is very carefully timed by examining the assembly output.
  198. // Do not change any statements, it could throw the timings off
  199. //
  200. // We get 16 cycles per microsecond, which should be plenty, but we need to
  201. // be conservative. Most assembly ops take 1 cycle, but a few take 2
  202. //
  203. // I use manually constructed for-loops out of gotos so I have more control
  204. // over the outputted assembly. I can insert nops where it was impossible
  205. // with a for loop
  206.  
  207. asm volatile (";Starting outer for loop");
  208. outer_loop:
  209. {
  210. asm volatile (";Starting inner for loop");
  211. bits=8;
  212. inner_loop:
  213. {
  214. // Starting a bit, set the line low
  215. asm volatile (";Setting line to low");
  216. CNSL_LOW; // 1 op, 2 cycles
  217.  
  218. asm volatile (";branching");
  219. if (*buffer >> 7) {
  220. asm volatile (";Bit is a 1");
  221. // 1 bit
  222. // remain low for 1us, then go high for 3us
  223. // nop block 1
  224. asm volatile ("nop\nnop\nnop\nnop\nnop\n");
  225.  
  226. asm volatile (";Setting line to high");
  227. CNSL_HIGH;
  228.  
  229. // nop block 2
  230. // we'll wait only 2us to sync up with both conditions
  231. // at the bottom of the if statement
  232. asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  233. "nop\nnop\nnop\nnop\nnop\n"
  234. "nop\nnop\nnop\nnop\nnop\n"
  235. "nop\nnop\nnop\nnop\nnop\n"
  236. "nop\nnop\nnop\nnop\nnop\n"
  237. "nop\nnop\nnop\nnop\nnop\n"
  238. );
  239.  
  240. } else {
  241. asm volatile (";Bit is a 0");
  242. // 0 bit
  243. // remain low for 3us, then go high for 1us
  244. // nop block 3
  245. asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  246. "nop\nnop\nnop\nnop\nnop\n"
  247. "nop\nnop\nnop\nnop\nnop\n"
  248. "nop\nnop\nnop\nnop\nnop\n"
  249. "nop\nnop\nnop\nnop\nnop\n"
  250. "nop\nnop\nnop\nnop\nnop\n"
  251. "nop\nnop\nnop\nnop\nnop\n"
  252. "nop\n");
  253.  
  254. asm volatile (";Setting line to high");
  255. CNSL_HIGH;
  256.  
  257. // wait for 1us
  258. asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
  259.  
  260. }
  261. // end of the if, the line is high and needs to remain
  262. // high for exactly 16 more cycles, regardless of the previous
  263. // branch path
  264.  
  265. asm volatile (";finishing inner loop body");
  266. --bits;
  267. if (bits != 0) {
  268. // nop block 4
  269. // this block is why a for loop was impossible
  270. asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  271. "nop\nnop\nnop\nnop\n");
  272. // rotate bits
  273. asm volatile (";rotating out bits");
  274. *buffer <<= 1;
  275.  
  276. goto inner_loop;
  277. } // fall out of inner loop
  278. }
  279. asm volatile (";continuing outer loop");
  280. // In this case: the inner loop exits and the outer loop iterates,
  281. // there are /exactly/ 16 cycles taken up by the necessary operations.
  282. // So no nops are needed here (that was lucky!)
  283. --length;
  284. if (length != 0) {
  285. ++buffer;
  286. goto outer_loop;
  287. } // fall out of outer loop
  288. }
  289.  
  290. // send a single stop (1) bit
  291. // nop block 5
  292. asm volatile ("nop\nnop\nnop\nnop\n");
  293. CNSL_LOW;
  294. // wait 1 us, 16 cycles, then raise the line
  295. // take another 3 off for the wide_stop check
  296. // 16-2-3=11
  297. // nop block 6
  298. asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  299. "nop\nnop\nnop\nnop\nnop\n"
  300. "nop\n");
  301. if (wide_stop) {
  302. asm volatile (";another 1us for extra wide stop bit\n"
  303. "nop\nnop\nnop\nnop\nnop\n"
  304. "nop\nnop\nnop\nnop\nnop\n"
  305. "nop\nnop\nnop\nnop\n");
  306. }
  307.  
  308. CNSL_HIGH;
  309. }
  310.  
  311. void get_console_command()
  312. {
  313.  
  314. int bitcount;
  315. char *bitbin = console_raw_dump;
  316. int idle_wait;
  317.  
  318. func_top:
  319. console_command = 0;
  320.  
  321. bitcount = 8;
  322.  
  323. // wait to make sure the line is idle before
  324. // we begin listening
  325. for (idle_wait=32; idle_wait>0; --idle_wait) {
  326. if (!CNSL_QUERY) {
  327. idle_wait = 32;
  328. }
  329. }
  330.  
  331. read_loop:
  332. // wait for the line to go low
  333. while (CNSL_QUERY){}
  334.  
  335. // wait approx 2us and poll the line
  336. asm volatile (
  337. "nop\nnop\nnop\nnop\nnop\n"
  338. "nop\nnop\nnop\nnop\nnop\n"
  339. "nop\nnop\nnop\nnop\nnop\n"
  340. "nop\nnop\nnop\nnop\nnop\n"
  341. "nop\nnop\nnop\nnop\nnop\n"
  342. "nop\nnop\nnop\nnop\nnop\n"
  343. );
  344. if (CNSL_QUERY)
  345. console_command |= 0x01;
  346.  
  347. --bitcount;
  348. if (bitcount == 0)
  349. goto read_more;
  350.  
  351. console_command <<= 1;
  352.  
  353. // wait for line to go high again
  354. // I don't want this to execute if the loop is exiting, so
  355. // I couldn't use a traditional for-loop
  356. while (!CNSL_QUERY) {}
  357. goto read_loop;
  358.  
  359. read_more:
  360. switch (console_command)
  361. {
  362. case (0x03):
  363. // write command
  364. // we expect a 2 byte address and 32 bytes of data
  365. bitcount = 272 + 1; // 34 bytes * 8 bits per byte
  366. //Serial.println("command is 0x03, write");
  367. break;
  368. case (0x02):
  369. // read command 0x02
  370. // we expect a 2 byte address
  371. bitcount = 16 + 1;
  372. //Serial.println("command is 0x02, read");
  373. break;
  374. case (0x00):
  375. case (0x01):
  376. default:
  377. // get the last (stop) bit
  378. bitcount = 1;
  379. break;
  380. //default:
  381. // Serial.println(consele_command, HEX);
  382. // goto func_top;
  383. }
  384.  
  385. // make sure the line is high. Hopefully we didn't already
  386. // miss the high-to-low transition
  387. while (!CNSL_QUERY) {}
  388. read_loop2:
  389. // wait for the line to go low
  390. while (CNSL_QUERY){}
  391.  
  392. // wait approx 2us and poll the line
  393. asm volatile (
  394. "nop\nnop\nnop\nnop\nnop\n"
  395. "nop\nnop\nnop\nnop\nnop\n"
  396. "nop\nnop\nnop\nnop\nnop\n"
  397. "nop\nnop\nnop\nnop\nnop\n"
  398. "nop\nnop\nnop\nnop\nnop\n"
  399. "nop\nnop\nnop\nnop\nnop\n"
  400. );
  401. *bitbin = CNSL_QUERY;
  402. ++bitbin;
  403. --bitcount;
  404. if (bitcount == 0)
  405. return;
  406.  
  407. // wait for line to go high again
  408. while (!CNSL_QUERY) {}
  409. goto read_loop2;
  410. }
  411.  
  412. void CTRL_get()
  413. {
  414. // listen for the expected 8 bytes of data back from the controller and
  415. // blast it out to the N64_raw_dump array, one bit per byte for extra speed.
  416. // Afterwards, call translate_raw_data() to interpret the raw data and pack
  417. // it into the N64_status struct.
  418. asm volatile (";Starting to listen");
  419. unsigned char timeout;
  420. char bitcount = 32;
  421. char *bitbin = controller_buffer;
  422.  
  423. // Again, using gotos here to make the assembly more predictable and
  424. // optimization easier (please don't kill me)
  425. read_loop:
  426. timeout = 0x3f;
  427. // wait for line to go low
  428. while (CTRL_QUERY) {
  429. if (!--timeout)
  430. return;
  431. }
  432. // wait approx 2us and poll the line
  433. asm volatile (
  434. "nop\nnop\nnop\nnop\nnop\n"
  435. "nop\nnop\nnop\nnop\nnop\n"
  436. "nop\nnop\nnop\nnop\nnop\n"
  437. "nop\nnop\nnop\nnop\nnop\n"
  438. "nop\nnop\nnop\nnop\nnop\n"
  439. "nop\nnop\nnop\nnop\nnop\n"
  440. );
  441. *bitbin = CTRL_QUERY;
  442. ++bitbin;
  443. --bitcount;
  444. if (bitcount == 0)
  445. return;
  446.  
  447. // wait for line to go high again
  448. // it may already be high, so this should just drop through
  449. timeout = 0x3f;
  450. while (!CTRL_QUERY) {
  451. if (!--timeout)
  452. return;
  453. }
  454. goto read_loop;
  455.  
  456. }
Add Comment
Please, Sign In to add comment