FoxProgTools.Mod 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  1. MODULE FoxProgTools; (** AUTHOR "fof"; PURPOSE "Oberon Compiler: Programming Tools"; **)
  2. (* (c) fof ETH Zürich, 2009 *)
  3. IMPORT Streams,Options,Commands,Files,Strings, Basic := FoxBasic;
  4. TYPE
  5. NameEntry = POINTER TO RECORD
  6. name: ARRAY 265 OF CHAR;
  7. END;
  8. NameList = OBJECT(Basic.List)
  9. PROCEDURE & Init;
  10. BEGIN InitList(256)
  11. END Init;
  12. PROCEDURE AddName(CONST name: ARRAY OF CHAR);
  13. VAR entry: NameEntry;
  14. BEGIN
  15. NEW(entry);
  16. COPY(name, entry.name);
  17. Add(entry)
  18. END AddName;
  19. PROCEDURE GetName(index: LONGINT; VAR name: ARRAY OF CHAR);
  20. VAR
  21. any: ANY;
  22. BEGIN
  23. any := Get(index);
  24. ASSERT(any IS NameEntry);
  25. COPY(any(NameEntry).name, name)
  26. END GetName;
  27. PROCEDURE ContainsName(CONST otherName: ARRAY OF CHAR): BOOLEAN;
  28. VAR
  29. i: LONGINT;
  30. name: ARRAY 256 OF CHAR;
  31. BEGIN
  32. FOR i := 0 TO Length() - 1 DO
  33. GetName(i, name);
  34. IF name = otherName THEN RETURN TRUE END
  35. END;
  36. RETURN FALSE
  37. END ContainsName;
  38. PROCEDURE DumpNames(w: Streams.Writer);
  39. VAR
  40. i: LONGINT;
  41. name: ARRAY 256 OF CHAR;
  42. BEGIN
  43. w.Int(Length(), 0); w.String(" names:"); w.Ln;
  44. FOR i := 0 TO Length() - 1 DO
  45. GetName(i, name);
  46. w.String(name); w.Ln
  47. END;
  48. w.Ln
  49. END DumpNames;
  50. PROCEDURE SortNames;
  51. BEGIN Sort(NameComparator)
  52. END SortNames;
  53. END NameList;
  54. (** string comparator function that is used to sort strings alphabetically **)
  55. PROCEDURE NameComparator(left, right: ANY): BOOLEAN;
  56. VAR
  57. result: SHORTINT;
  58. i: LONGINT;
  59. leftChar, rightChar: CHAR;
  60. BEGIN
  61. result := 0; i := 0;
  62. REPEAT
  63. leftChar := left(NameEntry).name[i];
  64. rightChar := right(NameEntry).name[i];
  65. IF leftChar < rightChar THEN result := -1
  66. ELSIF leftChar > rightChar THEN result := +1
  67. END;
  68. INC(i)
  69. UNTIL (result # 0) OR (leftChar = 0X) OR (rightChar = 0X);
  70. RETURN result < 0
  71. END NameComparator;
  72. PROCEDURE ParseARMInstructionSet*(context: Commands.Context);
  73. CONST
  74. PrependIS = FALSE;
  75. TYPE
  76. Operand = RECORD
  77. type: ARRAY 256 OF CHAR;
  78. mask: SET;
  79. END;
  80. VAR
  81. isThumb, comma: BOOLEAN;
  82. i, pos, numBits, numOps: LONGINT;
  83. char: CHAR;
  84. ones, mask: SET;
  85. filename, mnemonic, token, flagString, encodingName, pattern, type: ARRAY 256 OF CHAR;
  86. operands: ARRAY 10 OF Operand;
  87. operand: Operand;
  88. file: Files.File;
  89. reader: Files.Reader;
  90. encodingNames: NameList;
  91. PROCEDURE AppendMaskName(VAR string: ARRAY OF CHAR; mask: SET);
  92. VAR
  93. j, start: LONGINT;
  94. inside, first: BOOLEAN;
  95. numString: ARRAY 32 OF CHAR;
  96. BEGIN
  97. IF mask = {} THEN
  98. Strings.Append(string, "Implicit");
  99. ELSE
  100. first := TRUE;
  101. inside := FALSE;
  102. FOR j := 0 TO 31 + 1 DO
  103. IF (j < 32) & (j IN mask) THEN
  104. IF ~inside THEN
  105. IF first THEN first := FALSE ELSE Strings.Append(string, "and") END;
  106. Strings.IntToStr(j, numString);
  107. Strings.Append(string, numString);
  108. start := j;
  109. inside := TRUE
  110. END
  111. ELSE
  112. IF inside THEN
  113. IF j - 1 > start THEN
  114. Strings.Append(string, "to");
  115. Strings.IntToStr(j - 1, numString);
  116. Strings.Append(string, numString);
  117. END;
  118. inside := FALSE
  119. END
  120. END
  121. END;
  122. END
  123. END AppendMaskName;
  124. PROCEDURE Error(CONST message: ARRAY OF CHAR);
  125. BEGIN
  126. context.out.Ln;
  127. context.out.String("Error: ");
  128. context.out.String(message);
  129. context.out.Ln;
  130. context.out.Update
  131. END Error;
  132. (*
  133. PROCEDURE Log(CONST message: ARRAY OF CHAR);
  134. BEGIN
  135. context.out.Ln;
  136. context.out.String("Log: ");
  137. context.out.String(message);
  138. context.out.Ln;
  139. context.out.Update
  140. END Log;
  141. *)
  142. PROCEDURE IsUpperCase(char: CHAR): BOOLEAN;
  143. BEGIN RETURN (ORD(char) >= ORD('A')) & (ORD(char) <= ORD('Z'))
  144. END IsUpperCase;
  145. BEGIN
  146. context.arg.SkipWhitespace; context.arg.String(filename);
  147. IF filename # "" THEN
  148. file := Files.Old(filename);
  149. IF file = NIL THEN Error("file not found"); RETURN END;
  150. context.out.String("parsing file "); context.out.String(filename); context.out.Ln; context.out.Ln; context.out.Update;
  151. NEW(reader, file, 0);
  152. mnemonic := "";
  153. NEW(encodingNames);
  154. reader.Token(token);
  155. WHILE reader.Available() > 0 DO
  156. IF token = "" THEN
  157. reader.SkipWhitespace
  158. ELSIF token[0] = '%' THEN (* comment *)
  159. reader.SkipLn
  160. ELSIF (token = "T") OR (token = "A") THEN
  161. IF mnemonic = "" THEN Error("no mnemonic specified"); RETURN END;
  162. isThumb := token = 'T';
  163. (* parse flags *)
  164. reader.SkipWhitespace; reader.Token(token);
  165. flagString := "{";
  166. comma := FALSE;
  167. i := 0;
  168. WHILE token[i] # 0X DO
  169. char := token[i];
  170. IF char # '-' THEN
  171. IF comma THEN Strings.Append(flagString, ", "); END;
  172. Strings.Append(flagString, "flag");
  173. IF IsUpperCase(char) THEN Strings.Append(flagString, "Always") END;
  174. Strings.AppendChar(flagString, Strings.UP(char));
  175. comma := TRUE
  176. END;
  177. INC(i)
  178. END;
  179. Strings.Append(flagString, "}");
  180. (* reset operands *)
  181. FOR i := 0 TO LEN(operands) - 1 DO
  182. operand.type := "?";
  183. operand.mask := {};
  184. operands[i] := operand
  185. END;
  186. (* parse bit pattern *)
  187. reader.SkipWhitespace;
  188. pattern := "";
  189. i := 0;
  190. reader.Char(char);
  191. WHILE (char # 9X) & ~reader.EOLN() DO
  192. IF char # ' ' THEN
  193. pattern[i] := char;
  194. INC(i)
  195. END;
  196. reader.Char(char)
  197. END;
  198. pattern[i] := 0X;
  199. numBits := i;
  200. IF (numBits MOD 16) # 0 THEN Error("number of bits in pattern not a multiple of 16");
  201. context.out.String(pattern); context.out.Ln; context.out.Update;
  202. RETURN
  203. END;
  204. ones := {};
  205. mask := {};
  206. i := 0;
  207. WHILE pattern[i] # 0X DO
  208. pos := numBits - 1 - i;
  209. char := pattern[i];
  210. CASE char OF
  211. | '0': INCL(mask, pos)
  212. | '1': INCL(mask, pos); INCL(ones, pos)
  213. | '-': (* ignore *)
  214. | '+': INCL(ones, pos)
  215. | 'a' .. 'f': INCL(operands[ORD(char) - ORD('a')].mask, pos)
  216. ELSE (* ignore *)
  217. END;
  218. INC(i)
  219. END;
  220. (* parse operand types *)
  221. i := 0;
  222. WHILE ~reader.EOLN() DO
  223. reader.SkipWhitespace;
  224. reader.Token(token);
  225. IF token # "-" THEN operands[i].type := token; INC(i) END
  226. END;
  227. numOps := i;
  228. (* print Oberon line of code that registers this encoding *)
  229. context.out.String("EnterInstruction(instr"); context.out.String(mnemonic);
  230. context.out.String(", "); IF isThumb THEN context.out.String("Thumb") ELSE context.out.String("ARM") END;
  231. context.out.String(", "); context.out.Int(numBits DIV 16, 0);
  232. context.out.String(", "); context.out.Set(ones);
  233. context.out.String(", "); context.out.Set(mask);
  234. context.out.String(", "); context.out.String(flagString);
  235. context.out.Update;
  236. FOR i := 0 TO numOps - 1 DO
  237. operand := operands[i];
  238. context.out.String(", ");
  239. (* compose encoding name *)
  240. encodingName := "enc";
  241. IF PrependIS THEN IF isThumb THEN Strings.AppendChar(encodingName, 'T') ELSE Strings.AppendChar(encodingName, 'A') END END;
  242. (* manual names: *)
  243. type := operand.type;
  244. IF type = "r" THEN Strings.Append(encodingName, "Reg");
  245. ELSIF type = "isr" THEN Strings.Append(encodingName, "ImmShReg")
  246. ELSIF type = "rsr" THEN Strings.Append(encodingName, "RegShReg")
  247. ELSIF type = "i" THEN Strings.Append(encodingName, "Imm")
  248. ELSIF type = "wi" THEN Strings.Append(encodingName, "WideImm")
  249. ELSIF type = "l" THEN Strings.Append(encodingName, "Label")
  250. ELSIF type = "l-" THEN Strings.Append(encodingName, "ForwardLabel")
  251. ELSIF type = "l+" THEN Strings.Append(encodingName, "BackwardLabel")
  252. ELSIF type = "b" THEN Strings.Append(encodingName, "Bitfield")
  253. ELSIF type = "sp" THEN Strings.Append(encodingName, "SP")
  254. ELSIF type = "1" THEN Strings.Append(encodingName, "Op1")
  255. ELSIF type = "*" THEN (* ignore *)
  256. ELSE Error("unknown operand type found"); RETURN
  257. END;
  258. (* alternative, which works for arbitrary type strings
  259. COPY(Strings.UpperCaseInNew(operand.type)^, type);
  260. *)
  261. AppendMaskName(encodingName, operand.mask);
  262. context.out.String(encodingName);
  263. IF ~encodingNames.ContainsName(encodingName) THEN encodingNames.AddName(encodingName) END;
  264. END;
  265. context.out.String(");"); context.out.Ln;
  266. context.out.Update
  267. ELSE
  268. (* start of a new mnemonic *)
  269. mnemonic := token
  270. END;
  271. reader.Token(token)
  272. END;
  273. context.out.Ln; context.out.Ln;
  274. encodingNames.SortNames;
  275. encodingNames.DumpNames(context.out);
  276. context.out.Update
  277. ELSE
  278. Error("filename missing")
  279. END
  280. END ParseARMInstructionSet;
  281. (* e.g. InstructionBits "100xx0x0x" ~
  282. 0: should be 0
  283. 1: should be 1
  284. x: free
  285. *)
  286. PROCEDURE InstructionBits*(context: Commands.Context);
  287. VAR
  288. i, length, bitPos: LONGINT;
  289. shouldBeOnes, mask: SET;
  290. pattern: ARRAY 33 OF CHAR;
  291. w: Streams.Writer;
  292. options: Options.Options;
  293. BEGIN
  294. NEW(options);
  295. IF options.Parse(context.arg, context.error) THEN
  296. IF context.arg.GetString(pattern) THEN
  297. w := context.out;
  298. length := 0;
  299. WHILE pattern[length] # 0X DO INC(length) END;
  300. IF (length # 16) & (length # 32) THEN
  301. context.error.String("Error: invalid pattern length: ");
  302. context.error.Int(length, 0);
  303. context.error.Ln
  304. ELSE
  305. FOR i := 0 TO length - 1 DO
  306. bitPos := length - 1 - i;
  307. CASE pattern[i] OF
  308. | '0': INCL(mask, bitPos)
  309. | '1': INCL(mask, bitPos); INCL(shouldBeOnes, bitPos)
  310. ELSE (* ignore *)
  311. END
  312. END;
  313. w.Set(shouldBeOnes); w.String(", "); w.Set(mask); w.Ln
  314. END
  315. END
  316. END
  317. END InstructionBits;
  318. PROCEDURE Enum*(context: Commands.Context);
  319. VAR w: Streams.Writer; nr: LONGINT; options: Options.Options; export,incremental,hex: BOOLEAN; start: LONGINT;
  320. name,oldname,prefix: ARRAY 256 OF CHAR; lf: LONGINT;
  321. (** get next symbol **)
  322. PROCEDURE GetNextSymbol(VAR s: ARRAY OF CHAR ): BOOLEAN;
  323. CONST EOT=0X;
  324. VAR ch: CHAR; i: LONGINT;
  325. PROCEDURE SkipBlanks;
  326. BEGIN
  327. WHILE ch <= " " DO (*ignore control characters*)
  328. IF ch = EOT THEN RETURN
  329. ELSE ch := context.arg.Get();
  330. END
  331. END;
  332. END SkipBlanks;
  333. PROCEDURE Comment;
  334. VAR i: LONGINT;
  335. BEGIN
  336. i := 0;
  337. LOOP
  338. IF (ch = EOT) OR (i>256) THEN EXIT
  339. ELSIF ch = "(" THEN
  340. w.Char(ch); ch := context.arg.Get();
  341. IF ch = "*" THEN
  342. w.Char(ch); ch := context.arg.Get();
  343. Comment()
  344. END;
  345. ELSIF ch = "*" THEN
  346. w.Char(ch);
  347. ch := context.arg.Get();
  348. IF ch = ")" THEN
  349. w.Char(ch); ch := context.arg.Get();
  350. EXIT
  351. END;
  352. ELSE w.Char(ch); ch := context.arg.Get(); INC(i);
  353. END;
  354. END;
  355. END Comment;
  356. BEGIN
  357. ch := context.arg.Get();
  358. SkipBlanks; i := 0;
  359. LOOP
  360. IF (ch <= " ") OR (ch = EOT) OR (i>128) THEN EXIT
  361. ELSIF ch = "(" THEN
  362. ch := context.arg.Get();
  363. IF ch = "*" THEN
  364. ch := context.arg.Get();
  365. IF (nr-start) MOD lf # 0 (* avoid two linefeeds *) THEN w.Ln END;
  366. w.String("(*");
  367. Comment();
  368. w.Ln; start := nr;
  369. SkipBlanks;
  370. ELSE s[i] := "("; INC(i);
  371. END;
  372. ELSE s[i] := ch; INC(i); ch := context.arg.Get();
  373. END;
  374. END;
  375. s[i] := 0X;
  376. RETURN (i>0);
  377. END GetNextSymbol;
  378. BEGIN
  379. NEW(options);
  380. options.Add("e", "export", Options.Flag);
  381. options.Add("i", "incremental", Options.Flag);
  382. options.Add("s", "start", Options.Integer);
  383. options.Add("h","hex",Options.Flag);
  384. options.Add("l","linefeed",Options.Integer);
  385. options.Add("p","prefix",Options.String);
  386. IF options.Parse(context.arg,context.error) THEN
  387. w := context.out;
  388. IF ~options.GetInteger("s",start) THEN start := 0 END;
  389. export := options.GetFlag("e");
  390. incremental := options.GetFlag("i");
  391. hex := options.GetFlag("h");
  392. IF ~options.GetString("p", prefix) THEN prefix := "" END;
  393. IF ~options.GetInteger("l",lf) THEN lf := MAX(LONGINT) END;
  394. nr := start;
  395. WHILE GetNextSymbol(name) DO
  396. w.String(prefix);
  397. w.String(name);
  398. IF export THEN w.String("*"); END;
  399. w.String("= ");
  400. IF incremental THEN
  401. IF oldname = "" THEN
  402. IF hex THEN w.Hex(nr,1); w.String("H"); ELSE w.Int(nr,1) END;
  403. ELSE w.String(oldname); w.String("+1");
  404. END;
  405. ELSE
  406. IF hex THEN w.Hex(nr,1); w.String("H"); ELSE w.Int(nr,1) END;
  407. END;
  408. w.String("; ");
  409. IF (nr-start+1) MOD lf = 0 THEN w.Ln END;
  410. INC(nr);
  411. oldname := name;
  412. (* context.arg.GetString(name); *)
  413. END;
  414. w.Update;
  415. END;
  416. END Enum;
  417. PROCEDURE ParseAMDInstructionSet*(context: Commands.Context);
  418. CONST TAB = 9X; CR = 0DX; LF = 0AX;
  419. TYPE
  420. Instruction=POINTER TO RECORD
  421. op1,op2,op3,opcode,target,bitwidth: ARRAY 256 OF CHAR;
  422. next: Instruction;
  423. END;
  424. Mnemonic= POINTER TO RECORD
  425. name: ARRAY 32 OF CHAR;
  426. firstInstruction,lastInstruction: Instruction;
  427. next: Mnemonic;
  428. END;
  429. Replacement = POINTER TO RECORD
  430. from,to: ARRAY 256 OF CHAR;
  431. next: Replacement;
  432. END;
  433. TYPE Replacer= OBJECT
  434. VAR first,last: Replacement;
  435. PROCEDURE &Init;
  436. BEGIN first := NIL; last := NIL;
  437. END Init;
  438. PROCEDURE Add(CONST src,dest: ARRAY OF CHAR);
  439. VAR r: Replacement;
  440. BEGIN
  441. NEW(r); COPY(src,r.from); COPY(dest,r.to);
  442. IF first = NIL THEN first := r; last := r ELSE last.next := r; last := r END;
  443. END Add;
  444. PROCEDURE Do(VAR src: ARRAY OF CHAR);
  445. VAR pos: LONGINT; r: Replacement;
  446. BEGIN
  447. r := first;
  448. WHILE r # NIL DO
  449. pos := Strings.Pos(r.from,src);
  450. IF pos # -1 THEN
  451. Strings.Delete(src,pos,Strings.Length(r.from));
  452. Strings.Insert(r.to,src,pos);
  453. END;
  454. r := r.next;
  455. END;
  456. END Do;
  457. END Replacer;
  458. VAR fileName,instr: ARRAY 256 OF CHAR; w: Streams.Writer; reader: Files.Reader; file: Files.File; ch: CHAR;
  459. line: ARRAY 1024 OF CHAR;
  460. firstMnemonic,prevmnemonic,mnemonic : Mnemonic;
  461. instruction: Instruction;
  462. operandReplacer: Replacer;
  463. cpuoptionReplacer: Replacer;
  464. bitwidthReplacer: Replacer;
  465. numberMnemonics: LONGINT;
  466. numberInstructions : LONGINT;
  467. maxMnemonicNameLength: LONGINT;
  468. maxOpcodeLength: LONGINT;
  469. PROCEDURE Priority(i: Instruction): LONGINT;
  470. VAR prio: LONGINT;
  471. PROCEDURE OP(CONST o: ARRAY OF CHAR): LONGINT;
  472. BEGIN
  473. IF o = "" THEN
  474. RETURN 10
  475. ELSIF (o = "reg8") OR (o = "reg16") OR (o="reg32") OR (o="reg64") THEN
  476. RETURN 5
  477. ELSE
  478. RETURN 0
  479. END;
  480. END OP;
  481. BEGIN
  482. prio := 0;
  483. prio := OP(i.op1) + OP(i.op2) + OP(i.op3);
  484. RETURN prio
  485. END Priority;
  486. PROCEDURE InsertSorted(VAR first: Instruction; this: Instruction);
  487. VAR temp: Instruction;
  488. BEGIN
  489. IF (first = NIL) OR (Priority(this)>Priority(first)) THEN
  490. this.next := first;
  491. first := this;
  492. ELSE
  493. temp := first;
  494. WHILE (temp.next # NIL) & (Priority(temp.next) >= Priority(this)) DO
  495. temp := temp.next;
  496. END;
  497. this.next := temp.next;
  498. temp.next := this;
  499. END;
  500. END InsertSorted;
  501. PROCEDURE SortInstructions(VAR first: Instruction);
  502. VAR temp,next,newfirst: Instruction;
  503. BEGIN
  504. newfirst := NIL;
  505. temp := first;
  506. WHILE temp # NIL DO
  507. next := temp.next;
  508. InsertSorted(newfirst,temp);
  509. temp := next;
  510. END;
  511. first := newfirst;
  512. END SortInstructions;
  513. PROCEDURE GetCh;
  514. BEGIN
  515. ch := reader.Get();
  516. END GetCh;
  517. PROCEDURE GetLine(VAR line: ARRAY OF CHAR);
  518. VAR i : LONGINT;
  519. BEGIN
  520. i := 0;
  521. WHILE(ch # CR) & (ch # LF) & (ch # 0X) DO
  522. line[i] := ch; INC(i);
  523. GetCh();
  524. END;
  525. line[i] := 0X;
  526. WHILE(ch = CR) OR (ch=LF) DO
  527. GetCh();
  528. END;
  529. END GetLine;
  530. PROCEDURE Operand(CONST op: ARRAY OF CHAR);
  531. VAR i: LONGINT;
  532. BEGIN
  533. IF op[0] = 0X THEN w.String("none")
  534. ELSIF op = "1" THEN w.String("one")
  535. ELSIF op ="3" THEN w.String("three");
  536. ELSE
  537. i := 0;
  538. WHILE(op[i] # 0X) DO
  539. IF (op[i] # "/") & (op[i] # ":") & (op[i]# "&") THEN
  540. w.Char(op[i]);
  541. END;
  542. INC(i);
  543. END;
  544. END;
  545. END Operand;
  546. PROCEDURE AppendCh(VAR s: ARRAY OF CHAR; c: CHAR);
  547. VAR i: LONGINT;
  548. BEGIN
  549. i := 0;
  550. WHILE(s[i] # 0X) DO
  551. INC(i);
  552. END;
  553. s[i] := c; s[i+1] := 0X;
  554. END AppendCh;
  555. PROCEDURE Append(VAR s: ARRAY OF CHAR; CONST a: ARRAY OF CHAR);
  556. BEGIN
  557. Strings.Append(s,a)
  558. END Append;
  559. PROCEDURE Follows(CONST s: ARRAY OF CHAR; VAR i: LONGINT; CONST this: ARRAY OF CHAR): BOOLEAN;
  560. VAR j,k: LONGINT;
  561. BEGIN
  562. j := i; k := 0;
  563. WHILE(s[j] # 0X) & (this[k] # 0X) & (s[j] = this[k]) DO
  564. INC(j); INC(k);
  565. END;
  566. IF this[k] = 0X THEN
  567. i := j;
  568. RETURN TRUE
  569. ELSE
  570. RETURN FALSE
  571. END;
  572. END Follows;
  573. PROCEDURE OpCode(CONST code: ARRAY OF CHAR);
  574. VAR ch: CHAR; i: LONGINT;
  575. op: ARRAY 3 OF CHAR;
  576. nOp: LONGINT;
  577. ModRM:ARRAY 256 OF CHAR; error: BOOLEAN;
  578. opCodeExtension: LONGINT;
  579. length: LONGINT;
  580. PROCEDURE NextOption(VAR s: ARRAY OF CHAR);
  581. BEGIN
  582. IF s[0] # 0X THEN Append(s,",") END;
  583. END NextOption;
  584. PROCEDURE Hex(ch: CHAR): LONGINT;
  585. BEGIN
  586. IF ("0" <= ch) & (ch <= "9") THEN
  587. RETURN ORD(ch)-ORD("0");
  588. ELSIF ("A" <= ch) & (ch <= "F") THEN
  589. RETURN ORD(ch)-ORD("A")+10;
  590. ELSE error := TRUE; RETURN 0
  591. END;
  592. END Hex;
  593. BEGIN
  594. error := FALSE;
  595. ModRM := "";
  596. op[0] := 0X;
  597. op[1] := 0X;
  598. op[2] := 0X;
  599. opCodeExtension := -1;
  600. w.String('"');
  601. i := 0;
  602. nOp := 0;
  603. length := 0;
  604. REPEAT
  605. ch := code[i]; INC(i);
  606. CASE ch OF
  607. 0X:
  608. |" ":
  609. |"0".."9","A".."F":
  610. INC(length,2);
  611. (* options should not occur between opcodes, otherwise the order seems to be important...
  612. the only case where an option occurs between opcodes is for PAVGUSB: /r option which does not affect any order anyway
  613. *)
  614. CASE code[i] OF
  615. "0".."9","A".."F":
  616. w.Char(ch); w.Char(code[i]);
  617. op[nOp] := CHR(16*Hex(ch) + Hex(code[i])); INC(i);
  618. ELSE
  619. w.Char("0"); w.Char(ch);
  620. op[nOp] := CHR(Hex(ch));
  621. END;
  622. INC(nOp);
  623. |"/":
  624. ch := code[i]; INC(i);
  625. NextOption(ModRM);
  626. w.Char("/");
  627. CASE ch OF
  628. "0".."7":
  629. INC(length,2);
  630. w.Char(ch);
  631. Append(ModRM,"modRMExtension");
  632. opCodeExtension := ORD(ch)-ORD("0");
  633. |"r":
  634. INC(length);
  635. w.Char(ch);
  636. Append(ModRM,"modRMBoth");
  637. ELSE error := TRUE
  638. END;
  639. |"c":
  640. INC(length);
  641. ch := code[i]; INC(i);
  642. NextOption(ModRM);
  643. CASE ch OF
  644. "b","w","d","p": Append(ModRM,"c"); AppendCh(ModRM,ch); w.Char("c"); w.Char(ch);
  645. ELSE error := TRUE
  646. END;
  647. |"i":
  648. INC(length);
  649. ch := code[i]; INC(i);
  650. NextOption(ModRM);
  651. CASE ch OF
  652. "b","w","d","q": Append(ModRM,"i"); AppendCh(ModRM,ch);
  653. w.Char("i"); w.Char(ch);
  654. ELSE error := TRUE
  655. END;
  656. |"m":
  657. INC(length);
  658. NextOption(ModRM);
  659. IF Follows(code,i,"64") THEN
  660. Append(ModRM,"mem64Operand");
  661. w.String("m6");
  662. ELSIF Follows(code,i,"128") THEN
  663. Append(ModRM,"mem128Operand");
  664. w.String("m1");
  665. ELSE error := TRUE
  666. END;
  667. |"+":
  668. INC(length);
  669. ch := code[i]; INC(i);
  670. NextOption(ModRM);
  671. CASE ch OF
  672. 'i': Append(ModRM,"fpStackOperand");
  673. w.String("+i");
  674. |'o': Append(ModRM,"directMemoryOffset");
  675. w.String("+o");
  676. |'r': Append(ModRM,"r");
  677. ch := code[i]; INC(i);
  678. CASE ch OF
  679. 'b','w','d','q': AppendCh(ModRM,ch); w.Char("r"); w.Char(ch);
  680. ELSE w.String(" ERROR IN "); w.String(code); w.Update; HALT(100);
  681. END;
  682. ELSE error := TRUE
  683. END;
  684. ELSE
  685. error := TRUE
  686. END
  687. UNTIL error OR (ch = 0X);
  688. IF error THEN w.String(" ERROR IN "); w.String(code); w.String("at"); w.Int(i,1); w.Update; HALT(100); END;
  689. w.String('"');
  690. INC(length);
  691. IF length > maxOpcodeLength THEN
  692. maxOpcodeLength := length;
  693. END;
  694. (*
  695. w.Int(nOp,1);
  696. w.String(",");
  697. w.Hex(ORD(op[0]),1); w.String("X");
  698. w.String(",");
  699. w.Hex(ORD(op[1]),1); w.String("X");
  700. w.String(",");
  701. w.Hex(ORD(op[2]),1); w.String("X");
  702. w.String(",{");
  703. w.String(ModRM);
  704. w.String("}");
  705. w.String(", ");
  706. w.Int(opCodeExtension,1);
  707. *)
  708. END OpCode;
  709. PROCEDURE Options(CONST prefix,options: ARRAY OF CHAR);
  710. VAR i: LONGINT; first: BOOLEAN;
  711. PROCEDURE NextOptions;
  712. BEGIN
  713. WHILE(options[i] = ",") DO INC(i) END;
  714. IF options[i] = 0X THEN RETURN END;
  715. IF first THEN first := FALSE ELSE w.String(",") END;
  716. w.String(prefix);
  717. WHILE (options[i] # ",") & (options[i] # 0X) DO
  718. w.Char(options[i]);
  719. INC(i);
  720. END;
  721. END NextOptions;
  722. BEGIN
  723. i := 0;
  724. w.String("{");first := TRUE;
  725. WHILE(options[i] # 0X) DO
  726. NextOptions()
  727. END;
  728. w.String("}");
  729. END Options;
  730. PROCEDURE ParseLine(CONST line: ARRAY OF CHAR);
  731. VAR ch: CHAR; i : LONGINT;
  732. PROCEDURE NextSym(VAR sym: ARRAY OF CHAR);
  733. VAR len: LONGINT;
  734. BEGIN
  735. len := 0;
  736. ch:= line[i]; INC(i);
  737. WHILE(ch # TAB) & (ch # LF) & (ch # 0X) DO
  738. WHILE(ch = " ") DO ch:= line[i]; INC(i); END;
  739. sym[len] := ch; INC(len);
  740. ch:= line[i]; INC(i);
  741. END;
  742. sym[len] := 0X;
  743. END NextSym;
  744. BEGIN
  745. i := 0; ch := line[0];
  746. IF (ch = '"') OR (ch = ";") THEN (* comment line skipped *)
  747. ELSE
  748. NextSym(instr);
  749. IF instr = "" THEN
  750. ELSE
  751. mnemonic := firstMnemonic; prevmnemonic := NIL;
  752. WHILE(mnemonic # NIL) & (mnemonic.name < instr) DO
  753. prevmnemonic := mnemonic;
  754. mnemonic := mnemonic.next;
  755. END;
  756. IF (mnemonic = NIL) OR (mnemonic.name # instr) THEN
  757. NEW(mnemonic);
  758. COPY(instr,mnemonic.name);
  759. IF prevmnemonic = NIL THEN
  760. mnemonic.next := firstMnemonic;
  761. firstMnemonic := mnemonic;
  762. ELSE
  763. mnemonic.next := prevmnemonic.next;
  764. prevmnemonic.next := mnemonic;
  765. END;
  766. END;
  767. NEW(instruction);
  768. IF mnemonic.lastInstruction = NIL THEN
  769. mnemonic.lastInstruction := instruction;
  770. mnemonic.firstInstruction := instruction;
  771. ELSE
  772. mnemonic.lastInstruction.next := instruction;
  773. mnemonic.lastInstruction := instruction;
  774. END;
  775. NextSym(instruction.op1);
  776. NextSym(instruction.op2);
  777. NextSym(instruction.op3);
  778. NextSym(instruction.opcode);
  779. NextSym(instruction.target);
  780. NextSym(instruction.bitwidth);
  781. END;
  782. END;
  783. END ParseLine;
  784. BEGIN
  785. context.arg.SkipWhitespace; context.arg.String(fileName);
  786. context.out.String("parsing file "); context.out.String(fileName); context.out.Ln;
  787. IF fileName # "" THEN
  788. NEW(operandReplacer);
  789. operandReplacer.Add ("reg/mem8", "regmem8");
  790. operandReplacer.Add ("reg/mem16", "regmem16");
  791. operandReplacer.Add ("reg/mem32", "regmem32");
  792. operandReplacer.Add ("reg/mem64", "regmem64");
  793. operandReplacer.Add ("mem14/28env", "mem");
  794. operandReplacer.Add ("mem16&mem16", "mem");
  795. operandReplacer.Add ("mem32&mem32", "mem");
  796. operandReplacer.Add ("mem16:16", "mem");
  797. operandReplacer.Add ("mem16:32", "mem");
  798. operandReplacer.Add ("mem16:64", "mem");
  799. operandReplacer.Add ("mem512env", "mem");
  800. operandReplacer.Add ("mem80dec", "mem");
  801. operandReplacer.Add ("mem80real", "mem");
  802. operandReplacer.Add ("mem94/108env", "mem");
  803. operandReplacer.Add ("mem2env", "mem16");
  804. operandReplacer.Add ("xmm1", "xmm");
  805. operandReplacer.Add ("xmm2", "xmm");
  806. operandReplacer.Add ("xmm/mem", "xmmmem");
  807. operandReplacer.Add ("xmm/mem32", "xmmmem32");
  808. operandReplacer.Add ("xmm/mem64", "xmmmem64");
  809. operandReplacer.Add ("xmm/mem128", "xmmmem128");
  810. operandReplacer.Add ("xmm1/mem32", "xmmmem32");
  811. operandReplacer.Add ("xmm1/mem64", "xmmmem64");
  812. operandReplacer.Add ("xmm1/mem128", "xmmmem128");
  813. operandReplacer.Add ("xmm2/mem32", "xmmmem32");
  814. operandReplacer.Add ("xmm2/mem64", "xmmmem64");
  815. operandReplacer.Add ("xmm2/mem128", "xmmmem128");
  816. operandReplacer.Add ("mmx/mem64", "mmxmem64");
  817. operandReplacer.Add ("mmx1", "mmx");
  818. operandReplacer.Add ("mmx2", "mmx");
  819. operandReplacer.Add ("mmx1/mem64", "mmxmem64");
  820. operandReplacer.Add ("mmx2/mem32", "mmxmem32");
  821. operandReplacer.Add ("mmx2/mem64", "mmxmem64");
  822. operandReplacer.Add ("pntr16:16", "pntr1616");
  823. operandReplacer.Add ("pntr16:32", "pntr1632");
  824. operandReplacer.Add ("mem16int", "mem16");
  825. operandReplacer.Add ("mem32int", "mem32");
  826. operandReplacer.Add ("mem32real", "mem32");
  827. operandReplacer.Add ("mem64int", "mem64");
  828. operandReplacer.Add ("mem64real", "mem64");
  829. (* operandReplacer.Add ("rel8off", "imm");
  830. operandReplacer.Add ("rel16off", "imm");
  831. operandReplacer.Add ("rel32off", "imm");
  832. *)
  833. operandReplacer.Add ("ST(0)", "st0");
  834. operandReplacer.Add ("ST(i)", "sti");
  835. NEW(cpuoptionReplacer);
  836. cpuoptionReplacer.Add ("KATMAI", "Katmai");
  837. cpuoptionReplacer.Add ("PRESCOTT", "Prescott");
  838. cpuoptionReplacer.Add ("WILLAMETTE", "Willamette");
  839. cpuoptionReplacer.Add ("PENTIUM", "Pentium");
  840. cpuoptionReplacer.Add ("3DNOW", "3DNow");
  841. cpuoptionReplacer.Add ("PRIV", "Privileged");
  842. cpuoptionReplacer.Add ("PROT", "Protected");
  843. cpuoptionReplacer.Add ("SW","");
  844. cpuoptionReplacer.Add ("SB","");
  845. cpuoptionReplacer.Add ("SMM","");
  846. cpuoptionReplacer.Add ("AR1","");
  847. cpuoptionReplacer.Add ("AR2","");
  848. cpuoptionReplacer.Add ("ND","");
  849. NEW(bitwidthReplacer);
  850. bitwidthReplacer.Add("INV","I64");
  851. file := Files.Old(fileName);
  852. NEW(reader,file,0);
  853. GetCh();
  854. WHILE(ch # 0X) DO
  855. GetLine(line);
  856. ParseLine(line);
  857. END;
  858. w := context.out;
  859. numberMnemonics := 0;
  860. numberInstructions := 0;
  861. maxMnemonicNameLength := 0;
  862. mnemonic := firstMnemonic;
  863. maxOpcodeLength:= 0;
  864. WHILE(mnemonic # NIL) DO
  865. IF Strings.Length(mnemonic.name)+1 > maxMnemonicNameLength THEN maxMnemonicNameLength := Strings.Length(mnemonic.name)+1 END;
  866. w.Char(09X); w.Char(09X); w.String("StartMnemonic(");
  867. w.String("op");
  868. w.String(mnemonic.name);
  869. w.String(", ");
  870. w.Char('"');
  871. w.String(mnemonic.name);
  872. w.Char('"');
  873. w.String(", ");
  874. w.Int(numberInstructions,1);
  875. w.String(");");
  876. w.Ln;
  877. INC(numberMnemonics);
  878. instruction := mnemonic.firstInstruction;
  879. WHILE(instruction # NIL) DO
  880. operandReplacer.Do(instruction.op1);
  881. operandReplacer.Do(instruction.op2);
  882. operandReplacer.Do(instruction.op3);
  883. bitwidthReplacer.Do(instruction.bitwidth);
  884. cpuoptionReplacer.Do(instruction.target);
  885. instruction := instruction.next;
  886. END;
  887. SortInstructions(mnemonic.firstInstruction);
  888. instruction := mnemonic.firstInstruction;
  889. WHILE(instruction # NIL) DO
  890. w.Char(09X); w.Char(09X); w.String("AddInstruction(");
  891. w.Int(numberInstructions,1); w.String(", ");
  892. Operand(instruction.op1); w.String(", ");
  893. Operand(instruction.op2); w.String(", ");
  894. Operand(instruction.op3); w.String(", ");
  895. OpCode(instruction.opcode); w.String(", ");
  896. Options("opt",instruction.bitwidth); w.String(", ");
  897. Options("cpu",instruction.target); w.String(");");
  898. w.Ln;
  899. INC(numberInstructions);
  900. instruction := instruction.next;
  901. END;
  902. w.Char(09X); w.Char(09X); w.String("EndMnemonic(");
  903. w.String("op"); w.String(mnemonic.name);
  904. w.String(", ");
  905. w.Int(numberInstructions-1,1);
  906. w.String(");");
  907. w.Ln;
  908. w.Ln;
  909. mnemonic := mnemonic.next;
  910. END;
  911. w.Char(09X); w.String("numberMnemonics = "); w.Int(numberMnemonics,1); w.String(";"); w.Ln;
  912. w.Char(09X); w.String("numberInstructions = "); w.Int(numberInstructions,1); w.String(";");w.Ln;
  913. w.Char(09X); w.String("maxMnemonicNameLength ="); w.Int(maxMnemonicNameLength,1); w.String(";"); w.Ln;
  914. w.Char(09X); w.String("maxCodeLength* ="); w.Int(maxOpcodeLength,1); w.String(";"); w.Ln;
  915. mnemonic := firstMnemonic; numberMnemonics := 0;
  916. WHILE(mnemonic # NIL) DO
  917. w.Char(09X); w.String("op"); w.String(mnemonic.name);
  918. w.String("* = ");
  919. w.Int(numberMnemonics,1); w.String(";");
  920. w.Ln;
  921. INC(numberMnemonics);
  922. mnemonic := mnemonic.next;
  923. END;
  924. w.Update;
  925. context.out.String("done"); context.out.Ln;
  926. ELSE
  927. context.error.String("filename expected"); context.error.Ln;
  928. END;
  929. END ParseAMDInstructionSet;
  930. END FoxProgTools.
  931. SystemTools.Free FoxProgTools ~
  932. FoxProgTools.Enum -l=2 a b c (* test (* test *) *) d e f g h i j k (* ddd *) d d d ~
  933. FoxProgTools.Enum --incremental a b c d e ~
  934. FoxProgTools.Enum --start=10 a b c d e ~
  935. FoxProgTools.Enum --start=10 --hex a b c d e ~
  936. FoxProgTools.ParseAMDInstructionSet FoxInstructionSetAMD64TabSeperated.txt ~
  937. FoxProgTools.ParseARMInstructionSet OC/FoxInstructionSetARM.txt ~