trans8to64.mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. MODULE trans8to64 ;
  2. (* trans8to64 : translate a classic 16-bit Turbo Modula-2 .MCD binary
  3. into a 64-bit MC64 .MC4 image that mcint can boot.
  4. 16-bit file layout (little-endian) ::
  5. header : 16 bytes
  6. fileSize u16 (byte count not counting header)
  7. moduleStart u16 (blob offset of the module descriptor)
  8. codeSize u16 (size of the blob that follows the header)
  9. nbDependencies u16
  10. reserved [8] bytes
  11. blob : codeSize bytes
  12. + moduleStart descriptor:
  13. +66..73 name[8] +74,75 loadAddr
  14. +76,77 checksum +78,79 procsAddr
  15. +80 flags (bit2=TOINIT)
  16. +81 varCount +82 proc count k0 +83 depCount
  17. +84 var sizes : varCount*u16 BYTES
  18. proc table : cell i (i16) at procsAddr-2*i, descending;
  19. proc_i = (procsAddr-2*i) + 1 + cell
  20. scan i while cell<0 ; codeEnd = procsAddr-2*k0
  21. dependencies : nbDeps * 12 bytes (dropped by mc64)
  22. 16-bit dependencies : nbDependencies u16 (header +6) ; records of 12 bytes
  23. (name[8], version u16, location u16) sit at blob offset +4 (header +4),
  24. at the tail of the blob. The 64-bit image carries dcnt (DescDepCount)
  25. and an 8-byte name per dependency, appended at the image tail.
  26. 64-bit output : 64-byte "MC64" header + descriptor + translated code +
  27. rebuilt proc table (cells relative to cell start) + dcnt*8 dep-name table.
  28. Operand translation (locked against mc64/Interpreter.mod) ::
  29. 8E word imm 2B -> 8B zero-extended (CARDINAL/word semantics)
  30. 8F dword imm 4B -> 8B via 8E, sign-extended (LONGINT constant)
  31. CD switch u16 low/high(range size)/ret ; cells = high+1
  32. 64-bit: low u64, highw=low+high u64, t u64, cells i64
  33. cells relative to cell-start+8 ; call-switch return
  34. pushed as eot+t (mc64 CD honours t)
  35. CF push_code 2B -> 8B, target preserved
  36. E0/E1 jp 2B -> 8B, target preserved
  37. DC limit_check : 16-bit pops its limit (no operand) ; the 64-bit form
  38. reads a u8 immediate. Reference modules only contain DC in dead code
  39. (right after a leave), so we emit `DC 00` (never executed).
  40. All other opcodes have identical operand widths and pass through.
  41. Limitations :
  42. - global slots / module-table address tricks (e.g. 0xFFF7/0xFFFF) do
  43. not map onto the mc64 data window => globals misbehave at run time.
  44. - extern references 0xEF/0xF0 point at dependencies; mc64 sets dcnt=0,
  45. so such calls crash if executed.
  46. - 8F used for REAL constants decodes as a LONGINT.
  47. - runnable only for dependency-free modules; TOINIT (flags bit2) is
  48. preserved, so a module with a real proc0 init runs under mcint.
  49. *)
  50. FROM FileIO IMPORT ReadFile, WriteFile ;
  51. FROM Console IMPORT WriteString, WriteLn, WriteLongCard, Fatal ;
  52. FROM ProgramArgs IMPORT ArgChan, NextArg ;
  53. FROM TextIO IMPORT ReadString ;
  54. CONST
  55. MaxBuf = 65535 ;
  56. HeaderSize = 64 ;
  57. DescName = 264 ;
  58. DescLoadAddr = 280 ;
  59. DescChecksum = 288 ;
  60. DescFlags = 292 ;
  61. DescVarCount = 293 ;
  62. DescDepCount = 294 ;
  63. DescPad = 295 ;
  64. DescProcs = 296 ;
  65. DescVarSizes = 304 ;
  66. VAR
  67. inName, outName : ARRAY [0 .. 200] OF CHAR ;
  68. src : ARRAY [0 .. MaxBuf] OF CHAR ;
  69. dst : ARRAY [0 .. MaxBuf] OF CHAR ;
  70. map : ARRAY [0 .. MaxBuf] OF LONGCARD ;
  71. got : CARDINAL ;
  72. ms, cs : CARDINAL ;
  73. ndeps, deps16 : CARDINAL ;
  74. k2 : CARDINAL ;
  75. desc, procs16 : CARDINAL ;
  76. k0 : CARDINAL ;
  77. codeEnd : CARDINAL ;
  78. flags8, vcnt : CARDINAL ;
  79. varSizes : ARRAY [0 .. 31] OF CARDINAL ;
  80. procAddrs : ARRAY [0 .. 255] OF CARDINAL ;
  81. ents : ARRAY [0 .. 256] OF CARDINAL ;
  82. nEnt : CARDINAL ;
  83. codeOff, procTabImg, imgLen : CARDINAL ;
  84. i, j, sum : CARDINAL ;
  85. PROCEDURE H16 (off : CARDINAL) : CARDINAL ;
  86. BEGIN
  87. RETURN ORD (src [off]) + 256 * ORD (src [off + 1]) ;
  88. END H16 ;
  89. PROCEDURE B (p : CARDINAL) : CARDINAL ;
  90. BEGIN
  91. RETURN ORD (src [16 + p]) ;
  92. END B ;
  93. PROCEDURE Get16 (p : CARDINAL) : CARDINAL ;
  94. BEGIN
  95. IF (p > MaxBuf) OR (p + 1 > MaxBuf) THEN
  96. Fatal ("trans8to64: Get16 out of range") ;
  97. END ;
  98. RETURN ORD (src [16 + p]) + 256 * ORD (src [16 + p + 1]) ;
  99. END Get16 ;
  100. PROCEDURE Sig16 (p : CARDINAL) : LONGINT ;
  101. VAR v : CARDINAL ;
  102. BEGIN
  103. v := Get16 (p) ;
  104. IF v >= 32768 THEN
  105. RETURN VAL (LONGINT, v) - 65536 ;
  106. ELSE
  107. RETURN VAL (LONGINT, v) ;
  108. END ;
  109. END Sig16 ;
  110. PROCEDURE Align8 (c : CARDINAL) : CARDINAL ;
  111. BEGIN
  112. IF (c MOD 8) # 0 THEN
  113. RETURN c + 8 - (c MOD 8) ;
  114. ELSE
  115. RETURN c ;
  116. END ;
  117. END Align8 ;
  118. PROCEDURE WB (off : CARDINAL; v : CARDINAL) ;
  119. BEGIN
  120. dst [HeaderSize + off] := CHR (v MOD 256) ;
  121. END WB ;
  122. PROCEDURE Put64 (off : CARDINAL; v : LONGCARD) ;
  123. VAR j : CARDINAL ;
  124. BEGIN
  125. FOR j := 0 TO 7 DO
  126. dst [HeaderSize + off + j] := CHR (VAL (CARDINAL, v MOD 256)) ;
  127. v := v DIV 256 ;
  128. END ;
  129. END Put64 ;
  130. PROCEDURE Put32 (off, v : CARDINAL) ;
  131. BEGIN
  132. dst [HeaderSize + off] := CHR (v MOD 256) ;
  133. dst [HeaderSize + off + 1] := CHR ((v DIV 256) MOD 256) ;
  134. dst [HeaderSize + off + 2] := CHR ((v DIV 65536) MOD 256) ;
  135. dst [HeaderSize + off + 3] := CHR (v DIV 16777216) ;
  136. END Put32 ;
  137. (* number of sub-opcode operand bytes consumed by 0x12 quad ops *)
  138. PROCEDURE QuadLen (sub : CARDINAL) : CARDINAL ;
  139. BEGIN
  140. CASE sub OF
  141. | 0H, 1H, 2H, 4H, 5H, 6H : RETURN 1 ;
  142. | 3H, 7H : RETURN 2 ;
  143. | 0AH : RETURN 1 ;
  144. ELSE RETURN 0 ;
  145. END ;
  146. END QuadLen ;
  147. PROCEDURE InstrLen16 (p : CARDINAL) : CARDINAL ;
  148. VAR op, sub, v : CARDINAL ;
  149. BEGIN
  150. op := B (p) ;
  151. CASE op OF
  152. | 02H : RETURN 2 ;
  153. | 08H, 09H, 0AH, 0CH,
  154. 11H, 18H, 19H, 1AH, 1CH,
  155. 2CH, 2DH, 2EH,
  156. 3CH, 3DH, 3EH,
  157. 40H,
  158. 80H, 81H, 82H, 84H, 85H, 86H, 87H, 8DH,
  159. 0AEH, 0AFH, 0B0H, 0B1H,
  160. 0D4H, 0DEH, 0DFH,
  161. 0ECH, 0EDH, 0EEH, 0F0H : RETURN 2 ;
  162. | 0BH, 1BH, 2FH, 3FH, 83H, 0EFH : RETURN 3 ;
  163. | 12H : sub := B (p + 1) ; RETURN 2 + QuadLen (sub) ;
  164. | 8CH : RETURN 2 + B (p + 1) ;
  165. | 8EH, 0CFH, 0E0H, 0E1H : RETURN 3 ;
  166. | 8FH : RETURN 5 ;
  167. | 0CDH : v := Get16 (p + 3) ;
  168. IF v >= 30000 THEN
  169. v := 30000 ;
  170. END ;
  171. RETURN 7 + 2 * (v + 1) ; ELSE RETURN 1 ;
  172. END ;
  173. END InstrLen16 ;
  174. PROCEDURE InstrInfo (p : CARDINAL; VAR sz16, out, op : CARDINAL) ;
  175. BEGIN
  176. op := B (p) ;
  177. sz16 := InstrLen16 (p) ;
  178. out := sz16 ;
  179. CASE op OF
  180. | 8EH, 8FH, 0CFH, 0E0H, 0E1H : out := 9 ;
  181. | 0CDH : out := 25 + 8 * (Get16 (p + 3) + 1) ;
  182. | 0DCH : out := 2 ;
  183. ELSE out := sz16 ;
  184. END ;
  185. END InstrInfo ;
  186. (* pass 1 : assign image offsets to every instruction in [start, stop) *)
  187. PROCEDURE Laying (start, stop : CARDINAL; VAR startImg : CARDINAL) ;
  188. VAR p, op, sz16, out, cur : CARDINAL ;
  189. BEGIN
  190. p := start ;
  191. cur := startImg ;
  192. WHILE p < stop DO
  193. InstrInfo (p, sz16, out, op) ;
  194. IF p + sz16 > stop THEN
  195. sz16 := stop - p ;
  196. out := sz16 ;
  197. END ;
  198. map [p] := VAL (LONGCARD, cur) ;
  199. IF cur + out > MaxBuf THEN
  200. Fatal ("trans8to64: output too large") ;
  201. END ;
  202. cur := cur + out ;
  203. p := p + sz16 ;
  204. END ;
  205. map [stop] := VAL (LONGCARD, cur) ;
  206. startImg := cur ;
  207. END Laying ;
  208. (* pass 2 : emit [start, stop) at curImg (must follow pass 1 exactly) *)
  209. PROCEDURE Emitting (start, stop : CARDINAL; VAR curImg : CARDINAL) ;
  210. VAR p, op, sz16, out, cur, j, N, low, high, ret, tgt16, lsw, msw : CARDINAL ;
  211. first, eot, t, off64, rel64, cellImg, idx : LONGCARD ;
  212. BEGIN
  213. p := start ;
  214. cur := curImg ;
  215. WHILE p < stop DO
  216. InstrInfo (p, sz16, out, op) ;
  217. IF p + sz16 > stop THEN
  218. sz16 := stop - p ;
  219. out := sz16 ;
  220. op := 0H ;
  221. END ;
  222. IF op = 8EH THEN
  223. WB (cur, 8EH) ;
  224. Put64 (cur + 1, VAL (LONGCARD, Get16 (p + 1))) ;
  225. ELSIF op = 8FH THEN
  226. lsw := Get16 (p + 1) ;
  227. msw := Get16 (p + 3) ;
  228. IF B (p + 4) >= 128 THEN
  229. off64 := VAL (LONGCARD,
  230. (VAL (LONGINT, msw) - 65536) * 65536 +
  231. VAL (LONGINT, lsw)) ;
  232. ELSE
  233. off64 := VAL (LONGCARD,
  234. VAL (LONGINT, msw) * 65536 + VAL (LONGINT, lsw)) ;
  235. END ;
  236. WB (cur, 8EH) ;
  237. Put64 (cur + 1, off64) ;
  238. ELSIF op = 0CFH THEN
  239. tgt16 := VAL (CARDINAL,
  240. VAL (LONGINT, p) + 2 + Sig16 (p + 1)) ;
  241. off64 := map [tgt16] - (VAL (LONGCARD, cur) + 8) ;
  242. WB (cur, 0CFH) ;
  243. Put64 (cur + 1, off64) ;
  244. ELSIF (op = 0E0H) OR (op = 0E1H) THEN
  245. tgt16 := VAL (CARDINAL,
  246. VAL (LONGINT, p) + 2 + Sig16 (p + 1)) ;
  247. off64 := map [tgt16] - (VAL (LONGCARD, cur) + 9) ;
  248. WB (cur, op) ;
  249. Put64 (cur + 1, off64) ;
  250. ELSIF op = 0DCH THEN
  251. WB (cur, 0DCH) ;
  252. WB (cur + 1, 0) ;
  253. ELSIF op = 0CDH THEN
  254. low := Get16 (p + 1) ;
  255. high := Get16 (p + 3) ;
  256. ret := Get16 (p + 5) ;
  257. N := high + 1 ;
  258. WB (cur, 0CDH) ;
  259. Put64 (cur + 1, VAL (LONGCARD, low)) ;
  260. Put64 (cur + 9, VAL (LONGCARD, low + high)) ;
  261. first := VAL (LONGCARD, cur) + 25 ;
  262. eot := first + VAL (LONGCARD, N) * 8 ;
  263. idx := VAL (LONGCARD, p) + 6 + VAL (LONGCARD, ret) ;
  264. IF idx > VAL (LONGCARD, MaxBuf) THEN
  265. idx := VAL (LONGCARD, codeEnd) ;
  266. END ;
  267. t := map [VAL (CARDINAL, idx)] - eot ;
  268. Put64 (cur + 17, t) ;
  269. FOR j := 0 TO N - 1 DO
  270. cellImg := VAL (LONGCARD, cur) + 25 + VAL (LONGCARD, j) * 8 ;
  271. tgt16 := VAL (CARDINAL,
  272. VAL (LONGINT, p) + 7 + VAL (LONGINT, j) * 2 +
  273. 1 + Sig16 (p + 7 + 2 * j)) ;
  274. rel64 := map [tgt16] - (cellImg + 8) ;
  275. Put64 (VAL (CARDINAL, cellImg), rel64) ;
  276. END ;
  277. ELSE
  278. FOR j := 0 TO sz16 - 1 DO
  279. WB (cur + j, B (p + j)) ;
  280. END ;
  281. END ;
  282. cur := cur + out ;
  283. p := p + sz16 ;
  284. END ;
  285. curImg := cur ;
  286. END Emitting ;
  287. (* insert an entry, keeping ents[] sorted and unique *)
  288. PROCEDURE AddEnt (e : CARDINAL) ;
  289. VAR i, j : CARDINAL ;
  290. BEGIN
  291. i := 0 ;
  292. WHILE (i < nEnt) AND (ents [i] < e) DO
  293. INC (i) ;
  294. END ;
  295. IF (i < nEnt) AND (ents [i] = e) THEN
  296. RETURN ;
  297. END ;
  298. j := nEnt ;
  299. WHILE j > i DO
  300. ents [j] := ents [j - 1] ;
  301. DEC (j) ;
  302. END ;
  303. ents [i] := e ;
  304. INC (nEnt) ;
  305. END AddEnt ;
  306. PROCEDURE Usage ;
  307. BEGIN
  308. WriteString ("usage: trans8to64 in.MCD out.MC4") ;
  309. WriteLn ;
  310. END Usage ;
  311. BEGIN
  312. NextArg ;
  313. ReadString (ArgChan (), inName) ;
  314. NextArg ;
  315. ReadString (ArgChan (), outName) ;
  316. IF (inName [0] = 0C) OR (outName [0] = 0C) THEN
  317. Usage ;
  318. HALT (1) ;
  319. END ;
  320. IF NOT ReadFile (inName, src, got) THEN
  321. Fatal ("trans8to64: cannot read input") ;
  322. END ;
  323. IF got <= 16 THEN
  324. Fatal ("trans8to64: input too short") ;
  325. END ;
  326. FOR i := 0 TO MaxBuf DO
  327. dst [i] := 0C ;
  328. END ;
  329. ms := H16 (2) ;
  330. cs := H16 (4) ;
  331. IF 16 + cs > got THEN
  332. Fatal ("trans8to64: header/blob size mismatch") ;
  333. END ;
  334. ndeps := H16 (6) ;
  335. deps16 := cs ;
  336. IF ndeps > 32 THEN
  337. Fatal ("trans8to64: too many dependencies") ;
  338. END ;
  339. IF 16 + deps16 + ndeps * 12 > got THEN
  340. Fatal ("trans8to64: dependency table out of range") ;
  341. END ;
  342. IF ms + 84 > cs THEN
  343. Fatal ("trans8to64: descriptor out of range") ;
  344. END ;
  345. desc := ms ;
  346. procs16 := Get16 (desc + 78) ;
  347. flags8 := B (desc + 80) ;
  348. vcnt := B (desc + 81) ;
  349. IF vcnt > 32 THEN
  350. Fatal ("trans8to64: too many var sizes") ;
  351. END ;
  352. IF desc + 84 + 2 * vcnt > cs THEN
  353. Fatal ("trans8to64: var sizes out of range") ;
  354. END ;
  355. IF procs16 + 2 > cs THEN
  356. Fatal ("trans8to64: procs table out of range") ;
  357. END ;
  358. (* count proc-table cells (negative, descending) *)
  359. k0 := 0 ;
  360. WHILE (procs16 >= 2 * k0) AND (Sig16 (procs16 - 2 * k0) < 0) DO
  361. INC (k0) ;
  362. END ;
  363. IF k0 > 255 THEN
  364. Fatal ("trans8to64: too many procedures") ;
  365. END ;
  366. codeEnd := procs16 - 2 * k0 ;
  367. IF k0 > 0 THEN
  368. FOR j := 0 TO k0 - 1 DO
  369. procAddrs [j] := VAL (CARDINAL,
  370. VAL (LONGINT, procs16 - 2 * j) + 1 +
  371. Sig16 (procs16 - 2 * j)) ;
  372. END ;
  373. END ;
  374. (* sorted unique proc entries + final code end *)
  375. nEnt := 0 ;
  376. IF k0 > 0 THEN
  377. FOR j := 0 TO k0 - 1 DO
  378. IF procAddrs [j] <= codeEnd THEN
  379. AddEnt (procAddrs [j]) ;
  380. END ;
  381. END ;
  382. END ;
  383. AddEnt (codeEnd) ;
  384. (* 16-bit var sizes *)
  385. IF vcnt > 0 THEN
  386. FOR j := 0 TO vcnt - 1 DO
  387. varSizes [j] := Get16 (desc + 84 + 2 * j) ;
  388. END ;
  389. END ;
  390. codeOff := Align8 (304 + vcnt * 8) ;
  391. IF codeOff < 312 THEN
  392. codeOff := 312 ;
  393. END ;
  394. (* pass 1 : assign positions ; intervals start at each proc entry *)
  395. procTabImg := codeOff ;
  396. IF nEnt >= 2 THEN
  397. FOR j := 0 TO nEnt - 2 DO
  398. Laying (ents [j], ents [j + 1], procTabImg) ;
  399. END ;
  400. END ;
  401. (* descriptor *)
  402. FOR j := 0 TO 7 DO
  403. WB (DescName + j, B (desc + 66 + j)) ;
  404. END ;
  405. Put64 (DescLoadAddr, VAL (LONGCARD, Get16 (desc + 74))) ;
  406. WB (DescFlags, flags8) ;
  407. WB (DescVarCount, vcnt) ;
  408. WB (DescDepCount, ndeps) ;
  409. WB (DescPad, 0) ;
  410. IF vcnt > 0 THEN
  411. FOR j := 0 TO vcnt - 1 DO
  412. Put64 (DescVarSizes + j * 8, VAL (LONGCARD, Align8 (varSizes [j]))) ;
  413. END ;
  414. END ;
  415. (* pad the code region to 8 before the proc table *)
  416. procTabImg := Align8 (procTabImg) ;
  417. (* pass 2 : emit *)
  418. imgLen := codeOff ;
  419. FOR j := 0 TO nEnt - 2 DO
  420. Emitting (ents [j], ents [j + 1], imgLen) ;
  421. END ;
  422. imgLen := Align8 (imgLen) ;
  423. IF imgLen > procTabImg THEN
  424. procTabImg := imgLen ;
  425. END ;
  426. Put64 (DescProcs, VAL (LONGCARD, procTabImg)) ;
  427. (* rebuild the proc table : cell = target - cellAddr *)
  428. FOR j := 0 TO k0 - 1 DO
  429. Put64 (procTabImg + j * 8,
  430. map [procAddrs [j]] - VAL (LONGCARD, procTabImg + j * 8)) ;
  431. END ;
  432. imgLen := procTabImg + k0 * 8 ;
  433. (* append the dependency name table at the image tail : 8 bytes each *)
  434. IF ndeps > 0 THEN
  435. IF imgLen + ndeps * 8 > MaxBuf THEN
  436. Fatal ("trans8to64: output too large") ;
  437. END ;
  438. FOR j := 0 TO ndeps - 1 DO
  439. FOR k2 := 0 TO 7 DO
  440. WB (imgLen + k2, B (deps16 + j * 12 + k2)) ;
  441. END ;
  442. INC (imgLen, 8) ;
  443. END ;
  444. END ;
  445. (* file header : magic *)
  446. dst [0] := 'M' ;
  447. dst [1] := 'C' ;
  448. dst [2] := '6' ;
  449. dst [3] := '4' ;
  450. (* checksum over the image bytes, excluding the checksum field itself *)
  451. sum := 0 ;
  452. FOR i := HeaderSize TO HeaderSize + imgLen - 1 DO
  453. IF NOT ((i >= 352) AND (i <= 355)) THEN
  454. sum := sum + ORD (dst [i]) ;
  455. END ;
  456. END ;
  457. Put32 (DescChecksum, sum) ;
  458. IF HeaderSize + imgLen > MaxBuf + 1 THEN
  459. Fatal ("trans8to64: output too large") ;
  460. END ;
  461. IF NOT WriteFile (outName, dst, HeaderSize + imgLen) THEN
  462. Fatal ("trans8to64: cannot write output") ;
  463. END ;
  464. WriteString ("trans8to64: ") ;
  465. WriteString (inName) ;
  466. WriteString (" -> ") ;
  467. WriteString (outName) ;
  468. WriteString (" : ok (") ;
  469. WriteLongCard (VAL (LONGCARD, HeaderSize + imgLen)) ;
  470. WriteString (" bytes)") ;
  471. WriteLn ;
  472. END trans8to64.