spacepaste

  1.  
  2. <?php
  3. /**
  4. * DISASM6 - A NES-oriented 6502 disassembler which produces asm6 code
  5. * Created by Frantik 2011-2015
  6. *
  7. */
  8. define('VERSION', '1.5');
  9. define('LEFT_MARGIN', ' ');
  10. $time_start = microtime(true);
  11. $opcodes = array(
  12. // byte => legal, text, bytes, cycles, addressing mode
  13. 0x00 => array( 0, 'BRK', 1, 0, 0),
  14. 0x01 => array( 0, 'ORA', 2, 6, 7),
  15. 0x02 => array( 1, 'KIL', 1, 0, 0),
  16. 0x03 => array( 1, 'SLO', 2, 8, 7),
  17. 0x04 => array( 1, 'NOP', 2, 3, 4),
  18. 0x05 => array( 0, 'ORA', 2, 3, 4),
  19. 0x06 => array( 0, 'ASL', 2, 5, 4),
  20. 0x07 => array( 1, 'SLO', 2, 5, 4),
  21. 0x08 => array( 0, 'PHP', 1, 3, 0),
  22. 0x09 => array( 0, 'ORA', 2, 2, 0),
  23. 0x0A => array( 0, 'ASL', 1, 2, 0),
  24. 0x0B => array( 1, 'ANC', 2, 2, 0),
  25. 0x0C => array( 1, 'NOP', 3, 4, 1),
  26. 0x0D => array( 0, 'ORA', 3, 4, 1),
  27. 0x0E => array( 0, 'ASL', 3, 6, 1),
  28. 0x0F => array( 1, 'SLO', 3, 6, 1),
  29. 0x10 => array( 0, 'BPL', 2, 3, 0),
  30. 0x11 => array( 0, 'ORA', 2, 5, 8),
  31. 0x12 => array( 1, 'KIL', 1, 0, 0),
  32. 0x13 => array( 1, 'SLO', 2, 8, 8),
  33. 0x14 => array( 1, 'NOP', 2, 4, 5),
  34. 0x15 => array( 0, 'ORA', 2, 4, 5),
  35. 0x16 => array( 0, 'ASL', 2, 6, 5),
  36. 0x17 => array( 1, 'SLO', 2, 6, 5),
  37. 0x18 => array( 0, 'CLC', 1, 2, 0),
  38. 0x19 => array( 0, 'ORA', 3, 4, 3),
  39. 0x1A => array( 1, 'NOP', 1, 2, 0),
  40. 0x1B => array( 1, 'SLO', 3, 7, 3),
  41. 0x1C => array( 1, 'NOP', 3, 4, 2),
  42. 0x1D => array( 0, 'ORA', 3, 4, 2),
  43. 0x1E => array( 0, 'ASL', 3, 7, 2),
  44. 0x1F => array( 1, 'SLO', 3, 7, 2),
  45. 0x20 => array( 0, 'JSR', 3, 6, 10),
  46. 0x21 => array( 0, 'AND', 2, 6, 7),
  47. 0x22 => array( 1, 'KIL', 1, 0, 0),
  48. 0x23 => array( 1, 'RLA', 2, 8, 7),
  49. 0x24 => array( 0, 'BIT', 2, 3, 4),
  50. 0x25 => array( 0, 'AND', 2, 3, 4),
  51. 0x26 => array( 0, 'ROL', 2, 5, 4),
  52. 0x27 => array( 1, 'RLA', 2, 5, 4),
  53. 0x28 => array( 0, 'PLP', 1, 4, 0),
  54. 0x29 => array( 0, 'AND', 2, 2, 0),
  55. 0x2A => array( 0, 'ROL', 1, 2, 0),
  56. 0x2B => array( 1, 'ANC', 2, 2, 0),
  57. 0x2C => array( 0, 'BIT', 3, 4, 1),
  58. 0x2D => array( 0, 'AND', 3, 4, 1),
  59. 0x2E => array( 0, 'ROL', 3, 6, 1),
  60. 0x2F => array( 1, 'RLA', 3, 6, 1),
  61. 0x30 => array( 0, 'BMI', 2, 2, 0),
  62. 0x31 => array( 0, 'AND', 2, 5, 8),
  63. 0x32 => array( 1, 'KIL', 1, 0, 0),
  64. 0x33 => array( 1, 'RLA', 2, 8, 8),
  65. 0x34 => array( 1, 'NOP', 2, 4, 5),
  66. 0x35 => array( 0, 'AND', 2, 4, 5),
  67. 0x36 => array( 0, 'ROL', 2, 6, 5),
  68. 0x37 => array( 1, 'RLA', 2, 6, 5),
  69. 0x38 => array( 0, 'SEC', 1, 2, 0),
  70. 0x39 => array( 0, 'AND', 3, 4, 3),
  71. 0x3A => array( 1, 'NOP', 1, 2, 0),
  72. 0x3B => array( 1, 'RLA', 3, 7, 3),
  73. 0x3C => array( 1, 'NOP', 3, 4, 2),
  74. 0x3D => array( 0, 'AND', 3, 4, 2),
  75. 0x3E => array( 0, 'ROL', 3, 7, 2),
  76. 0x3F => array( 1, 'RLA', 3, 7, 2),
  77. 0x40 => array( 0, 'RTI', 1, 6, 0),
  78. 0x41 => array( 0, 'EOR', 2, 6, 7),
  79. 0x42 => array( 1, 'KIL', 1, 0, 0),
  80. 0x43 => array( 1, 'SRE', 2, 8, 7),
  81. 0x44 => array( 1, 'NOP', 2, 3, 4),
  82. 0x45 => array( 0, 'EOR', 2, 3, 4),
  83. 0x46 => array( 0, 'LSR', 2, 5, 4),
  84. 0x47 => array( 1, 'SRE', 2, 5, 4),
  85. 0x48 => array( 0, 'PHA', 1, 3, 0),
  86. 0x49 => array( 0, 'EOR', 2, 2, 0),
  87. 0x4A => array( 0, 'LSR', 1, 2, 0),
  88. 0x4B => array( 1, 'ALR', 2, 2, 0),
  89. 0x4C => array( 0, 'JMP', 3, 3, 10),
  90. 0x4D => array( 0, 'EOR', 3, 4, 1),
  91. 0x4E => array( 0, 'LSR', 3, 6, 1),
  92. 0x4F => array( 1, 'SRE', 3, 6, 1),
  93. 0x50 => array( 0, 'BVC', 2, 3, 0),
  94. 0x51 => array( 0, 'EOR', 2, 5, 8),
  95. 0x52 => array( 1, 'KIL', 1, 0, 0),
  96. 0x53 => array( 1, 'SRE', 2, 8, 8),
  97. 0x54 => array( 1, 'NOP', 2, 4, 5),
  98. 0x55 => array( 0, 'EOR', 2, 4, 5),
  99. 0x56 => array( 0, 'LSR', 2, 6, 5),
  100. 0x57 => array( 1, 'SRE', 2, 6, 5),
  101. 0x58 => array( 0, 'CLI', 1, 2, 0),
  102. 0x59 => array( 0, 'EOR', 3, 4, 3),
  103. 0x5A => array( 1, 'NOP', 1, 2, 0),
  104. 0x5B => array( 1, 'SRE', 3, 7, 3),
  105. 0x5C => array( 1, 'NOP', 3, 4, 2),
  106. 0x5D => array( 0, 'EOR', 3, 4, 2),
  107. 0x5E => array( 0, 'LSR', 3, 7, 2),
  108. 0x5F => array( 1, 'SRE', 3, 7, 2),
  109. 0x60 => array( 0, 'RTS', 1, 6, 0),
  110. 0x61 => array( 0, 'ADC', 2, 6, 7),
  111. 0x62 => array( 1, 'KIL', 1, 0, 0),
  112. 0x63 => array( 1, 'RRA', 2, 8, 7),
  113. 0x64 => array( 1, 'NOP', 2, 3, 4),
  114. 0x65 => array( 0, 'ADC', 2, 3, 4),
  115. 0x66 => array( 0, 'ROR', 2, 5, 4),
  116. 0x67 => array( 1, 'RRA', 2, 5, 4),
  117. 0x68 => array( 0, 'PLA', 1, 4, 0),
  118. 0x69 => array( 0, 'ADC', 2, 2, 0),
  119. 0x6A => array( 0, 'ROR', 1, 2, 0),
  120. 0x6B => array( 1, 'ARR', 2, 2, 0),
  121. 0x6C => array( 0, 'JMP', 3, 5, 9),
  122. 0x6D => array( 0, 'ADC', 3, 4, 1),
  123. 0x6E => array( 0, 'ROR', 3, 6, 1),
  124. 0x6F => array( 1, 'RRA', 3, 6, 1),
  125. 0x70 => array( 0, 'BVS', 2, 2, 0),
  126. 0x71 => array( 0, 'ADC', 2, 5, 8),
  127. 0x72 => array( 1, 'KIL', 1, 0, 0),
  128. 0x73 => array( 1, 'RRA', 2, 8, 8),
  129. 0x74 => array( 1, 'NOP', 2, 4, 5),
  130. 0x75 => array( 0, 'ADC', 2, 4, 5),
  131. 0x76 => array( 0, 'ROR', 2, 6, 5),
  132. 0x77 => array( 1, 'RRA', 2, 6, 5),
  133. 0x78 => array( 0, 'SEI', 1, 2, 0),
  134. 0x79 => array( 0, 'ADC', 3, 4, 3),
  135. 0x7A => array( 1, 'NOP', 1, 2, 0),
  136. 0x7B => array( 1, 'RRA', 3, 7, 3),
  137. 0x7C => array( 1, 'NOP', 3, 4, 2),
  138. 0x7D => array( 0, 'ADC', 3, 4, 2),
  139. 0x7E => array( 0, 'ROR', 3, 7, 2),
  140. 0x7F => array( 1, 'RRA', 3, 7, 2),
  141. 0x80 => array( 1, 'NOP', 2, 2, 0),
  142. 0x81 => array( 0, 'STA', 2, 6, 7),
  143. 0x82 => array( 1, 'NOP', 2, 2, 0),
  144. 0x83 => array( 1, 'SAX', 2, 6, 7),
  145. 0x84 => array( 0, 'STY', 2, 3, 4),
  146. 0x85 => array( 0, 'STA', 2, 3, 4),
  147. 0x86 => array( 0, 'STX', 2, 3, 4),
  148. 0x87 => array( 1, 'SAX', 2, 3, 4),
  149. 0x88 => array( 0, 'DEY', 1, 2, 0),
  150. 0x89 => array( 1, 'NOP', 2, 2, 0),
  151. 0x8A => array( 0, 'TXA', 1, 2, 0),
  152. 0x8B => array( 1, 'XAA', 2, 2, 0),
  153. 0x8C => array( 0, 'STY', 3, 4, 1),
  154. 0x8D => array( 0, 'STA', 3, 4, 1),
  155. 0x8E => array( 0, 'STX', 3, 4, 1),
  156. 0x8F => array( 1, 'SAX', 3, 4, 1),
  157. 0x90 => array( 0, 'BCC', 2, 3, 0),
  158. 0x91 => array( 0, 'STA', 2, 6, 8),
  159. 0x92 => array( 1, 'KIL', 1, 0, 0),
  160. 0x93 => array( 1, 'AHX', 2, 6, 8),
  161. 0x94 => array( 0, 'STY', 2, 4, 5),
  162. 0x95 => array( 0, 'STA', 2, 4, 5),
  163. 0x96 => array( 0, 'STX', 2, 4, 6),
  164. 0x97 => array( 1, 'SAX', 2, 4, 6),
  165. 0x98 => array( 0, 'TYA', 1, 2, 0),
  166. 0x99 => array( 0, 'STA', 3, 5, 3),
  167. 0x9A => array( 0, 'TXS', 1, 2, 0),
  168. 0x9B => array( 1, 'TAS', 1, 5, 0),
  169. 0x9C => array( 1, 'SHY', 3, 5, 2),
  170. 0x9D => array( 0, 'STA', 3, 5, 2),
  171. 0x9E => array( 1, 'SHX', 3, 5, 3),
  172. 0x9F => array( 1, 'AHX', 3, 5, 3),
  173. 0xA0 => array( 0, 'LDY', 2, 2, 0),
  174. 0xA1 => array( 0, 'LDA', 2, 6, 7),
  175. 0xA2 => array( 0, 'LDX', 2, 2, 0),
  176. 0xA3 => array( 1, 'LAX', 2, 6, 7),
  177. 0xA4 => array( 0, 'LDY', 2, 3, 4),
  178. 0xA5 => array( 0, 'LDA', 2, 3, 4),
  179. 0xA6 => array( 0, 'LDX', 2, 3, 4),
  180. 0xA7 => array( 1, 'LAX', 2, 3, 4),
  181. 0xA8 => array( 0, 'TAY', 1, 2, 0),
  182. 0xA9 => array( 0, 'LDA', 2, 2, 0),
  183. 0xAA => array( 0, 'TAX', 1, 2, 0),
  184. 0xAB => array( 1, 'LAX', 2, 2, 0),
  185. 0xAC => array( 0, 'LDY', 3, 4, 1),
  186. 0xAD => array( 0, 'LDA', 3, 4, 1),
  187. 0xAE => array( 0, 'LDX', 3, 4, 1),
  188. 0xAF => array( 1, 'LAX', 3, 4, 1),
  189. 0xB0 => array( 0, 'BCS', 2, 2, 0),
  190. 0xB1 => array( 0, 'LDA', 2, 5, 8),
  191. 0xB2 => array( 1, 'KIL', 1, 0, 0),
  192. 0xB3 => array( 1, 'LAX', 2, 5, 8),
  193. 0xB4 => array( 0, 'LDY', 2, 4, 5),
  194. 0xB5 => array( 0, 'LDA', 2, 4, 5),
  195. 0xB6 => array( 0, 'LDX', 2, 4, 6),
  196. 0xB7 => array( 1, 'LAX', 2, 4, 6),
  197. 0xB8 => array( 0, 'CLV', 1, 2, 0),
  198. 0xB9 => array( 0, 'LDA', 3, 4, 3),
  199. 0xBA => array( 0, 'TSX', 1, 2, 0),
  200. 0xBB => array( 1, 'LAS', 3, 4, 3),
  201. 0xBC => array( 0, 'LDY', 3, 4, 2),
  202. 0xBD => array( 0, 'LDA', 3, 4, 2),
  203. 0xBE => array( 0, 'LDX', 3, 4, 3),
  204. 0xBF => array( 1, 'LAX', 3, 4, 3),
  205. 0xC0 => array( 0, 'CPY', 2, 2, 0),
  206. 0xC1 => array( 0, 'CMP', 2, 6, 7),
  207. 0xC2 => array( 1, 'NOP', 2, 2, 0),
  208. 0xC3 => array( 1, 'DCP', 2, 8, 7),
  209. 0xC4 => array( 0, 'CPY', 2, 3, 4),
  210. 0xC5 => array( 0, 'CMP', 2, 3, 4),
  211. 0xC6 => array( 0, 'DEC', 2, 5, 4),
  212. 0xC7 => array( 1, 'DCP', 2, 5, 4),
  213. 0xC8 => array( 0, 'INY', 1, 2, 0),
  214. 0xC9 => array( 0, 'CMP', 2, 2, 0),
  215. 0xCA => array( 0, 'DEX', 1, 2, 0),
  216. 0xCB => array( 1, 'AXS', 2, 2, 0),
  217. 0xCC => array( 0, 'CPY', 3, 4, 1),
  218. 0xCD => array( 0, 'CMP', 3, 4, 1),
  219. 0xCE => array( 0, 'DEC', 3, 6, 1),
  220. 0xCF => array( 1, 'DCP', 3, 6, 1),
  221. 0xD0 => array( 0, 'BNE', 2, 3, 0),
  222. 0xD1 => array( 0, 'CMP', 2, 5, 8),
  223. 0xD2 => array( 1, 'KIL', 1, 0, 0),
  224. 0xD3 => array( 1, 'DCP', 2, 8, 8),
  225. 0xD4 => array( 1, 'NOP', 2, 4, 5),
  226. 0xD5 => array( 0, 'CMP', 2, 4, 5),
  227. 0xD6 => array( 0, 'DEC', 2, 6, 5),
  228. 0xD7 => array( 1, 'DCP', 2, 6, 5),
  229. 0xD8 => array( 0, 'CLD', 1, 2, 0),
  230. 0xD9 => array( 0, 'CMP', 3, 4, 3),
  231. 0xDA => array( 1, 'NOP', 1, 2, 0),
  232. 0xDB => array( 1, 'DCP', 3, 7, 3),
  233. 0xDC => array( 1, 'NOP', 3, 4, 2),
  234. 0xDD => array( 0, 'CMP', 3, 4, 2),
  235. 0xDE => array( 0, 'DEC', 3, 7, 2),
  236. 0xDF => array( 1, 'DCP', 3, 7, 2),
  237. 0xE0 => array( 0, 'CPX', 2, 2, 0),
  238. 0xE1 => array( 0, 'SBC', 2, 6, 7),
  239. 0xE2 => array( 1, 'NOP', 2, 2, 0),
  240. 0xE3 => array( 1, 'ISC', 2, 8, 7),
  241. 0xE4 => array( 0, 'CPX', 2, 3, 4),
  242. 0xE5 => array( 0, 'SBC', 2, 3, 4),
  243. 0xE6 => array( 0, 'INC', 2, 5, 4),
  244. 0xE7 => array( 1, 'ISC', 2, 5, 4),
  245. 0xE8 => array( 0, 'INX', 1, 2, 0),
  246. 0xE9 => array( 0, 'SBC', 2, 2, 0),
  247. 0xEA => array( 0, 'NOP', 1, 2, 0),
  248. 0xEB => array( 1, 'SBC', 2, 2, 0),
  249. 0xEC => array( 0, 'CPX', 3, 4, 1),
  250. 0xED => array( 0, 'SBC', 3, 4, 1),
  251. 0xEE => array( 0, 'INC', 3, 6, 1),
  252. 0xEF => array( 1, 'ISC', 3, 6, 1),
  253. 0xF0 => array( 0, 'BEQ', 2, 2, 0),
  254. 0xF1 => array( 0, 'SBC', 2, 5, 8),
  255. 0xF2 => array( 1, 'KIL', 1, 0, 0),
  256. 0xF3 => array( 1, 'ISC', 2, 8, 8),
  257. 0xF4 => array( 1, 'NOP', 2, 4, 5),
  258. 0xF5 => array( 0, 'SBC', 2, 4, 5),
  259. 0xF6 => array( 0, 'INC', 2, 6, 5),
  260. 0xF7 => array( 1, 'ISC', 2, 6, 5),
  261. 0xF8 => array( 0, 'SED', 1, 2, 0),
  262. 0xF9 => array( 0, 'SBC', 3, 4, 3),
  263. 0xFA => array( 1, 'NOP', 1, 2, 0),
  264. 0xFB => array( 1, 'ISC', 3, 7, 3),
  265. 0xFC => array( 1, 'NOP', 3, 4, 2),
  266. 0xFD => array( 0, 'SBC', 3, 4, 2),
  267. 0xFE => array( 0, 'INC', 3, 7, 2),
  268. 0xFF => array( 1, 'ISC', 3, 7, 2),
  269. );
  270. $registers = array(
  271. '2000' => 'PPUCTRL',
  272. '2001' => 'PPUMASK',
  273. '2002' => 'PPUSTATUS',
  274. '2003' => 'OAMADDR',
  275. '2004' => 'OAMDATA',
  276. '2005' => 'PPUSCROLL',
  277. '2006' => 'PPUADDR',
  278. '2007' => 'PPUDATA',
  279. '4000' => 'SQ1_VOL',
  280. '4001' => 'SQ1_SWEEP',
  281. '4002' => 'SQ1_LO',
  282. '4003' => 'SQ1_HI',
  283. '4004' => 'SQ2_VOL',
  284. '4005' => 'SQ2_SWEEP',
  285. '4006' => 'SQ2_LO',
  286. '4007' => 'SQ2_HI',
  287. '4008' => 'TRI_LINEAR',
  288. '400A' => 'TRI_LO',
  289. '400B' => 'TRI_HI',
  290. '400C' => 'NOISE_VOL',
  291. '400E' => 'NOISE_LO',
  292. '400F' => 'NOISE_HI',
  293. '4010' => 'DMC_FREQ',
  294. '4011' => 'DMC_RAW',
  295. '4012' => 'DMC_START',
  296. '4013' => 'DMC_LEN',
  297. '4014' => 'OAM_DMA',
  298. '4015' => 'SND_CHN',
  299. '4016' => 'JOY1',
  300. '4017' => 'JOY2',
  301. );
  302. // used for branch opcodes
  303. function addressOffset($value, $offset)
  304. {
  305. $offset = hexdec($offset);
  306. $offset += 2; // length of brance command
  307. if ($offset > 0x80)
  308. {
  309. $offset = $offset - 0x100;
  310. }
  311. else
  312. {
  313. //$offset += 2;
  314. }
  315. return str_pad(dechex($value + $offset), 4, '0', STR_PAD_LEFT);
  316. }
  317. function isValidLabel($addr)
  318. {
  319. global $origin;
  320. $newaddr = hexdec($addr);
  321. return ($newaddr >= $origin && $newaddr < 0xFFFA);
  322. }
  323. function addValidLabel($addr, &$labels)
  324. {
  325. if (isValidLabel($addr) && !isset($labels[$addr]))
  326. {
  327. $labels[$addr] = true;
  328. return true;
  329. }
  330. return false;
  331. }
  332. function addVector($vector, $str, &$labels)
  333. {
  334. if (isset($labels[$vector]))
  335. {
  336. if ($labels[$vector] === true)
  337. {
  338. $labels[$vector] = $str;
  339. }
  340. elseif (is_array($labels[$vector]))
  341. {
  342. if( !in_array($str, $labels[$vector]))
  343. {
  344. $labels[$vector][] = $str;
  345. }
  346. }
  347. elseif ($labels[$vector] !== $str)
  348. {
  349. $labels[$vector] = array($labels[$vector], $str);
  350. }
  351. }
  352. else
  353. {
  354. $labels[$vector] = $str;
  355. }
  356. }
  357. function wordStr($str)
  358. {
  359. return dechex_pad(ord($str[1])) . dechex_pad(ord($str[0]));
  360. }
  361. // make sure hex values have leading zeros
  362. function dechex_pad($dec , $len = 2)
  363. {
  364. if ($dec > 0xFF)
  365. {
  366. $len = 4;
  367. }
  368. elseif ($dec > 0xFFFF)
  369. {
  370. $len = 6;
  371. }
  372. return str_pad(dechex($dec), $len, '0', STR_PAD_LEFT);
  373. }
  374. // make sure binary values have leading zeros
  375. function decbin_pad($dec , $len = 8)
  376. {
  377. if ($dec > 0xFF)
  378. {
  379. $len = 16;
  380. }
  381. elseif ($dec > 0xFFFF)
  382. {
  383. $len = 32;
  384. }
  385. return str_pad(decbin($dec), $len, '0', STR_PAD_LEFT);
  386. }
  387. function commentLine($len = 80)
  388. {
  389. return ";" . str_repeat('-', $len - 1) . "\n";
  390. }
  391. function commentHeader($text, $initialNL = true, $initialLine = true)
  392. {
  393. $ret = ($initialNL ? "\n" : '') . ($initialLine ? commentLine() : '');
  394. $ret .= "; $text";
  395. $ret .= "\n" . commentLine();
  396. return $ret;
  397. }
  398. function strToHex($str, $fancy = false)
  399. {
  400. $len = strlen($str);
  401. $ret = '';
  402. for($i = 0; $i < $len; $i++)
  403. {
  404. $ret .= ($fancy ? '$' : '') . dechex_pad(ord($str[$i]));
  405. if ($i < $len - 1)
  406. { $ret .= ($fancy ? ',' : '') . " ";
  407. }
  408. }
  409. return $ret;
  410. }
  411. function getHeaderInfo($file)
  412. {
  413. $oldloc = ftell($file);
  414. $head = fread($file, 0x4);
  415. if ($head == "NES" . chr(0x1A))
  416. {
  417. $info = new stdClass;
  418. $info->head = $head;
  419. $info->prg = ord(fread($file, 1));
  420. $info->chr = ord(fread($file, 1));
  421. $info->ctrl_1 = ord(fread($file, 1));
  422. $info->ctrl_2 = ord(fread($file, 1));
  423. $info->tail = fread($file, 8);
  424. $info->mirroring = $info->ctrl_1 & bindec('00000001');
  425. $info->sram = ($info->ctrl_1 & bindec('0000010')) >> 1;
  426. $info->trainer = ($info->ctrl_1 & bindec('00000100')) >> 2;
  427. $info->fourscreen = ($info->ctrl_1 & bindec('00001000')) >> 3;
  428. $info->romtype = $info->ctrl_2 & bindec('00000011');
  429. $info->mapper = (($info->ctrl_1 & bindec('11110000')) >> 4)
  430. + $info->ctrl_2 & bindec('00001111');
  431. return $info;
  432. }
  433. else
  434. {
  435. fseek($file, $oldloc);
  436. return false;
  437. }
  438. }
  439. function processHeaderInfo($info)
  440. {
  441. global $labelLen;
  442. $pad = 30 + $labelLen;
  443. $ret = '';
  444. if (is_object($info))
  445. {
  446. //$ret .= commentLine();
  447. $ret .= commentHeader("iNES Header");
  448. $ret .= str_pad(LEFT_MARGIN . '.db "NES", $1A', $pad) . " ; Header\n";
  449. $ret .= str_pad(LEFT_MARGIN . ".db $info->prg", $pad) . " ; $info->prg x 16k PRG banks\n";
  450. $ret .= str_pad(LEFT_MARGIN . ".db $info->chr", $pad) . " ; $info->chr x 8k CHR banks\n";
  451. $ret .= str_pad(LEFT_MARGIN . ".db %" . decbin_pad($info->ctrl_1), $pad) . " ; Mirroring: " . ($info->mirroring ? 'Vertical' : 'Horizontal') . "\n";
  452. $ret .= str_repeat(" ", $pad) . " ; SRAM: " . ($info->sram ? 'Enabled' : 'Not used') . "\n";
  453. $ret .= str_repeat(" ", $pad) . " ; 512k Trainer: " . ($info->trainer ? 'Enabled' : 'Not used') . "\n";
  454. $ret .= str_repeat(" ", $pad) . " ; 4 Screen VRAM: " . ($info->fourscreen ? 'Enabled' : 'Not used') . "\n";
  455. $ret .= str_repeat(" ", $pad) . " ; Mapper: " . $info->mapper . "\n";
  456. switch($info->romtype)
  457. {
  458. case 0:
  459. $romtype = 'NES';
  460. break;
  461. case 1:
  462. $romtype = 'VS Unisystem';
  463. break;
  464. case 2:
  465. $romtype = 'Playchoice 10';
  466. break;
  467. }
  468. $ret .= str_pad(LEFT_MARGIN . ".db %" . decbin_pad($info->ctrl_2), $pad) . " ; RomType: " . $romtype . "\n";
  469. $ret .= str_pad(LEFT_MARGIN . ".hex " . strToHex(substr($info->tail,0,4)), $pad) . " ; iNES Tail \n";
  470. $ret .= str_pad(LEFT_MARGIN . ".hex " . strToHex(substr($info->tail,4)), $pad) . " \n";
  471. return $ret;
  472. }
  473. return false;
  474. }
  475. function toLittleEndianStr($str)
  476. {
  477. return $str[2] . $str[3] . " " . $str[0] . $str[1];
  478. }
  479. function processVectors($nmi, $reset, $break)
  480. {
  481. global $labelLen;
  482. $marginLen = strlen(LEFT_MARGIN);
  483. $pad = 30 + $marginLen;
  484. $ret = commentHeader('Vector Table');
  485. $line1 = str_pad("vectors:", $marginLen);
  486. $line1 .= ".dw nmi";
  487. $ret = $ret . str_pad($line1, $pad) . ' ; $fffa: ' . toLittleEndianStr($nmi) . " Vector table\n";
  488. $ret .= str_pad(LEFT_MARGIN . ".dw reset", $pad) . ' ; $fffc: ' . toLittleEndianStr($reset) . " Vector table\n";
  489. $ret .= str_pad(LEFT_MARGIN . ".dw irq", $pad) . ' ; $fffe: ' . toLittleEndianStr($break) . " Vector table\n";
  490. return $ret;
  491. }
  492. function baseToDec($str)
  493. {
  494. switch ($str{0})
  495. {
  496. case '0':
  497. if ($str[1] !== 'x')
  498. { break;
  499. }
  500. else
  501. {
  502. return hexdec(substr($str,2));
  503. }
  504. break;
  505. case '$':
  506. return hexdec(substr($str,1));
  507. break;
  508. case '$':
  509. return bindec(substr($str,1));
  510. break;
  511. }
  512. return $str;
  513. }
  514. function readLabels($filename)
  515. {
  516. $arr = readLabelText(file_get_contents($filename));
  517. return $arr;
  518. }
  519. function readLabelText($str)
  520. {
  521. $arr = array();
  522. $len = 0;
  523. $str = preg_replace('%;.*$%m', '', $str);
  524. if (preg_match_all('%^\s*([a-zA-Z0-9_\-\+\@]*)\s*\=\s*([\$\%]*)([a-fA-F0-9]*)%m', $str, $matches))
  525. {
  526. foreach($matches[0] as $n => $v)
  527. { $matches[1][$n] = trim($matches[1][$n]);
  528. $thislen = strlen($matches[1][$n]);
  529. if ($thislen > $len)
  530. {
  531. $len = $thislen;
  532. }
  533. if (strlen($matches[1][$n]) > 0)
  534. {
  535. if ($matches[2][$n] == '')
  536. { $matches[3][$n] = dechex_pad($matches[3][$n]);
  537. }
  538. if ($matches[2][$n] == '%')
  539. {
  540. $matches[3][$n] = dechex_pad(bindec($matches[3][$n]));
  541. }
  542. $arr[strtolower($matches[3][$n])] = $matches[1][$n];
  543. }
  544. }
  545. }
  546. $arr['maxLength'] = $len;
  547. return $arr;
  548. }
  549. function outputLabels($arr, $text)
  550. {
  551. global $origin;
  552. $ret = commentHeader($text);
  553. foreach ($arr as $n => $v)
  554. {
  555. if ($n == 'maxLength')
  556. {
  557. continue;
  558. }
  559. if (hexdec($n) < $origin)
  560. {
  561. $ret .= str_pad($v , 20) . ' = $' . $n . "\n";
  562. }
  563. }
  564. return $ret;
  565. }
  566. function outputHelp($text = false)
  567. {
  568. global $argv;
  569. $dasm = pathinfo($argv[0], PATHINFO_BASENAME);
  570. echo <<<ENDOFSTRING
  571. Usage:
  572. disasm6 <file> [-t <file>] [-o #] [-l <file>] [-cdl <file>] [-cdlo #] [-d] [-i]
  573. [-h] [-c] [-p #] [-r] [-lc] [-uc] [-fs #] [-cs #] [-fe #] [-ce <#>]
  574. [-len #] [-iw] [-m2]
  575. <file> The file to disassemble
  576. t target <file> Target output filename (default is input filename.asm)
  577. o origin # Set the program origin.
  578. (default: 0x8000 for 32k roms, 0xC000 for 16k roms)
  579. l labels <file> Load user defined labels from file
  580. cdl cdl <file> Use a code/data log generated by FCEUX
  581. cdlo cdloffset # Set the offset of the cdl file
  582. d nodetect Disable 16kb prg size detection
  583. i ignoreheader Do not look for iNES header
  584. h noheader Do not include iNES header (if found) in disassembly
  585. c chr Export CHR-ROM as file and include in disassembly
  586. p passes # Maximum number of passes (default: 9)
  587. r registers Use default NES registers
  588. lc lowercase Use lowercase mnemonics [default]
  589. uc uppercase Use uppercase mnemonics
  590. fs filestart Start reading at a specific file location
  591. cs codestart Start reading at a specific code location
  592. fe fileend Stop reading at a specific file location
  593. ce codeend Stop reading at a specific code location
  594. len length Number of bytes to read
  595. iw ignorewrites Ignore writes to \$8000 - \$FFFF
  596. m2 mapper2 Enable mapper 2 (UxROM) support
  597. ENDOFSTRING;
  598. echo "\n" . ($text ? "\nERROR: $text\n" : '');
  599. exit;
  600. }
  601. function isCounterLabel($addr, $labels)
  602. {
  603. $addr= dechex_pad($addr);
  604. if (isset($labels[$addr]))
  605. {
  606. if (preg_match('%^([^\+\-]+)[\+\-][0-9]+%', $labels[$addr]))
  607. {
  608. return false;
  609. }
  610. return true;
  611. }
  612. return false;
  613. }
  614. // Program start
  615. $head = "DISASM6 v" . VERSION ." - A NES-oriented 6502 disassembler - Created by Frantik 2015";
  616. echo "\n$head\n" . str_repeat('-', 79) . "\n";
  617. if (!isset($argv[1]))
  618. {
  619. outputHelp();
  620. }
  621. elseif (!file_exists($argv[1]))
  622. {
  623. outputHelp("File not found\n");
  624. }
  625. else
  626. {
  627. $filename = $argv[1];
  628. }
  629. $origin = 0x8000;
  630. $showHeader = true;
  631. $includeChr = false;
  632. $includeReg = false;
  633. $originOverride = false;
  634. $noDetect = false;
  635. $shortname = pathinfo($filename, PATHINFO_FILENAME);
  636. $labelFile = false;
  637. $cdlFilename = false;
  638. $ignoreHeader = false;
  639. $fileStart = 0;
  640. $fileStartOverride = false;
  641. $fileLength = 0x10000;
  642. $lengthOverride = false;
  643. $fileEnd = 0;
  644. $fileEndOverride = false;
  645. $codeStart = 0;
  646. $codeStartOverride = false;
  647. $codeEnd = 0;
  648. $codeEndOverride = false;
  649. $cdlOffset = 0;
  650. $ignoreWrites = false;
  651. $useLowerCase = true;
  652. $usingMapper2 = false;
  653. $lastPass = 9;
  654. $marginLen = strlen(LEFT_MARGIN);
  655. // check command line params
  656. for ($i = 2; $i < $argc; $i++)
  657. {
  658. $nextParam = false;
  659. if (isset($argv[$i + 1]) && substr($argv[$i + 1],0,1) != '-')
  660. { $nextParam = $argv[$i + 1];
  661. }
  662. switch (strtolower($argv[$i]))
  663. {
  664. case "-o":
  665. case "-origin":
  666. if (!$nextParam)
  667. { outputHelp("Must specify a valid origin");
  668. }
  669. $origin = baseToDec($argv[++$i]);
  670. $originOverride = true;
  671. break;
  672. case "-cs":
  673. case "-codestart":
  674. if (!$nextParam)
  675. { outputHelp("Must specify a valid code start location ");
  676. }
  677. $codeStart = baseToDec($argv[++$i]);
  678. $codeStartOverride = true;
  679. break;
  680. case "-fs":
  681. case "-filestart":
  682. if (!$nextParam)
  683. { outputHelp("Must specify a valid file start location ");
  684. }
  685. $fileStart = baseToDec($argv[++$i]);
  686. $fileStartOverride = true;
  687. break;
  688. case '-len':
  689. case '-length':
  690. if (!$nextParam)
  691. { outputHelp("Must specify a valid length to read");
  692. }
  693. $fileLength = baseToDec($argv[++$i]); // this will be tweaked later
  694. $lengthOverride = true;
  695. break;
  696. case "-fe":
  697. case "-fileend":
  698. if (!$nextParam)
  699. { outputHelp("Must specify a valid file end location ");
  700. }
  701. $fileEnd = baseToDec($argv[++$i]);
  702. $fileEndOverride = true;
  703. break;
  704. case "-ce":
  705. case "-codeend":
  706. if (!$nextParam)
  707. { outputHelp("Must specify a valid code end location ");
  708. }
  709. $fileLength = baseToDec($argv[++$i]); // will NOT be tweaked since lengthOverride isn't enable
  710. $codeEndOverride = true;
  711. break;
  712. case "-h":
  713. case '-noheader';
  714. $showHeader = false;
  715. break;
  716. case "-i":
  717. case '-ignoreheader';
  718. $ignoreHeader = true;
  719. break;
  720. case "-c":
  721. case "-chr":
  722. $includeChr = true;
  723. break;
  724. case '-r':
  725. case '-registers':
  726. $includeReg = true;
  727. break;
  728. case '-t':
  729. case '-target':
  730. if (!$nextParam)
  731. { outputHelp("You must specify a target file");
  732. }
  733. $shortname = pathinfo(preg_replace("%[^a-zA-Z0-9_\-\. ]%", '', $argv[++$i]), PATHINFO_FILENAME);
  734. break;
  735. case '-p':
  736. case '-passes':
  737. if (!$nextParam || !is_numeric($nextParam))
  738. { outputHelp("You must specify a number of passes");
  739. }
  740. $lastPass = (int)$argv[++$i];
  741. break;
  742. case '-nodetect':
  743. case '-d':
  744. $noDetect = true;
  745. break;
  746. case '-l':
  747. case '-labels':
  748. if (!$nextParam || !file_exists($nextParam))
  749. { outputHelp("You must specify a valid file");
  750. }
  751. $labelFile = $argv[++$i];
  752. break;
  753. case '-cdl':
  754. if (!$nextParam || !file_exists($nextParam))
  755. { outputHelp("You must specify a valid file");
  756. }
  757. $cdlFilename = $argv[++$i];
  758. break;
  759. case '-cdlo':
  760. case '-cdloffset':
  761. if (!$nextParam)
  762. { outputHelp("You must specify a valid offset for the CDL");
  763. }
  764. $cdlOffset = baseToDec($argv[++$i]);
  765. break;
  766. case '-lc':
  767. case '-lowercase':
  768. $useLowerCase = true;
  769. break;
  770. case '-cc':
  771. case '-uppercase':
  772. $useLowerCase = false;
  773. break;
  774. case '-iw':
  775. case '-ignorewrites':
  776. $ignoreWrites = true;
  777. break;
  778. case '-m2':
  779. case '-mapper2':
  780. $usingMapper2 = true;
  781. break;
  782. }
  783. }
  784. if ($fileEndOverride)
  785. {
  786. $fileLength = $fileStart + $fileEnd;
  787. $lengthOverride = true;
  788. }
  789. $file = fopen($filename, 'r');
  790. $pass = 1;
  791. $oldLabels = false;
  792. $initLabels = array(
  793. 'fffa' => "vectors",
  794. 'fffc' => true,
  795. 'fffe' => true,
  796. );
  797. if ($includeReg)
  798. {
  799. $initLabels += $registers;
  800. }
  801. $labelLen = 0;
  802. if ($labelFile !== false)
  803. {
  804. $fileLabels = readLabels($labelFile);
  805. //$mapperArr = $fileLabels['mapperArr'];
  806. //unset($fileLabels['mapperArr']);
  807. $labelLen = $fileLabels['maxLength'] - 10;
  808. $labelLen = $labelLen < 0 ? 0: $labelLen;
  809. unset ($fileLabels['maxLength']);
  810. $initLabels += $fileLabels;
  811. }
  812. $cdlFile = false;
  813. if ($cdlFilename !== false)
  814. {
  815. $cdlFile = fopen($cdlFilename, 'r');
  816. $cdlByte = 0;
  817. }
  818. $header = false;
  819. $theOldLabel = '';
  820. $theText = commentHeader(pathinfo($filename, PATHINFO_BASENAME) . " disasembled by DISASM6 v" . VERSION, false);
  821. //$invalidCounter = 0;
  822. $prgBank = 0;
  823. $theLabel = '';
  824. // This loop is done x times
  825. // The first pass we just collect addesses
  826. // The next passes we look for new addresses
  827. //
  828. // The last pass we build the actual output
  829. while ($pass <= $lastPass)
  830. {
  831. if ($pass < 3)
  832. { $labels = $initLabels;
  833. }
  834. $prgLabels = $initLabels;
  835. $counter = $origin;
  836. if ($fileStartOverride && !$codeStartOverride)
  837. {
  838. fseek($file, $fileStart);
  839. }
  840. if (!$ignoreHeader)
  841. {
  842. $headerInfo = getHeaderInfo($file);
  843. }
  844. else
  845. {
  846. $headerInfo = false;
  847. }
  848. if ($codeStartOverride)
  849. {
  850. fseek($file, $fileStart);
  851. }
  852. if ($headerInfo)
  853. { $oldPrg = $headerInfo->prg;
  854. }
  855. else
  856. {
  857. }
  858. // do this stuff only on the first pass
  859. if ($pass == 1)
  860. {
  861. $oldDidDrawLine = false;
  862. $oldLabels = $labels;
  863. // check for 16k roms
  864. if (!$noDetect)
  865. { $newPrg = false;
  866. if ($headerInfo && $headerInfo->prg == 2)
  867. {
  868. $prg0 = fread($file, 0x4000);
  869. $prg1 = fread($file, 0x4000);
  870. fseek($file, $fileStart + 0x10);
  871. if ($prg0 === $prg1 && $headerInfo->mapper == 0)
  872. { echo "PRG Banks 0 and 1 are identical, 16k PRG suspected, use -d to disable check\n";
  873. $newPrg = 1;
  874. $origin = $originOverride ? $origin : 0xc000;
  875. if ($cdlFilename !== false)
  876. {
  877. $cdlOffset += 0x4000;
  878. }
  879. }
  880. }
  881. elseif ($headerInfo && $headerInfo->prg == 1)
  882. {
  883. $origin = $originOverride ? $origin : 0xc000;
  884. }
  885. }
  886. echo "Using Origin: 0x" . dechex_pad($origin) . "\n\n";
  887. if ($headerInfo !== false)
  888. {
  889. echo 'NES Header Found - ' . ($showHeader ? 'included in disassembly' : 'not included') ."\n";
  890. }
  891. if ($labelFile !== false)
  892. {
  893. echo "Using user defined labels\n";
  894. }
  895. if ($includeReg)
  896. {
  897. echo "Using NES registers\n";
  898. }
  899. if ($cdlFilename !== false)
  900. {
  901. echo "Using code/data log\n";
  902. }
  903. if ($ignoreWrites !== false)
  904. {
  905. echo "Writes to PRG will not create labels\n";
  906. }
  907. if ($usingMapper2 !== false)
  908. {
  909. echo "Mapper 2 (UxROM) support enabled\n";
  910. }
  911. if ($fileStartOverride && !$codeStartOverride)
  912. {
  913. echo "Starting at file location 0x" . dechex_pad($fileStart) ."\n";
  914. }
  915. if ($codeStartOverride)
  916. {
  917. $fileStart = $codeStart - $origin + ($headerInfo ? 10 : 0);
  918. $origin = $codeStart;
  919. $originOverride = true;
  920. $fileStartOverride = true;
  921. fseek($file, $fileStart);
  922. $cdlOffset += $fileStart - ($headerInfo ? 10 : 0);
  923. echo "Starting at code location $" . dechex_pad($fileStart)."\n";
  924. }
  925. if ($lengthOverride)
  926. {
  927. echo "Reading 0x" . dechex_pad($fileLength) . " bytes\n";
  928. $fileLength += $origin - ($headerInfo ? 0x10 : 0);
  929. }
  930. if ($includeChr && $headerInfo !== false)
  931. {
  932. //echo "Using CHR-ROM\n";
  933. }
  934. echo "\n";
  935. }
  936. if ($cdlFilename !== false)
  937. {
  938. fseek($cdlFile, $cdlOffset);
  939. }
  940. // if 16k rom, update prg info
  941. if ($newPrg)
  942. {
  943. $headerInfo->prg = $newPrg;
  944. }
  945. // do this stuff only on the lass pass
  946. if ($pass == $lastPass)
  947. {
  948. if ($labelFile !== false)
  949. {
  950. $theText .= outputLabels($fileLabels, "User Defined Labels");
  951. }
  952. if ($includeReg)
  953. {
  954. $theText .= outputLabels($registers, "Registers");
  955. }
  956. $header = processHeaderInfo($headerInfo);
  957. if ($header !== false && $showHeader)
  958. {
  959. $theText .= $header;
  960. }
  961. $theText .= commentHeader("Program Origin");
  962. $theText .= str_pad(LEFT_MARGIN . ".org $" . dechex_pad($counter), 30 + $labelLen) . " ; Set program counter\n";
  963. $theText .= commentHeader('ROM Start');
  964. }
  965. // read the file
  966. // each pass of this loop completes one line of output
  967. $counter = $origin;
  968. echo "Starting pass $pass " . ($pass == $lastPass ? "(final) " : '' ) . "... ";
  969. while (!feof($file) && $counter < $fileLength)
  970. {
  971. $add = false;
  972. $invalidText = "Invalid Opcode";
  973. $didDrawLine = false;
  974. // handle mapper 2
  975. if ($usingMapper2
  976. && $headerInfo
  977. && $headerInfo->mapper == 2
  978. && $counter == 0xC000
  979. && $prgBank < ($headerInfo->prg - 1)
  980. )
  981. {
  982. $counter = 0x8000;
  983. $prgBank++;
  984. if ($pass == $lastPass)
  985. { $theText .= commentHeader("PRG Bank $prgBank");
  986. $theText .= LEFT_MARGIN . ".base 0x8000\n";
  987. $theText .= commentLine();
  988. }
  989. continue;
  990. }
  991. // handle vectors
  992. if ($pass < $lastPass && $counter == 0xFFFA)
  993. {
  994. $nmi = wordStr(fread($file, 2));
  995. $reset = wordStr(fread($file, 2));
  996. $break = wordStr(fread($file, 2));
  997. addVector($nmi, "nmi", $labels);
  998. addVector($reset, "reset", $labels);
  999. addVector($break, "irq", $labels);
  1000. $prgLabels[$nmi] = true;
  1001. $prgLabels[$reset] = true;
  1002. $prgLabels[$break] = true;
  1003. $counter += 6;
  1004. continue;
  1005. }
  1006. elseif($pass == $lastPass && $counter == 0xFFFA)
  1007. {
  1008. $theText .= processVectors($nmi, $reset, $break);
  1009. fread($file, 6);
  1010. $counter += 6;
  1011. continue;
  1012. }
  1013. //read opcode
  1014. $opcode = ord(fread($file, 1));
  1015. $isInvalid = $opcodes[$opcode][0];
  1016. $mnemonic = $opcodes[$opcode][1];
  1017. $byteLen = $opcodes[$opcode][2];
  1018. $addressingType = $opcodes[$opcode][4];
  1019. $isDataByte = false;
  1020. $dataStr = 'Suspected data';
  1021. // check code/data log - if data, don' process as an opcode
  1022. if ($cdlFilename !== false)
  1023. {
  1024. $newCdlByte = ord(fread($cdlFile, 1)) ;
  1025. // draw line between data and code
  1026. if ($pass == $lastPass
  1027. && !$oldDidDrawLine
  1028. && $counter !== $origin
  1029. && $newCdlByte !== 0
  1030. && (($newCdlByte & bindec('00000001')) != ($cdlByte & bindec('00000001')))
  1031. )
  1032. {
  1033. $theText .= "\n" . commentLine();
  1034. $didDrawLine = true;
  1035. }
  1036. // check if the CDL byte is known, if known, copy, otherwise do some checks
  1037. if ($newCdlByte !== 0)
  1038. {
  1039. $cdlByte = $newCdlByte;
  1040. }
  1041. // if byte is zero and we're at a program label, assume code
  1042. elseif (isset($oldPrgLabels[dechex_pad($counter)]))
  1043. {
  1044. $cdlByte = bindec('00000001');
  1045. }
  1046. // if byte is zero and we're at a label, but not program, assume data (only on 2nd pass)
  1047. elseif (isset($oldLabels[dechex_pad($counter)]) && $pass > 1)
  1048. {
  1049. $cdlByte = bindec('00000010');
  1050. }
  1051. // else assume program code
  1052. // data byte
  1053. if ((($cdlByte & bindec('00000010')) >> 1) && !($cdlByte & bindec('00000001')))
  1054. {
  1055. $counter_pad = dechex_pad($counter);
  1056. if (isCounterLabel($counter, $oldLabels))
  1057. {
  1058. $theOldLabel = $oldLabels[$counter_pad] === true
  1059. ? '__' . $counter_pad
  1060. : $oldLabels[$counter_pad];
  1061. //$theOldLabel = preg_replace('%^([^\+\-]+)[\+\-][0-9]+%', '$1', $theOldLabel);
  1062. }
  1063. if (substr($theOldLabel, -9) == 'JumpTable')
  1064. {
  1065. $byteLen = 2;
  1066. $mnemonic = '.word';
  1067. $addressingType = 11;
  1068. $isInvalid = 0;
  1069. //fseek($file, ftell($file) - 1);
  1070. }
  1071. elseif (substr($theOldLabel, -8) == 'RTSTable')
  1072. {
  1073. $byteLen = 2;
  1074. $mnemonic = '.word';
  1075. $addressingType = 12;
  1076. $isInvalid = 0;
  1077. }/*
  1078. elseif (substr($theOldLabel, -8) == 'TableLow')
  1079. {
  1080. $byteLen = 1;
  1081. $mnemonic = '.byte';
  1082. $addressingType = 13;
  1083. $isInvalid = 0;
  1084. }
  1085. elseif (substr($theOldLabel, -9) == 'TableHigh')
  1086. {
  1087. $byteLen = 1;
  1088. $mnemonic = '.byte';
  1089. $addressingType = 14;
  1090. $isInvalid = 0;
  1091. } */
  1092. else
  1093. {
  1094. $byteLen = 4;
  1095. //echo substr($theLabel, -11);
  1096. $mnemonic = '';
  1097. $addressingType = -1;
  1098. $isInvalid = 1;
  1099. }
  1100. $isDataByte = true;
  1101. $dataStr = 'Data';
  1102. }
  1103. }
  1104. else
  1105. {
  1106. $theOldLabel = ''; // Reset 'theOldLabel' when we are no longer in a known data byte
  1107. }
  1108. $readBytes = $byteLen - 1;
  1109. $bytes = '';
  1110. $byteStr = '';
  1111. $trailer = '';
  1112. $hextext = dechex_pad($opcode);
  1113. $byteArr = array($hextext);
  1114. // read 1 or 2 byte paramters for the opcode
  1115. if ($readBytes > 0)
  1116. {
  1117. if ($pass >= 1)
  1118. {
  1119. if ($cdlFilename !== false)
  1120. {
  1121. $cdlPos = ftell($cdlFile);
  1122. $didMoveCdlPtr = false;
  1123. }
  1124. // check to see if a label exists in this opcode.. if so then usually it's data
  1125. for ($i = 1; $i <= $readBytes; $i++)
  1126. {
  1127. if (isCounterLabel($counter + $i, $oldLabels)
  1128. //if (isset($oldLabels[dechex_pad($counter + $i)])
  1129. || $counter + $i >= 0xFFFA
  1130. || ($counter + $i >= $fileLength)
  1131. || ($usingMapper2 && $headerInfo && $headerInfo->mapper == 2 && $counter + $i > 0xBFFF && $prgBank < $headerInfo->prg - 1)
  1132. ) // if counter in the vectors
  1133. {
  1134. $invalidCounter = 0;
  1135. $readBytes = $i - 1;
  1136. $isInvalid = 1;
  1137. $byteLen = $i;
  1138. $addressingType = -1;
  1139. continue;
  1140. }
  1141. // if this byte marked as data in cdl; check if next bytes are code
  1142. if ($cdlFilename !== false && $isDataByte)
  1143. {
  1144. $newCdlByte = ord(fread($cdlFile, 1));
  1145. $didMoveCdlPtr = true;
  1146. if ($newCdlByte & bindec('00000001'))
  1147. {
  1148. $invalidCounter = 0;
  1149. $readBytes = $i - 1;
  1150. $isInvalid = 1;
  1151. $byteLen = $i;
  1152. $addressingType = -1;
  1153. continue;
  1154. }
  1155. }
  1156. }
  1157. if ($didMoveCdlPtr && $cdlFilename !== false)
  1158. {
  1159. fseek($cdlFile, $cdlPos);
  1160. }
  1161. }
  1162. if ($readBytes > 0) // if readbytes is still > 0 after above
  1163. {
  1164. $bytes = fread($file, $readBytes);
  1165. if ($cdlFilename !== false)
  1166. {
  1167. $cdlBytes = fread($cdlFile, $readBytes);
  1168. }
  1169. for($j = 0; $j < $readBytes; $j++)
  1170. {
  1171. $byteArr[] = dechex_pad(ord($bytes[$j]));
  1172. $hextext .= ' ' . $byteArr[$j+1];
  1173. }
  1174. if ($addressingType == 12)
  1175. {
  1176. $byteStr = (isset($byteArr[1]) ? $byteArr[1]:'') . $byteArr[0];
  1177. $byteStr = dechex_pad(hexdec($byteStr) + 1);
  1178. //echo " $counter $addressingType $byteStr ";
  1179. //print_r($byteArr);
  1180. }
  1181. elseif ($addressingType == 11)
  1182. {
  1183. $byteStr = (isset($byteArr[1]) ? $byteArr[1]:'') . $byteArr[0];
  1184. }
  1185. else
  1186. {
  1187. $byteStr = (isset($byteArr[2]) ? $byteArr[2]:'') . $byteArr[1];
  1188. }
  1189. }
  1190. }
  1191. // ASM6 seems to do some optimization and won't allow absolute addr mode
  1192. // when using $00xx.. it turns it into $xx
  1193. // so instead we'll use .hex
  1194. if ($readBytes == 2
  1195. && substr($byteStr,0,2) == "00"
  1196. && $addressingType > 0
  1197. && $addressingType < 9
  1198. // && $addressingType != 9
  1199. && $addressingType != 3)
  1200. {
  1201. $isInvalid = 1;
  1202. $invalidText = "Bad Addr Mode";
  1203. }
  1204. // add label to list
  1205. $oldByteStr = $byteStr;
  1206. $lbl = '$';
  1207. if ($addressingType > 0
  1208. && isValidLabel($byteStr)
  1209. && !($ignoreWrites && substr($mnemonic,0,2) == 'ST' && ($byteStr < 0x8000))) // do not add labels when writing to PRG
  1210. {
  1211. $lbl = '__';
  1212. if ($pass < $lastPass && $isInvalid !== 1)
  1213. {
  1214. addValidLabel($byteStr, $labels);
  1215. }
  1216. }
  1217. $oldByteStr = $byteStr;
  1218. // $byteStrDec = (dechex_pad($byteStr);
  1219. $newByteStr = $lbl . $byteStr;
  1220. if (isset($oldLabels[$byteStr]) && $lbl !== '')
  1221. {
  1222. $oldLabel = $oldLabels[$byteStr];
  1223. $newByteStr = $oldLabel === true
  1224. ? $newByteStr
  1225. : $oldLabel;
  1226. }
  1227. // lets check for various addressing types to figure out how to format the text
  1228. switch($addressingType)
  1229. {
  1230. case 0: // Implicit/Accumulator/Immediate
  1231. $byteStr = ($readBytes > 0 ? '#$' . $byteStr : '');
  1232. break;
  1233. case 12:
  1234. case 11:
  1235. case 10:
  1236. if (!$isInvalid)
  1237. { addValidLabel($byteStr, $prgLabels);
  1238. }
  1239. case 1: // Absolute
  1240. case 4: // Zero Page
  1241. $byteStr = $newByteStr;
  1242. if ($addressingType == 12)
  1243. {
  1244. $byteStr .= "-1";
  1245. }
  1246. break;
  1247. case 2: // Absolute X
  1248. case 5: // Zero Page X
  1249. $byteStr = $newByteStr . ',x';
  1250. break;
  1251. case 3: // Absolute Y
  1252. case 6: // Zero Page Y
  1253. $byteStr = $newByteStr . ',y';
  1254. break;
  1255. case 7: // Indrect X
  1256. $byteStr = '(' . $newByteStr . ',x)';
  1257. break;
  1258. case 8: // Indirect Y
  1259. $byteStr = '(' . $newByteStr . '),y';
  1260. break;
  1261. case 9: // Indirect Jump
  1262. $byteStr = '(' . $newByteStr . ')';
  1263. break;
  1264. }
  1265. // now lets cover specific mnemonics
  1266. switch($mnemonic)
  1267. {
  1268. // handle branches
  1269. case 'BCC':
  1270. case 'BCS':
  1271. case 'BEQ':
  1272. case 'BMI':
  1273. case 'BNE':
  1274. case 'BPL':
  1275. case 'BVC':
  1276. case 'BVS':
  1277. $addr = addressOffset($counter, $oldByteStr );
  1278. $isInvalidBranch = false;
  1279. if ($pass < $lastPass && !$isInvalid && !$isInvalidBranch)
  1280. {
  1281. addValidLabel($addr, $labels);
  1282. addValidLabel($addr, $prgLabels);
  1283. }
  1284. if (!$isInvalidBranch && isValidLabel($addr))
  1285. { if (isset($labels[$addr]) && $labels[$addr] !== true)
  1286. {
  1287. $byteStr = $labels[$addr];
  1288. }
  1289. else
  1290. {
  1291. $byteStr = '__' . $addr;
  1292. }
  1293. }
  1294. else
  1295. {
  1296. $isInvalid = true;
  1297. $invalidText = "Illegal Branch";
  1298. }
  1299. break;
  1300. // add some space after RTS/JMP
  1301. case 'RTS':
  1302. case 'RTI':
  1303. case 'JMP':
  1304. if (!$isInvalid)
  1305. { $trailer = "\n" . commentLine();
  1306. $didDrawLine = true;
  1307. }
  1308. break;
  1309. }
  1310. // only deal with output on last pass
  1311. if ($pass == $lastPass)
  1312. {
  1313. if ($isInvalid)
  1314. {
  1315. $oldMnemonicStr = $addressingType == -1 ? $dataStr : ($invalidText . " - " . $mnemonic .' '. $byteStr);
  1316. $mnemonic = ".hex";
  1317. $byteStr = $hextext;
  1318. }
  1319. $counter_pad = dechex_pad($counter);
  1320. if (array_key_exists($counter_pad, $oldLabels))
  1321. {
  1322. $leng = 1;
  1323. if (is_array($oldLabels[$counter_pad]))
  1324. { $leng = count($oldLabels[$counter_pad]);
  1325. }
  1326. for ($i = 0; $i < $leng; $i++)
  1327. {
  1328. if (is_array($oldLabels[$counter_pad]))
  1329. {
  1330. $theLabel = $oldLabels[$counter_pad][$i];
  1331. }
  1332. else
  1333. {
  1334. $theLabel = $oldLabels[$counter_pad] === true
  1335. ? '__' . $counter_pad
  1336. : $oldLabels[$counter_pad];
  1337. }
  1338. // if label has a + or - in it but doesn't start with one
  1339. // then don't show it
  1340. // if not 0 or false
  1341. if (strpos($theLabel, '+') || strpos($theLabel, '-'))
  1342. {
  1343. $theText .= LEFT_MARGIN;
  1344. continue;
  1345. }
  1346. switch($theLabel)
  1347. {
  1348. case "irq":
  1349. $theText .= commentHeader("irq/brk vector", !($oldDidDrawLine || $didDrawLine), !($oldDidDrawLine || $didDrawLine));
  1350. break;
  1351. case "nmi":
  1352. case "reset":
  1353. $theText .= commentHeader("$theLabel vector", !($oldDidDrawLine || $didDrawLine), !($oldDidDrawLine || $didDrawLine));
  1354. break;
  1355. }
  1356. if (strlen($theLabel) >= $marginLen - 1)
  1357. {
  1358. $theText .= ($oldDidDrawLine || $didDrawLine || ($counter == $origin) ? '' : "\n")
  1359. . "$theLabel:\n" . LEFT_MARGIN;
  1360. }
  1361. else
  1362. {
  1363. $theText .= str_pad($theLabel . ':', $marginLen);
  1364. }
  1365. }
  1366. }
  1367. else
  1368. {
  1369. $theText .= LEFT_MARGIN;
  1370. }
  1371. //$line = array_key_exists(dechex_pad($counter), $oldLabels) ? '__' . dechex_pad($counter) .':' : ' ';
  1372. $line = '';
  1373. $line .= ($useLowerCase ? strtolower($mnemonic) : $mnemonic) .' '. $byteStr;
  1374. //$labelLen
  1375. $line = str_pad($line, 30 - $marginLen + $labelLen);
  1376. $line .= ' ; $' . dechex_pad($counter) . ": " .$hextext;
  1377. $line = str_pad($line, ($isDataByte ? 54 : 50) - $marginLen + $labelLen);
  1378. $line .= ($isInvalid ? $oldMnemonicStr : '');
  1379. $line .= "\n" . $trailer;
  1380. $theText .= $line;
  1381. }
  1382. $counter += $byteLen;
  1383. $oldDidDrawLine = $didDrawLine;
  1384. } // end line by line loop
  1385. if ($pass < $lastPass && $oldLabels !== false && $labels == $oldLabels)
  1386. {
  1387. $lastPass = $pass + 1;
  1388. }
  1389. /*elseif($pass < $lastPass)
  1390. {
  1391. echo "$pass < $lastPass && $oldLabels !== false && $labels == $oldLabels (" . print_r($labels == $oldLabels,true);
  1392. file_put_contents('out', print_r($labels,true) . print_r($oldLabels, true));
  1393. file_put_contents('out2', print_r(array_diff_assoc($labels, $oldLabels),true));
  1394. }*/
  1395. if ($pass < $lastPass)
  1396. {
  1397. $oldLabels = $labels;
  1398. $oldPrgLabels = $prgLabels;
  1399. rewind($file);
  1400. }
  1401. echo "complete\n";
  1402. $pass++;
  1403. }
  1404. if ($includeChr && $headerInfo !== false)
  1405. {
  1406. fseek($file, $oldPrg * 0x4000 + 0x10);
  1407. $chr = '';
  1408. while (!feof($file))
  1409. {
  1410. $chr .= fread($file,1024);
  1411. }
  1412. $theText .= "\n" . commentLine();
  1413. $theText .= "; CHR-ROM";
  1414. $theText .= "\n" . commentLine();
  1415. $incLine = LEFT_MARGIN . ".incbin " . $shortname.'.chr';
  1416. $theText .= str_pad($incLine, 30 + $labelLen) . " ; Include CHR-ROM\n";
  1417. file_put_contents($shortname.'.chr', $chr);
  1418. echo "\nCHR-ROM exported as $shortname.chr";
  1419. }elseif ($includeChr)
  1420. {
  1421. echo "\nCHR-ROM cannot be exported without iNES header data";
  1422. if ($ignoreHeader)
  1423. {
  1424. "\nTry disabling -ignoreheader if you wish to export CHR-ROM data";
  1425. }
  1426. }
  1427. file_put_contents($shortname.'.asm', $theText);
  1428. $time_end = microtime(true);
  1429. $time = round($time_end - $time_start,3);
  1430. echo "\nDisassembly $shortname.asm generated in $time seconds\n\n";
  1431.