FoxDisassembler.Mod 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. MODULE FoxDisassembler; (** AUTHOR ""; PURPOSE ""; *)
  2. IMPORT Streams, ObjectFile, Scanner := FoxScanner, Basic := FoxBasic, BitSets, D := Debugging, Files, Commands;
  3. CONST Trace = FALSE;
  4. TYPE
  5. Block*= POINTER TO RECORD (ObjectFile.Section)
  6. to-: LONGINT;
  7. next-: Block;
  8. END;
  9. Disassembler* = OBJECT
  10. VAR
  11. first, block: Block; w: Streams.Writer; stringWriter: Streams.StringWriter; code,data: BitSets.BitSet; codeUnit, dataUnit: LONGINT;
  12. codeDisplacement-, dataDisplacement: LONGINT;
  13. PROCEDURE & Init*(w: Streams.Writer);
  14. BEGIN SELF.w := w; NEW(stringWriter, 256);
  15. codeDisplacement := 0; dataDisplacement := 0;
  16. END Init;
  17. PROCEDURE GetLogFile*(CONST binaryFileName: ARRAY OF CHAR): Files.File;
  18. VAR fileName,extension: Files.FileName;
  19. BEGIN
  20. Files.SplitExtension(binaryFileName, fileName, extension);
  21. Files.JoinExtension(fileName, "log", fileName);
  22. RETURN Files.Old(fileName)
  23. END GetLogFile;
  24. PROCEDURE BlockHeader(block: Block);
  25. VAR name: ObjectFile.SectionName; offset: LONGINT;
  26. BEGIN
  27. Basic.SegmentedNameToString(block.identifier.name, name);
  28. w.String("-------- "); w.Char(Scanner.TAB);
  29. IF ObjectFile.IsCode(block.type) THEN w.String("code");
  30. ELSE w.String("data");
  31. END;
  32. w.Char(Scanner.TAB);
  33. w.String(name);
  34. w.String(" @");
  35. w.Hex(block.alignment,-8); (*w.String("-"); w.Hex(block.to+offset,-8);*)
  36. w.String(" ---");
  37. w.Ln;
  38. END BlockHeader;
  39. PROCEDURE WriteReference*(adr: LONGINT; isCode: BOOLEAN; w: Streams.Writer);
  40. VAR b: Block; name: ObjectFile.SectionName; offset: LONGINT;
  41. BEGIN
  42. w.String(" --> "); w.Hex(adr+codeDisplacement,-8);
  43. b := first; IF b = NIL THEN RETURN END;
  44. IF isCode THEN offset := codeDisplacement ELSE offset := dataDisplacement END;
  45. WHILE (b # NIL) & ((adr < b.alignment-offset) OR (adr > b.to - offset) OR (ObjectFile.IsCode(b.type)#isCode)) DO
  46. b := b.next;
  47. END;
  48. IF b = NIL THEN
  49. (* try to find any matching section *)
  50. b := first;
  51. WHILE (b # NIL) & ((adr < b.alignment-offset) OR (adr > b.to - offset)) DO
  52. b := b.next;
  53. END;
  54. END;
  55. IF (b # NIL) & (b # block) THEN
  56. Basic.SegmentedNameToString(b.identifier.name, name);
  57. w.String(" [");
  58. w.String(name);
  59. IF adr # b.alignment THEN
  60. w.String("+"); w.Int(adr-b.alignment+offset,1)
  61. END;
  62. w.String("]");
  63. END;
  64. END WriteReference;
  65. PROCEDURE DisassembleInstruction*(bitset: BitSets.BitSet; VAR adrInUnits: LONGINT; maxInstructionSize: LONGINT; w: Streams.Writer);
  66. BEGIN
  67. END DisassembleInstruction;
  68. PROCEDURE DisassembleBlock(from, to: LONGINT);
  69. VAR adr, prevadr, max, digit: LONGINT; value: WORD; string: ARRAY 256 OF CHAR;
  70. BEGIN
  71. IF code = NIL THEN RETURN END;
  72. adr := from;
  73. max := MIN(to+1, code.GetSize() DIV codeUnit);
  74. WHILE adr < max DO
  75. (* adr *)
  76. w.Hex(adr+codeDisplacement,-8); w.String(": ");
  77. prevadr := adr;
  78. DisassembleInstruction(code, adr, max-adr, stringWriter);
  79. IF prevadr = adr THEN w.String("decoder error: address must increase"); w.Ln; RETURN END;
  80. stringWriter.Update;
  81. stringWriter.Get(string);
  82. (* value *)
  83. WHILE prevadr < adr DO
  84. value := code.GetBits(prevadr*codeUnit, codeUnit);
  85. w.Hex(value,-((codeUnit-1) DIV 4 +1)); w.String(" ");
  86. INC(prevadr);
  87. END;
  88. (* instruction string *)
  89. w.Char(Scanner.TAB); w.String(string); w.Ln;
  90. END;
  91. END DisassembleBlock;
  92. PROCEDURE DataBlock(from, to: LONGINT);
  93. VAR adr,width,max: LONGINT; value: WORD;
  94. BEGIN
  95. IF data = NIL THEN RETURN END;
  96. adr := from;
  97. max := MIN(to+1, data.GetSize() DIV dataUnit);
  98. WHILE adr < max DO
  99. w.Hex(adr+dataDisplacement,-8); w.String(": ");
  100. width := 8;
  101. WHILE (adr < max) & (width > 0) DO
  102. value := data.GetBits(adr*dataUnit, dataUnit);
  103. w.Hex(value,-((dataUnit-1) DIV 4 +1)); w.String(" ");
  104. INC(adr); DEC(width);
  105. END;
  106. w.Ln;
  107. END;
  108. END DataBlock;
  109. PROCEDURE ParseLogFile*(file: Files.File): Block;
  110. VAR reader: Files.Reader; newline: BOOLEAN; sectionName: ObjectFile.SectionName; scanner: Scanner.AssemblerScanner;
  111. token: Scanner.Token; b: BOOLEAN; block: Block; first: Block; last: Block;
  112. PROCEDURE GetNextToken;
  113. VAR b: BOOLEAN;
  114. BEGIN b := scanner.GetNextToken(token)
  115. END GetNextToken;
  116. PROCEDURE ExpectToken(symbol: Scanner.Symbol): BOOLEAN;
  117. BEGIN IF token.symbol = symbol THEN GetNextToken; RETURN TRUE ELSE RETURN FALSE END;
  118. END ExpectToken;
  119. PROCEDURE ExpectIdentifier(VAR name: ARRAY OF CHAR): BOOLEAN;
  120. BEGIN
  121. IF (token.symbol = Scanner.Identifier) THEN COPY(token.identifierString,name); GetNextToken; RETURN TRUE
  122. ELSE RETURN FALSE
  123. END;
  124. END ExpectIdentifier;
  125. PROCEDURE ExpectThisIdentifier(CONST name: ARRAY OF CHAR): BOOLEAN;
  126. BEGIN
  127. IF (token.symbol = Scanner.Identifier) & (token.identifierString = name) THEN GetNextToken; RETURN TRUE
  128. ELSE RETURN FALSE
  129. END;
  130. END ExpectThisIdentifier;
  131. PROCEDURE ExpectNumber(VAR int: LONGINT):BOOLEAN;
  132. BEGIN
  133. IF (token.symbol = Scanner.Number) THEN
  134. int := SHORT(token.hugeint); GetNextToken; RETURN TRUE
  135. ELSE RETURN FALSE
  136. END;
  137. END ExpectNumber;
  138. PROCEDURE ParseLine(): Block;
  139. VAR from,to: LONGINT; block: Block; displacement: LONGINT;
  140. BEGIN
  141. block := NIL;
  142. IF ExpectNumber(from) & ExpectToken(Scanner.Colon) THEN
  143. IF ExpectThisIdentifier("code") & ExpectIdentifier(sectionName) & ExpectThisIdentifier("to") & ExpectNumber(to) THEN
  144. NEW(block); block.type := ObjectFile.Code; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
  145. ELSIF ExpectThisIdentifier("data") & ExpectIdentifier(sectionName) & ExpectThisIdentifier("to") & ExpectNumber(to) THEN
  146. NEW(block); block.type := ObjectFile.Data; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
  147. END;
  148. ELSIF ExpectThisIdentifier("code") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
  149. codeDisplacement := displacement; dataDisplacement := displacement
  150. ELSIF ExpectThisIdentifier("data") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
  151. dataDisplacement := displacement;
  152. ELSE scanner.SkipToEndOfLine; GetNextToken; scanner.ResetError;
  153. END;
  154. IF (block # NIL) & Trace THEN
  155. D.String("found section ");
  156. D.String(sectionName);
  157. IF ObjectFile.IsCode(block.type) THEN D.String(" (code) ") ELSE D.String(" (data) ") END;
  158. D.Int(block.alignment,1); D.String(" "); D.Int(block.to,1);
  159. D.Ln;
  160. END;
  161. RETURN block
  162. END ParseLine;
  163. BEGIN
  164. first := NIL; last := NIL;
  165. IF file = NIL THEN RETURN NIL END;
  166. NEW(reader, file, 0);
  167. scanner := Scanner.NewAssemblerScanner("",reader,0,NIL);
  168. b := scanner.GetNextToken(token);
  169. REPEAT
  170. block := ParseLine();
  171. IF block # NIL THEN
  172. IF first = NIL THEN first := block; last := block ELSE last.next := block; last := block END;
  173. END;
  174. GetNextToken
  175. UNTIL token.symbol = Scanner.EndOfText;
  176. RETURN first;
  177. END ParseLogFile;
  178. PROCEDURE Disassemble*(code, data: BitSets.BitSet; codeUnit, dataUnit: LONGINT; logFile: Files.File; address: ADDRESS);
  179. BEGIN
  180. SELF.code := code;SELF.data := data; SELF.codeUnit := codeUnit; SELF.dataUnit := dataUnit;
  181. first := ParseLogFile(logFile); block := first;
  182. IF block = NIL THEN
  183. w.String("------ code ------"); w.Ln;
  184. DisassembleBlock(0, MAX(LONGINT)-1);
  185. IF code # data THEN
  186. w.String("------ data ------"); w.Ln;
  187. DataBlock(0, MAX(LONGINT)-1);
  188. END;
  189. ELSE
  190. WHILE block # NIL DO
  191. IF (address = 0) OR (block.alignment <= address) & (block.to > address) THEN
  192. BlockHeader(block);
  193. IF ObjectFile.IsCode(block.type) & (code # NIL) THEN
  194. DisassembleBlock(block.alignment-codeDisplacement, block.to-codeDisplacement);
  195. ELSE
  196. DataBlock(block.alignment-dataDisplacement, block.to-dataDisplacement);
  197. END;
  198. END;
  199. block := block.next
  200. END;
  201. END;
  202. END Disassemble;
  203. PROCEDURE SetDisplacements*(code, data: LONGINT);
  204. BEGIN
  205. codeDisplacement := code;
  206. dataDisplacement := data;
  207. END SetDisplacements;
  208. END Disassembler;
  209. PROCEDURE FindPC*(context: Commands.Context);
  210. VAR file: Files.File; logFile: Files.FileName; adr,maxadr: LONGINT; disassembler: Disassembler; block, found: Block; name: ObjectFile.SectionName;
  211. BEGIN
  212. IF context.arg.GetString(logFile) & context.arg.GetInteger(adr, TRUE) THEN
  213. file := Files.Old(logFile);
  214. IF file = NIL THEN Files.JoinExtension(logFile,".log",logFile); file := Files.Old(logFile) END;
  215. IF file = NIL THEN
  216. context.error.String("file not found "); context.error.String(logFile); context.error.Ln
  217. ELSE
  218. NEW(disassembler, context.out);
  219. maxadr := 0;
  220. block := disassembler.ParseLogFile(file);
  221. WHILE (block # NIL ) DO
  222. IF (block.alignment < adr) & (block.alignment > maxadr) & ObjectFile.IsCode(block.type) THEN
  223. found := block;
  224. maxadr := block.alignment
  225. END;
  226. block := block.next
  227. END;
  228. Basic.SegmentedNameToString(found.identifier.name, name);
  229. context.out.String(name); context.out.String(":"); context.out.Int(adr-found.alignment,1); context.out.Ln;
  230. END;
  231. END;
  232. END FindPC;
  233. (*
  234. PROCEDURE Test*(context: Commands.Context);
  235. VAR filename: Files.FileName; name: ObjectFile.SectionName; block: Block;
  236. BEGIN
  237. IF context.arg.GetString(filename) THEN
  238. file := Files.Old(filename);
  239. IF file = NIL THEN (* error *) HALT(100) END;
  240. block := ParseLogFile(filename);
  241. WHILE block # NIL DO
  242. Basic.SegmentedNameToString(block.identifier.name, name);
  243. context.out.String(name); context.out.String(" at "); context.out.Int(block.alignment,1); context.out.Ln;
  244. block := block.next;
  245. END;
  246. END;
  247. END Test;
  248. *)
  249. END FoxDisassembler.
  250. System.FreeDownTo FoxDisassembler ~
  251. FoxDisassembler.Test ins.log ~