CompilerEmit.mod 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. IMPLEMENTATION MODULE CompilerEmit ;
  2. (* Low-level 8086 emission: byte/word writers, patch list, calls/jumps,
  3. short conditional branches and the primitive instruction helpers.
  4. All shared state lives in CompilerShared; Compiler imports these names
  5. unqualified, so the parser and code generator are unchanged. *)
  6. FROM SYSTEM IMPORT BYTE ;
  7. FROM CompilerShared IMPORT MaxCode, CodeMax, MaxPatch, EMemOvf, ECompOvf,
  8. cbuf, pc, imgReady, patches, nPatch, Err ;
  9. (* ---------------------------------------------------------------- *)
  10. (* emission : ebyte / eword / ecall / ejump *)
  11. (* ---------------------------------------------------------------- *)
  12. PROCEDURE Ebyte (b : BYTE) ;
  13. BEGIN
  14. IF (pc >= MaxCode) OR (imgReady AND (pc >= CodeMax)) THEN
  15. (* The image buffer bound (MaxCode) and the boot harness's code ceiling
  16. (CodeMax). Globals no longer sit immediately above the code - they
  17. live at DataStart and initmem zeroes them - so this is purely a
  18. capacity check: cbuf cannot hold another byte, or the image would
  19. reach the configured data base. Reported as TP3's error
  20. 98 (memory overflow) rather than a silent truncation. *)
  21. Err (EMemOvf)
  22. ELSE
  23. cbuf [pc] := b ;
  24. INC (pc)
  25. END
  26. END Ebyte ;
  27. PROCEDURE Eword (w : CARDINAL) ;
  28. BEGIN
  29. Ebyte (VAL (BYTE, w MOD 100H)) ;
  30. Ebyte (VAL (BYTE, (w DIV 100H) MOD 100H))
  31. END Eword ;
  32. PROCEDURE PatchWord (at, w : CARDINAL) ;
  33. (* Store a 16-bit word into cbuf at an absolute offset.
  34. A helper, because writing this inline got it wrong in all four header
  35. words: the low byte was (w DIV 16) MOD 100H, which is a NIBBLE shift, not
  36. the byte shift (w MOD 100H). So 1181h - the data base - was stored as
  37. 0118h = 280. It was invisible for as long as nothing read those words,
  38. which is exactly what "write it inline once and trust it" buys you. *)
  39. BEGIN
  40. cbuf [at] := VAL (BYTE, w MOD 100H) ;
  41. cbuf [at + 1] := VAL (BYTE, (w DIV 100H) MOD 100H)
  42. END PatchWord ;
  43. PROCEDURE AddPatch (place, target : CARDINAL) ;
  44. BEGIN
  45. IF nPatch < MaxPatch THEN
  46. patches [nPatch].place := place ;
  47. patches [nPatch].target := target ;
  48. patches [nPatch].filled := FALSE ;
  49. INC (nPatch)
  50. ELSE
  51. Err (ECompOvf)
  52. END
  53. END AddPatch ;
  54. PROCEDURE SetPatTgt (idx, t : CARDINAL) ;
  55. BEGIN
  56. IF idx < nPatch THEN
  57. patches [idx].target := t
  58. END
  59. END SetPatTgt ;
  60. PROCEDURE EmCall (target : CARDINAL) : CARDINAL ;
  61. (* E8 rel16 near call; target = 0 => forward (patched later).
  62. Returns the patch slot, or 0 when resolved directly. *)
  63. VAR rel, p : CARDINAL ;
  64. BEGIN
  65. Ebyte (0E8H) ;
  66. IF target = 0 THEN
  67. Eword (0) ;
  68. p := nPatch ;
  69. AddPatch (pc - 2, 0) ;
  70. RETURN p
  71. END ;
  72. (* rel16 is measured from the END of the instruction. Here pc already
  73. points past the opcode(s) and at the displacement field, so the
  74. instruction ends at pc+2 - the same convention ResolvePatches uses
  75. with "place + 2". Omitting the +2 lands every direct call/jump 2 bytes
  76. past its target. *)
  77. rel := (target + 10000H - (pc + 2)) MOD 10000H ;
  78. Eword (rel) ;
  79. RETURN 0
  80. END EmCall ;
  81. PROCEDURE EmJmpNear (target : CARDINAL) : CARDINAL ;
  82. (* E9 rel16; target = 0 => forward. Returns patch slot or 0. *)
  83. VAR rel, p : CARDINAL ;
  84. BEGIN
  85. Ebyte (0E9H) ;
  86. IF target = 0 THEN
  87. Eword (0) ;
  88. p := nPatch ;
  89. AddPatch (pc - 2, 0) ;
  90. RETURN p
  91. END ;
  92. rel := (target + 10000H - (pc + 2)) MOD 10000H ; (* see EmCall *)
  93. Eword (rel) ;
  94. RETURN 0
  95. END EmJmpNear ;
  96. PROCEDURE JccShort (cc : BYTE) : BYTE ;
  97. (* The 8086 SHORT Jcc opcode for a condition nibble. 70h..7Fh is exactly
  98. 70h + nibble: 70 JO 71 JNO 72 JB 73 JAE 74 JE 75 JNE 76 JBE 77 JA
  99. 78 JS 79 JNS 7A JP 7B JNP 7C JL 7D JGE 7E JLE 7F JG.
  100. So 70H + cc is the same condition the 386-only `0F 8x rel16' (for a Jcc) or
  101. `0F 9x' (for a SETcc) encoded, which is what lets the seven EmJcc sites and
  102. the six EmSetcc arms go on passing the low byte they always passed. *)
  103. VAR n : CARDINAL ;
  104. BEGIN
  105. n := VAL (CARDINAL, cc) MOD 10H ;
  106. RETURN VAL (BYTE, 70H + n)
  107. END JccShort ;
  108. PROCEDURE JccShortInv (cc : BYTE) : BYTE ;
  109. (* The same, for a jump that is taken when the condition does NOT hold.
  110. EmJcc needs this one and EmSetcc needs the other, and the difference is the
  111. whole bug, so it is worth being explicit about where it comes from: the low
  112. bit of a Jcc code IS the negation bit. 4/5, C/D, E/F, 2/3, 6/7, A/9, B/8 and
  113. 0/1 are the eight (condition, its negation) pairs, so negating a condition is
  114. `n XOR 1' and nothing more - `JE' and `JNE' are 0x74 and 0x75.
  115. Gm2 under -fiso has no XOR on integers at all: BITAND and BAND are both
  116. syntax errors, and arithmetic on a BYTE operand is rejected too, which is
  117. why every operand here goes through VAL. n + 1 - 2*(n MOD 2) is XOR 1 for a
  118. four-bit n and it lives in one named place rather than open-coded, because
  119. an open-coded negation at two call sites is how they end up disagreeing. *)
  120. VAR n : CARDINAL ;
  121. BEGIN
  122. n := VAL (CARDINAL, cc) MOD 10H ;
  123. RETURN VAL (BYTE, 70H + n + 1 - 2 * (n MOD 2))
  124. END JccShortInv ;
  125. PROCEDURE EmJcc (cc : BYTE ; target : CARDINAL) : CARDINAL ;
  126. (* A conditional branch, 8086 style. `cc' is the condition nibble, and it is
  127. exactly the low byte of the `0F 8x rel16' this used to emit.
  128. That was a real fault and nothing in the build could see it: `0F' is a
  129. 386-and-later opcode prefix and the 8086 has none, so EVERY conditional
  130. branch in EVERY compiled program was an illegal instruction on the machine
  131. TP3 targets. FCML decoded it happily, because FCML's -m16 mode is 386 --
  132. and FCML is this project's independent disassembler, so the one tool that
  133. could have objected was the one guaranteed to agree. qemu-system-i386 has
  134. no 8086 model either; its lowest is 486. So the compile succeeded, the
  135. .COM linked, the layout checked, the golden held and all 30 fixtures ran to
  136. the right answers, all at once, with the bug in.
  137. TPSRC8 lays IF, WHILE and REPEAT out as
  138. MOV AL,brnchop ; MOV AH,#$03 ; CALL eword ; PUSH pc ; CALL ejump
  139. i.e. a SHORT Jcc of displacement 3, stepping over a 3-byte EJMP. That is
  140. the shape here too, and EmJmpNear already owns the displacement arithmetic
  141. and the patch slot, so it is three lines and there is no second copy of
  142. that rule.
  143. The one thing that is NOT the same as TPSRC8, and cost a round of "every
  144. conditional is inverted" (t09_if printed pos/nonpos/lt for a program that
  145. must print nonpos/pos/ge): TP3's brnchop is the branch taken when the
  146. condition is TRUE, and TP3 steps over the EJMP when it is taken. Here `cc'
  147. is the branch taken when the condition is FALSE -- IF's `EmJcc (84H)' is
  148. JZ, patched to the ELSE, so it must fire when the test failed. EmJcc jumps
  149. to the target, it does not step over it, so stepping over an EJMP and then
  150. falling into the destination is the wrong way round: the byte has to be
  151. JccShortInv, not JccShort. The control flow that comes out is identical to
  152. the `0F 8x' form this replaces; only which of the pair is spelled differs.
  153. The flags survive, and the FOR test needs them to: it emits CMP and then
  154. Jcc with nothing in between, so anything that wrote a flag here would
  155. break the loop. Jcc and EJMP both leave the flags alone. *)
  156. BEGIN
  157. Ebyte (JccShortInv (cc)) ; (* Jcc_s, taken when cc does NOT hold *)
  158. Ebyte (03H) ; (* rel8: step over the 3-byte EJMP *)
  159. RETURN EmJmpNear (target) (* target = 0 => forward, see above *)
  160. END EmJcc ;
  161. PROCEDURE ResolvePatches () ;
  162. VAR i : CARDINAL ;
  163. rel : CARDINAL ;
  164. BEGIN
  165. i := 0 ;
  166. WHILE i < nPatch DO
  167. IF NOT patches [i].filled THEN
  168. rel := (patches [i].target + 10000H - (patches [i].place + 2))
  169. MOD 10000H ;
  170. cbuf [patches [i].place] := VAL (BYTE, rel MOD 100H) ;
  171. cbuf [patches [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H) ;
  172. patches [i].filled := TRUE
  173. END ;
  174. INC (i)
  175. END
  176. END ResolvePatches ;
  177. (* 1-byte const emitters *)
  178. PROCEDURE EmMovAxi (imm : CARDINAL) ;
  179. BEGIN
  180. Ebyte (0B8H) ; Eword (imm)
  181. END EmMovAxi ;
  182. PROCEDURE EmMovCxi (imm : CARDINAL) ;
  183. BEGIN
  184. Ebyte (0B9H) ; Eword (imm)
  185. END EmMovCxi ;
  186. PROCEDURE EmMovBpSp () ;
  187. BEGIN
  188. Ebyte (8BH) ; Ebyte (0ECH)
  189. END EmMovBpSp ;
  190. PROCEDURE EmMovAh0 () ;
  191. BEGIN
  192. Ebyte (0B4H) ; Ebyte (0H)
  193. END EmMovAh0 ;
  194. PROCEDURE EmMovAxSp () ;
  195. (* Read the top of the stack into AX, leaving the stack unchanged.
  196. The obvious encoding, MOV AX,[SP], DOES NOT EXIST on the 8086. There is
  197. no encoding of [SP] as a memory operand: SIB bytes, which is how [ESP]
  198. would be written, did not exist until the 386, and mod=00 / rm=100 is
  199. [SI], not [SP]. The first version of this emitted 8B 44 24 00 - mod=01,
  200. rm=100, SIB=24h, disp8=0 - which is correct only on a 386 and above. Two
  201. of this project's oracles agree that it is wrong: fcml in 16-bit mode
  202. decodes it as MOV AX,[SI+0x24h], and so does qemu executing it, because
  203. qemu follows the CPU's rules for the encoding it is given rather than
  204. guessing. It was not the encoder's fault that the bytes were well formed;
  205. they were, and they read SI+24h.
  206. The observable effect was that CASE compiled to no branches at all: each
  207. label test loaded a garbage address, every comparison failed, and the
  208. program fell straight past the whole statement and exited without printing.
  209. A CASE fixture caught it. Nothing else could have - the encoding is
  210. valid, the size is right, and the byte-level checks have no way to know
  211. what register was meant.
  212. So: POP then PUSH the same value. Two bytes, no SIB, correct on every
  213. 8086, and observationally identical to peeking - the stack pointer ends
  214. where it started, holding the same value. *)
  215. BEGIN
  216. Ebyte (58H) ; (* POP AX *)
  217. Ebyte (50H) (* PUSH AX *)
  218. END EmMovAxSp ;
  219. PROCEDURE EmMovCxSp () ;
  220. (* The same, for CX - the FOR loop's bound, pushed by the FOR statement and
  221. re-read on every iteration. This one was emitting 8B 0C and nothing else,
  222. which is MOV CX,[SI] with the SIB slot missing: the *next* instruction was
  223. consumed as the SIB byte and the displacement. Same root cause, same fix,
  224. and it had not been noticed only because no FOR fixture is executed yet. *)
  225. BEGIN
  226. Ebyte (59H) ; (* POP CX *)
  227. Ebyte (51H) (* PUSH CX *)
  228. END EmMovCxSp ;
  229. PROCEDURE EmPushAx () ;
  230. BEGIN
  231. Ebyte (50H)
  232. END EmPushAx ;
  233. PROCEDURE EmPopCx () ;
  234. BEGIN
  235. Ebyte (59H)
  236. END EmPopCx ;
  237. PROCEDURE EmPopDx () ;
  238. BEGIN
  239. Ebyte (5AH)
  240. END EmPopDx ;
  241. PROCEDURE EmPopSi () ;
  242. BEGIN
  243. Ebyte (5EH) (* POP SI *)
  244. END EmPopSi ;
  245. PROCEDURE EmPopAx () ;
  246. BEGIN
  247. Ebyte (58H) (* POP AX *)
  248. END EmPopAx ;
  249. PROCEDURE EmPushBx () ;
  250. BEGIN Ebyte (53H) END EmPushBx ;
  251. PROCEDURE EmPushCx () ;
  252. BEGIN Ebyte (51H) END EmPushCx ;
  253. PROCEDURE EmPushDx () ;
  254. BEGIN Ebyte (52H) END EmPushDx ;
  255. PROCEDURE EmPopBx () ;
  256. BEGIN Ebyte (5BH) END EmPopBx ;
  257. PROCEDURE EmMovAxCx () ;
  258. BEGIN Ebyte (89H) ; Ebyte (0C8H) END EmMovAxCx ; (* MOV AX,CX *)
  259. PROCEDURE EmMovChAl () ;
  260. BEGIN Ebyte (88H) ; Ebyte (0C5H) END EmMovChAl ; (* MOV CH,AL *)
  261. PROCEDURE EmMovChImm (v : CARDINAL) ;
  262. BEGIN Ebyte (0B5H) ; Ebyte (VAL (BYTE, v MOD 256)) END EmMovChImm ;
  263. (* 91 = XCHG AX,CX, and NOT 93. LoadPair hands the operator the left
  264. operand in CX and the right in AX (in the original, and still the
  265. commonest, shape: it pushes the left, loads the right, then pops the
  266. left into CX), so the exchange is what puts LEFT in AX for the
  267. operation to act on.
  268. Without it, `a - b` computes `b - a`; with the wrong register, `a + b`
  269. computes `AX' + a` where AX' is whatever BX happened to hold.
  270. This emitted 93H = XCHG BX,AX for its entire life, which is the same class
  271. of mistake as MovSiBx = 89 DC in Runtime.mod: the right opcode, the wrong
  272. ModRM, decoding cleanly. Byte counts were right, the compile matrix was
  273. green, and no exec fixture did arithmetic on two variables - the first one
  274. to do so, `c := a + b` with a=7 b=5, printed 263 = 0100h+7, where 0100h
  275. was the caller's leftover BX. The name was the only thing wrong, and
  276. nothing read the name: audit_helpers.py swept Runtime.mod and not
  277. Compiler.mod, which is where most of these emitters live. It does both
  278. modules now. *)
  279. PROCEDURE EmXchgAxCx () ;
  280. BEGIN
  281. Ebyte (91H)
  282. END EmXchgAxCx ;
  283. PROCEDURE EmXorAxAx () ;
  284. BEGIN
  285. Ebyte (33H) ; Ebyte (0C0H)
  286. END EmXorAxAx ;
  287. PROCEDURE EmAddAxCx () ;
  288. BEGIN
  289. Ebyte (3H) ; Ebyte (0C1H)
  290. END EmAddAxCx ;
  291. PROCEDURE EmSubAxCx () ;
  292. BEGIN
  293. Ebyte (2BH) ; Ebyte (0C1H)
  294. END EmSubAxCx ;
  295. PROCEDURE EmMulAxCx () ;
  296. BEGIN
  297. Ebyte (0F7H) ; Ebyte (0E9H)
  298. END EmMulAxCx ;
  299. PROCEDURE EmIDivAxCx () ;
  300. BEGIN
  301. Ebyte (99H) ; Ebyte (0F7H) ; Ebyte (0F9H)
  302. END EmIDivAxCx ;
  303. PROCEDURE EmAndAxCx () ;
  304. BEGIN
  305. Ebyte (23H) ; Ebyte (0C1H)
  306. END EmAndAxCx ;
  307. PROCEDURE EmOrAxCx () ;
  308. BEGIN
  309. Ebyte (0BH) ; Ebyte (0C1H)
  310. END EmOrAxCx ;
  311. PROCEDURE EmOrAxAx () ;
  312. (* 09 C0: OR AX,AX - the 8086's flag-only test of AX, and the one the string
  313. comparison needs, because cmpstr hands back a signed -1/0/+1 rather than
  314. flags. OR AX,AX sets SF and ZF from the value and clears OF and CF, which
  315. is exactly the flag state a CMP AX,0 would leave - see EmSetcc and
  316. ParseCmp's EmRelSetcc. TEST AX,AX (85 C0) does the same thing, but
  317. tests/audit_helpers.py's name grammar has no `Test' mnemonic, and an
  318. emitter that cannot be name-checked is worse than one that can.
  319. It is NOT EmXorAxAx: XORing AX with itself zeroes it, which would throw
  320. away the very value the flags are supposed to describe. *)
  321. BEGIN
  322. Ebyte (09H) ; Ebyte (0C0H)
  323. END EmOrAxAx ;
  324. PROCEDURE EmNegAx () ;
  325. BEGIN
  326. Ebyte (0F7H) ; Ebyte (0D8H)
  327. END EmNegAx ;
  328. PROCEDURE EmNotAx () ;
  329. BEGIN
  330. Ebyte (0F7H) ; Ebyte (0D0H)
  331. END EmNotAx ;
  332. PROCEDURE EmXorAl01 () ;
  333. (* XOR AL,#01 - TPSRC9 neglevel's *boolean* NOT:
  334. CALL loadatom ; CALL ecode ; B $02,$34,$01
  335. neglevel tests the type first (CMP CL,#$0A -> integer -> NOT AX,
  336. CMP CL,#$0B -> boolean -> XOR AL,#01) and reports error 47 for anything
  337. else. Emitting NOT AX for a boolean leaves 0FFFEh or 0FFFFh, and the
  338. runtime's wrbool tests [BP+4] <> 0, so `not (a = a)' came out TRUE. *)
  339. BEGIN
  340. Ebyte (34H) ; Ebyte (01H)
  341. END EmXorAl01 ;
  342. PROCEDURE EmCmpAxCx () ;
  343. BEGIN
  344. Ebyte (3BH) ; Ebyte (0C1H)
  345. END EmCmpAxCx ;
  346. PROCEDURE EmCmpAxi (imm : CARDINAL) ;
  347. BEGIN
  348. Ebyte (03DH) ; Eword (imm)
  349. END EmCmpAxi ;
  350. PROCEDURE EmSetcc (cc : BYTE) ;
  351. (* Flags -> a Boolean in AX, on an 8086. `cc' is the SETcc opcode's low byte
  352. (94H = E, 95H = NE, 9CH = L, 9DH = GE, 9EH = LE, 9FH = G), i.e. the same
  353. condition nibble EmJcc takes.
  354. This used to emit `0F cc C0' - SETcc - which is 386-and-later, and then a
  355. MOV AH,0. The 8086 cannot read its flags as a value at all, so there was
  356. nothing else to fall back on.
  357. TPSRC9's flgbool is the fallback, and emits exactly this for exactly this
  358. case (CH = 04h, a comparison whose result is wanted as a value rather than
  359. as a branch):
  360. CALL ecode ; B $03,$B8,$01,$00 -> MOV AX,#0001
  361. MOV AL,brnchop ; CALL ebyte -> JNZ +1
  362. CALL ecode ; B $02,$01,$48 -> DEC AX
  363. AX stays 1 because the DEC was stepped over, and becomes 0 because it ran.
  364. So the shape is one MOV, one short Jcc whose displacement is the length of
  365. the DEC, and the DEC - which is EmJcc's shape with a different displacement,
  366. and the reason both are two instructions and a byte.
  367. The polarity is the opposite of EmJcc's, and deliberately so: here the jump
  368. must be taken when the comparison is TRUE, because what is being asked is
  369. "is this comparison true", and the nibble the six ParseCmp arms pass is the
  370. comparison's own opcode. So this is JccShort and EmJcc is JccShortInv --
  371. see EmJcc for why the difference is there at all. TP3's flgbool writes JNZ
  372. for the same reason; in the one case IT reaches flgbool from, the boolean is
  373. sitting in AX rather than in the flags, so JNZ is how it says "AX is
  374. non-zero".
  375. AH comes out 0 for free, which is why the EmMovAh0 this used to end with is
  376. gone: 6 bytes here where the old sequence was 5. The FLAGS do not survive,
  377. which the old SETcc did - and nothing reads them. Every conditional branch
  378. in the compiler is preceded by its own CMP (see EmJcc's note on the FOR
  379. test), and a comparison's value is consumed either as an AX operand or by
  380. the test that follows it; the 30 executed fixtures are what holds that
  381. down, not this comment. *)
  382. BEGIN
  383. Ebyte (0B8H) ; Eword (1) ; (* MOV AX,#0001 *)
  384. Ebyte (JccShort (cc)) ; (* taken when the comparison HOLDS *)
  385. Ebyte (01H) ; (* rel8: step over the DEC AX *)
  386. Ebyte (48H) (* DEC AX *)
  387. END EmSetcc ;
  388. PROCEDURE EmRelSetcc (op : CARDINAL) ;
  389. (* The relational operator -> SETcc table, in ONE place, because there are now
  390. two callers: ParseCmp's numeric arm, whose flags come from EmCmpAxCx, and
  391. its string arm, whose flags come from a cmpstr result put through
  392. EmOrAxAx. It was factored out of ParseCmp when the string arm arrived, and
  393. the note it carried is repeated here rather than referenced, because a
  394. comment in the caller about a table in another routine is a comment nobody
  395. reads.
  396. The mnemonic is written next to every opcode on purpose. `op' is a number,
  397. so the arm for ">" and the arm for ">=" differed only by two hex digits
  398. that are each one letter from the other meaning -- SETG (9FH) and SETGE
  399. (9DH). They were swapped once, which made a > b mean a >= b and a >= b
  400. mean a > b. Only the equality boundary could see it: 6>5, 5<5, -1>-2 and
  401. every other case I tried were already right. An opcode on its own does not
  402. say which comparison it is the answer to.
  403. `op' is ParseCmp's own numbering: 1 "=", 2 "<>", 3 "<", 4 ">", 5 ">=",
  404. 6 "<=". The condition nibbles are the SIGNED ones (L/G/GE/LE), and both
  405. callers hand it a value whose SIGN is the answer - ParseCmp's numeric arm
  406. compares with CMP AX,CX on signed data, and cmpstr answers -1/0/+1 - so
  407. the signed test is the right one in both places. One table also means that
  408. swap cannot come back in one arm and not the other: tests/nonvacuity.sh
  409. mutates exactly these two lines, and it now breaks the string comparison
  410. at the same time as the numeric one. *)
  411. BEGIN
  412. CASE op OF
  413. 1 : EmSetcc (94H) ; (* = SETE *)
  414. | 2 : EmSetcc (95H) ; (* <> SETNE *)
  415. | 3 : EmSetcc (9CH) ; (* < SETL *)
  416. | 4 : EmSetcc (9FH) ; (* > SETG *)
  417. | 5 : EmSetcc (9DH) ; (* >= SETGE *)
  418. | 6 : EmSetcc (9EH) (* <= SETLE *)
  419. END
  420. END EmRelSetcc ;
  421. PROCEDURE EmIncAx () ;
  422. BEGIN
  423. Ebyte (40H)
  424. END EmIncAx ;
  425. PROCEDURE EmDecAx () ;
  426. BEGIN
  427. Ebyte (48H)
  428. END EmDecAx ;
  429. PROCEDURE EmBpDisp (off, regf : CARDINAL) ;
  430. (* Emit the ModR/M byte and displacement for a [BP+off] operand, picking the
  431. encoding from the size of off. This is the ONE place that choice is made,
  432. because getting it wrong is invisible: 8B 46 d8 and 8B 86 lo hi are both
  433. well-formed MOVs, both decode cleanly, and only one of them reads the
  434. variable the symbol table names. So the two are chosen here, once, rather
  435. than re-derived at each of the four call sites. See the ModR/M table in
  436. Runtime.mod.
  437. off <= 127 -> mod=01 rm=110 -> 46 <disp8> 3 bytes with the opcode
  438. otherwise -> mod=10 rm=110 -> 86 <disp16> 4 bytes with the opcode
  439. Both displacements are SIGNED, and that is the whole subtlety:
  440. - `off` is a 16-bit value and locals are packed top-anchored below BP:
  441. locFree is the highest still-free byte (0FFFFh = -1 at entry), a local of
  442. size n is based so its LAST byte sits on locFree, and locFree then drops
  443. by n. So the first INTEGER local is at off = 0FFFEh = -2, the second at
  444. 0FFFCh = -4, and so on - and a string local, which grows UP from its
  445. base, lands wholly below BP instead of straddling it. The disp16 form
  446. reads those same two bytes as a signed value and addresses the negative
  447. slot correctly. There is no overflow case: all 65536 values of `off`
  448. are representable, and a frame larger than 64K is a different problem.
  449. - The old code took `off MOD 100H` and always emitted disp8. That is
  450. the correct low byte for every displacement, so it was accidentally
  451. right across -32768..+127, which is where locals actually live. It
  452. went wrong at +128, where disp8 80h is -128 and not +128. So this
  453. changes no existing program's bytes and fixes the one case that was
  454. broken -- a bug nobody had hit yet, which is exactly why it wanted a
  455. test rather than an argument. *)
  456. BEGIN
  457. (* mod=01 rm=110 -> 40H + regf*8 + 6H, mod=10 rm=110 -> 80H + regf*8 + 6H.
  458. regf is the ModR/M reg field: 0 when the opcode's register is the
  459. accumulator (every original caller), 7 for the DI forms EmLeaDi needs.
  460. rm is 110 ([BP+disp]) in both. *)
  461. IF off <= 127 THEN
  462. Ebyte (VAL (BYTE, 40H + regf * 8 + 6H)) ; Ebyte (VAL (BYTE, off))
  463. ELSE
  464. Ebyte (VAL (BYTE, 80H + regf * 8 + 6H)) ; Eword (off)
  465. END
  466. END EmBpDisp ;
  467. PROCEDURE EmBxDisp (off, regf : CARDINAL) ;
  468. (* The [BX+disp] twin of EmBpDisp. A nested procedure reaches an enclosing
  469. frame through the static link in BX; the displacement rules are identical
  470. except for the rm field (111 for BX instead of 110 for BP). *)
  471. BEGIN
  472. IF off <= 127 THEN
  473. Ebyte (VAL (BYTE, 40H + regf * 8 + 7H)) ; Ebyte (VAL (BYTE, off))
  474. ELSE
  475. Ebyte (VAL (BYTE, 80H + regf * 8 + 7H)) ; Eword (off)
  476. END
  477. END EmBxDisp ;
  478. PROCEDURE EmMovBxBp () ;
  479. BEGIN Ebyte (89H) ; Ebyte (0EBH) END EmMovBxBp ; (* MOV BX,BP *)
  480. PROCEDURE EmMovDxBp () ;
  481. BEGIN Ebyte (89H) ; Ebyte (0EAH) END EmMovDxBp ; (* MOV DX,BP *)
  482. PROCEDURE EmMovDxBx () ;
  483. BEGIN Ebyte (89H) ; Ebyte (0DAH) END EmMovDxBx ; (* MOV DX,BX *)
  484. PROCEDURE EmMovDxAx () ;
  485. BEGIN Ebyte (89H) ; Ebyte (0C2H) END EmMovDxAx ; (* DX:=AX *)
  486. PROCEDURE EmMovAxDx () ;
  487. BEGIN Ebyte (89H) ; Ebyte (0D0H) END EmMovAxDx ; (* MOV AX,DX *)
  488. PROCEDURE EmLdBxBx (off : CARDINAL) ;
  489. (* BX := [BX+off] - one step up the static-link chain. *)
  490. BEGIN Ebyte (8BH) ; EmBxDisp (off, 3) END EmLdBxBx ;
  491. PROCEDURE EmLoadVarBx (off, nbytes : CARDINAL) ;
  492. BEGIN
  493. IF nbytes = 1 THEN
  494. Ebyte (8AH) ; EmBxDisp (off, 0)
  495. ELSE
  496. Ebyte (8BH) ; EmBxDisp (off, 0)
  497. END
  498. END EmLoadVarBx ;
  499. PROCEDURE EmStoreVarBx (off, nbytes : CARDINAL) ;
  500. BEGIN
  501. IF nbytes = 1 THEN
  502. Ebyte (88H) ; EmBxDisp (off, 0)
  503. ELSE
  504. Ebyte (89H) ; EmBxDisp (off, 0)
  505. END
  506. END EmStoreVarBx ;
  507. PROCEDURE EmLeaDiBx (off : CARDINAL) ;
  508. BEGIN Ebyte (8DH) ; EmBxDisp (off, 7) END EmLeaDiBx ;
  509. END CompilerEmit.