Exec86.mod 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. IMPLEMENTATION MODULE Exec86 ;
  2. (* Exec86 -- implementation. See Exec86.def for what this is for.
  3. WHAT IS IMPLEMENTED, and where the list comes from
  4. --------------------------------------------------
  5. Not "the 8086": a CPU has no meaning without the programs it must run, and
  6. this one has a corpus. The set below is the union of exactly two measured
  7. things:
  8. (a) every byte the runtime emits. Runtime.mod's code region is pure
  9. code with no inline data, so it can be swept instruction by
  10. instruction and the sweep must complete -- tests/check_8086.py does
  11. that, and it covers 219 instructions, 121 distinct forms. That
  12. sweep is the authority for the runtime.
  13. (b) every byte Compiler.mod's emitters can write. The generated code
  14. region CANNOT be swept the same way: inline string literals are
  15. emitted into it after the code, and a linear sweep desynchronises
  16. on them (t09_if dies at the bytes `FE E9 0D 00', which are ASCII
  17. text). So the authority there is the source -- the Em* procedures
  18. and their Ebyte constants -- not a disassembly.
  19. Everything outside that union faults rather than guessing. An
  20. interpreter that guesses at an unknown opcode does not fail; it produces
  21. an answer.
  22. FLAGS: CF, ZF, SF and OF are maintained, because every condition the
  23. corpus uses reads one of them: the compiler emits 4,5,C,D,E,F
  24. (JE JNE JL JGE JLE JG) and the runtime adds 2,6,7 (JB JBE JA).
  25. PF and AF are NOT maintained, and JP/JNP fault saying so rather than
  26. returning a value nobody computed. DF, IF and TF are not maintained;
  27. no string instruction is implemented, so nothing can read DF. Nothing in
  28. the corpus sets a flag that a later instruction in the corpus reads
  29. except through those four.
  30. SEGMENTS are checked every step. The machine is 64 KB flat, so a
  31. non-zero segment register would alias silently rather than fault; it
  32. faults instead.
  33. INT 21h PRESERVES THE FLAGS. That is not an approximation: a real INT
  34. pushes FLAGS, and IRET pops them back, so the handler's own CLC/STC are
  35. discarded before the caller can see them. bootcom.s does `clc' and
  36. `stc' in its handler and they change nothing outside it. This is the
  37. one place where matching the machine rather than the visible source is
  38. easy to get wrong, so it is written down.
  39. INT 10h is also provided, for the CRT builtins the compiler emits. There is
  40. no display model: the cursor is tracked (AH=02/AH=03) and the screen calls
  41. are no-ops, so the CONSOLE output this interpreter captures - which is what
  42. the execution oracles compare - is identical to qemu's. See DoInt10. *)
  43. FROM Posix IMPORT read, write ;
  44. FROM SYSTEM IMPORT ADR ;
  45. CONST
  46. STDIN = 0 ;
  47. STDOUT = 1 ;
  48. STDERR = 2 ;
  49. LoadAt = 100H ; (* where DOS puts a .COM, and where bootcom
  50. jumps, so the entry point matches qemu *)
  51. MaxSteps = 2000000000 ; (* runaway guard; see Exec86.def *)
  52. VAR
  53. mem : ARRAY [0..65535] OF CARDINAL ; (* one CARDINAL per byte, 0..255 *)
  54. AX, BX, CX, DX, SI, DI, BP, SP, IP : CARDINAL ;
  55. CS, DS, ES, SS : CARDINAL ;
  56. CF, ZF, SF, OFl : BOOLEAN ;
  57. halted : BOOLEAN ;
  58. haltCode : CARDINAL ;
  59. isFault : BOOLEAN ;
  60. faultIP : CARDINAL ;
  61. loadHi : CARDINAL ; (* one past the highest address poked *)
  62. stepCnt : LONGCARD ;
  63. curRow, curCol : CARDINAL ; (* INT 10h's cursor, for AH=02/AH=03 *)
  64. peeked : BOOLEAN ; (* AH=0Bh looked one byte ahead; keep it for
  65. AH=08h rather than losing it *)
  66. peekCh : CHAR ;
  67. (* filled in by DoModRM *)
  68. eaAddr : CARDINAL ;
  69. eaReg : CARDINAL ;
  70. eaIsReg : BOOLEAN ;
  71. rmReg : CARDINAL ;
  72. (* ------------------------------------------------------------ diagnostics *)
  73. PROCEDURE WrErr1 (c : CHAR) ;
  74. VAR n : LONGINT ;
  75. BEGIN
  76. n := write (STDERR, ADR (c), 1)
  77. END WrErr1 ;
  78. PROCEDURE WrS (s : ARRAY OF CHAR) ;
  79. VAR i : CARDINAL ;
  80. c : CHAR ;
  81. BEGIN
  82. i := 0 ;
  83. WHILE (i <= HIGH (s)) AND (s [i] # 0C) DO
  84. c := s [i] ;
  85. WrErr1 (c) ;
  86. i := i + 1
  87. END
  88. END WrS ;
  89. PROCEDURE WrHex (v : CARDINAL) ;
  90. VAR k, d, p, n : CARDINAL ;
  91. c : CHAR ;
  92. BEGIN
  93. WrS ("0x") ;
  94. FOR k := 0 TO 3 DO
  95. p := 1 ;
  96. d := 3 - k ;
  97. WHILE d > 0 DO
  98. p := p * 16 ;
  99. d := d - 1
  100. END ;
  101. n := (v DIV p) MOD 16 ;
  102. IF n < 10 THEN
  103. c := CHR (ORD ('0') + n)
  104. ELSE
  105. c := CHR (ORD ('A') + n - 10)
  106. END ;
  107. WrErr1 (c)
  108. END
  109. END WrHex ;
  110. PROCEDURE WrDec (v : CARDINAL) ;
  111. VAR buf : ARRAY [0..10] OF CHAR ;
  112. k, j, x : CARDINAL ;
  113. c : CHAR ;
  114. BEGIN
  115. k := 10 ;
  116. buf [10] := 0C ;
  117. x := v ;
  118. REPEAT
  119. k := k - 1 ;
  120. buf [k] := CHR (ORD ('0') + x MOD 10) ;
  121. x := x DIV 10
  122. UNTIL x = 0 ;
  123. j := k ;
  124. WHILE j <= 9 DO
  125. c := buf [j] ;
  126. WrErr1 (c) ;
  127. j := j + 1
  128. END
  129. END WrDec ;
  130. PROCEDURE Fault (msg : ARRAY OF CHAR) ;
  131. BEGIN
  132. isFault := TRUE ;
  133. WrS ("exec86: fault at IP=") ;
  134. WrHex (faultIP) ;
  135. WrS (": ") ;
  136. WrS (msg) ;
  137. WrErr1 (CHR (10))
  138. END Fault ;
  139. (* --------------------------------------------------- widths and sign bits *)
  140. PROCEDURE Sz (w : CARDINAL) : CARDINAL ;
  141. BEGIN
  142. IF w = 1 THEN RETURN 65536 ELSE RETURN 256 END
  143. END Sz ;
  144. (* The value with only the sign bit set, for a byte (80H) or word (8000H). *)
  145. PROCEDURE Sg (w : CARDINAL) : CARDINAL ;
  146. BEGIN
  147. IF w = 1 THEN RETURN 8000H ELSE RETURN 80H END
  148. END Sg ;
  149. (* A signed byte widened to a 16-bit two's-complement CARDINAL, so that
  150. adding it does the right thing modulo 65536. 128 -> 65408 = -128. *)
  151. PROCEDURE SE8 (v : CARDINAL) : CARDINAL ;
  152. BEGIN
  153. IF v >= 80H THEN RETURN v + 65280 ELSE RETURN v END
  154. END SE8 ;
  155. (* Signed interpretations, for MUL/IMUL/DIV/IDIV. *)
  156. PROCEDURE S16 (v : CARDINAL) : LONGINT ;
  157. BEGIN
  158. IF v >= 8000H THEN
  159. RETURN VAL (LONGINT, v) - VAL (LONGINT, 65536)
  160. ELSE
  161. RETURN VAL (LONGINT, v)
  162. END
  163. END S16 ;
  164. PROCEDURE S8 (v : CARDINAL) : LONGINT ;
  165. BEGIN
  166. IF v >= 80H THEN
  167. RETURN VAL (LONGINT, v) - VAL (LONGINT, 256)
  168. ELSE
  169. RETURN VAL (LONGINT, v)
  170. END
  171. END S8 ;
  172. (* --------------------------------------------------------- memory and stack *)
  173. PROCEDURE MemWr (a, v, w : CARDINAL) ;
  174. BEGIN
  175. mem [a] := v MOD 256 ;
  176. IF w = 1 THEN
  177. mem [(a + 1) MOD 65536] := (v DIV 256) MOD 256
  178. END
  179. END MemWr ;
  180. PROCEDURE MemRd (a, w : CARDINAL) : CARDINAL ;
  181. BEGIN
  182. IF w = 1 THEN
  183. RETURN mem [a] + 256 * mem [(a + 1) MOD 65536]
  184. ELSE
  185. RETURN mem [a]
  186. END
  187. END MemRd ;
  188. PROCEDURE Push (v : CARDINAL) ;
  189. BEGIN
  190. SP := (SP + 65534) MOD 65536 ; (* SP - 2, wrapped *)
  191. MemWr (SP, v, 1)
  192. END Push ;
  193. PROCEDURE Pop () : CARDINAL ;
  194. VAR v : CARDINAL ;
  195. BEGIN
  196. v := MemRd (SP, 1) ;
  197. SP := (SP + 2) MOD 65536 ;
  198. RETURN v
  199. END Pop ;
  200. PROCEDURE Fetch8 () : CARDINAL ;
  201. VAR v : CARDINAL ;
  202. BEGIN
  203. v := mem [IP] ;
  204. IP := (IP + 1) MOD 65536 ;
  205. RETURN v
  206. END Fetch8 ;
  207. PROCEDURE Fetch16 () : CARDINAL ;
  208. VAR a, b : CARDINAL ;
  209. BEGIN
  210. a := Fetch8 () ;
  211. b := Fetch8 () ;
  212. RETURN a + 256 * b
  213. END Fetch16 ;
  214. (* --------------------------------------------------------------- registers *)
  215. PROCEDURE GetReg (r, w : CARDINAL) : CARDINAL ;
  216. BEGIN
  217. IF w = 1 THEN
  218. CASE r OF
  219. | 0 : RETURN AX
  220. | 1 : RETURN CX
  221. | 2 : RETURN DX
  222. | 3 : RETURN BX
  223. | 4 : RETURN SP
  224. | 5 : RETURN BP
  225. | 6 : RETURN SI
  226. ELSE RETURN DI
  227. END
  228. ELSE
  229. CASE r OF
  230. | 0 : RETURN AX MOD 256 (* AL *)
  231. | 1 : RETURN CX MOD 256 (* CL *)
  232. | 2 : RETURN DX MOD 256 (* DL *)
  233. | 3 : RETURN BX MOD 256 (* BL *)
  234. | 4 : RETURN AX DIV 256 (* AH *)
  235. | 5 : RETURN CX DIV 256 (* CH *)
  236. | 6 : RETURN DX DIV 256 (* DH *)
  237. ELSE RETURN BX DIV 256 (* BH *)
  238. END
  239. END
  240. END GetReg ;
  241. PROCEDURE PutReg (r, w, v : CARDINAL) ;
  242. VAR hi, lo : CARDINAL ;
  243. BEGIN
  244. IF w = 1 THEN
  245. CASE r OF
  246. | 0 : AX := v
  247. | 1 : CX := v
  248. | 2 : DX := v
  249. | 3 : BX := v
  250. | 4 : SP := v
  251. | 5 : BP := v
  252. | 6 : SI := v
  253. ELSE DI := v
  254. END
  255. ELSE
  256. lo := v MOD 256 ;
  257. CASE r OF
  258. | 0 : AX := (AX DIV 256) * 256 + lo (* AL *)
  259. | 1 : CX := (CX DIV 256) * 256 + lo (* CL *)
  260. | 2 : DX := (DX DIV 256) * 256 + lo (* DL *)
  261. | 3 : BX := (BX DIV 256) * 256 + lo (* BL *)
  262. | 4 : hi := lo * 256 ; AX := (AX MOD 256) + hi (* AH *)
  263. | 5 : hi := lo * 256 ; CX := (CX MOD 256) + hi (* CH *)
  264. | 6 : hi := lo * 256 ; DX := (DX MOD 256) + hi (* DH *)
  265. ELSE hi := lo * 256 ; BX := (BX MOD 256) + hi (* BH *)
  266. END
  267. END
  268. END PutReg ;
  269. (* ---------------------------------------------------------- addressing mode *)
  270. PROCEDURE DoModRM (w : CARDINAL) ;
  271. (* Fetch the ModR/M byte and, if the operand is in memory, its displacement.
  272. `w' is only needed so the caller can size the access afterwards; the
  273. address is the same either way.
  274. The 16-bit addressing modes, since this is the part where a wrong table
  275. still decodes cleanly and just reads the wrong variable:
  276. rm mod=00 mod=01/10
  277. 0 [BX+SI] [BX+SI+disp]
  278. 1 [BX+DI] [BX+DI+disp]
  279. 2 [BP+SI] [BP+SI+disp]
  280. 3 [BP+DI] [BP+DI+disp]
  281. 4 [SI] [SI+disp]
  282. 5 [DI] [DI+disp]
  283. 6 [disp16] [BP+disp] <- mod=00,rm=6 is the ONLY direct form
  284. 7 [BX] [BX+disp]
  285. Displacements are signed, and adding them modulo 65536 is exactly two's
  286. complement addition, so no branch is needed for a negative one. SE8
  287. handles the disp8 case by widening it first. *)
  288. VAR b, md, rg, rm, base, d : CARDINAL ;
  289. BEGIN
  290. b := Fetch8 () ;
  291. md := b DIV 64 ;
  292. rg := (b DIV 8) MOD 8 ;
  293. rm := b MOD 8 ;
  294. rmReg := rg ;
  295. IF md = 3 THEN
  296. eaIsReg := TRUE ;
  297. eaReg := rm ;
  298. eaAddr := 0
  299. ELSE
  300. eaIsReg := FALSE ;
  301. eaReg := 0 ;
  302. CASE rm OF
  303. | 0 : base := (BX + SI) MOD 65536
  304. | 1 : base := (BX + DI) MOD 65536
  305. | 2 : base := (BP + SI) MOD 65536
  306. | 3 : base := (BP + DI) MOD 65536
  307. | 4 : base := SI
  308. | 5 : base := DI
  309. | 6 : base := BP
  310. ELSE base := BX
  311. END ;
  312. IF (md = 0) AND (rm = 6) THEN
  313. eaAddr := Fetch16 ()
  314. ELSIF md = 0 THEN
  315. eaAddr := base
  316. ELSIF md = 1 THEN
  317. d := Fetch8 () ;
  318. eaAddr := (base + SE8 (d)) MOD 65536
  319. ELSE
  320. d := Fetch16 () ;
  321. eaAddr := (base + d) MOD 65536
  322. END
  323. END
  324. END DoModRM ;
  325. PROCEDURE RmRd (w : CARDINAL) : CARDINAL ;
  326. BEGIN
  327. IF eaIsReg THEN
  328. RETURN GetReg (eaReg, w)
  329. ELSE
  330. RETURN MemRd (eaAddr, w)
  331. END
  332. END RmRd ;
  333. PROCEDURE RmWr (v, w : CARDINAL) ;
  334. BEGIN
  335. IF eaIsReg THEN
  336. PutReg (eaReg, w, v)
  337. ELSE
  338. MemWr (eaAddr, v, w)
  339. END
  340. END RmWr ;
  341. (* ------------------------------------------------------------------- flags *)
  342. PROCEDURE SetZSF (r, w : CARDINAL) ;
  343. (* ZF and SF from the result. PF is deliberately absent: see the header. *)
  344. BEGIN
  345. ZF := r = 0 ;
  346. SF := r >= Sg (w)
  347. END SetZSF ;
  348. PROCEDURE DoAdd (a, b, cin, w : CARDINAL) : CARDINAL ;
  349. (* a + b + cin, setting the flags. raw is at most 65535+65535+1, so it
  350. never overflows the 32-bit CARDINAL and the carry can be read off it
  351. directly rather than inferred from a wrapped result. *)
  352. VAR raw, res, sz : CARDINAL ;
  353. ex : LONGINT ;
  354. BEGIN
  355. sz := Sz (w) ;
  356. raw := a + b + cin ;
  357. res := raw MOD sz ;
  358. CF := raw >= sz ;
  359. (* OF: the exact mathematical result left the signed range. Computing it
  360. exactly, rather than from the signs of the two operands, is what makes
  361. ADC work too: the carry can turn 127+0 into 128, and the two sign bits
  362. alone say nothing about that. S16/S8 are the signed readings of a
  363. value at this width, so the whole test is one addition and a range
  364. check. *)
  365. IF w = 1 THEN
  366. ex := S16 (a) + S16 (b) + VAL (LONGINT, cin) ;
  367. OFl := (ex < VAL (LONGINT, -32768)) OR (ex > VAL (LONGINT, 32767))
  368. ELSE
  369. ex := S8 (a) + S8 (b) + VAL (LONGINT, cin) ;
  370. OFl := (ex < VAL (LONGINT, -128)) OR (ex > VAL (LONGINT, 127))
  371. END ;
  372. SetZSF (res, w) ;
  373. RETURN res
  374. END DoAdd ;
  375. PROCEDURE DoSub (a, b, cin, w : CARDINAL) : CARDINAL ;
  376. (* Subtracts b and a borrow. Same reason for computing OF exactly: with
  377. cin = 1 and b = 127 the subtrahend is really 128, whose sign bit is not
  378. the sign bit of b, so the sign-bit rule gets SBB wrong. *)
  379. VAR sz, res, bb : CARDINAL ;
  380. ex : LONGINT ;
  381. BEGIN
  382. sz := Sz (w) ;
  383. bb := b + cin ;
  384. IF a >= bb THEN
  385. res := a - bb ; (* CARDINAL is 32-bit, so this cannot wrap *)
  386. CF := FALSE
  387. ELSE
  388. res := a + sz - bb ; (* a + sz >= bb always, so still non-negative *)
  389. CF := TRUE
  390. END ;
  391. res := res MOD sz ;
  392. IF w = 1 THEN
  393. ex := S16 (a) - S16 (b) - VAL (LONGINT, cin) ;
  394. OFl := (ex < VAL (LONGINT, -32768)) OR (ex > VAL (LONGINT, 32767))
  395. ELSE
  396. ex := S8 (a) - S8 (b) - VAL (LONGINT, cin) ;
  397. OFl := (ex < VAL (LONGINT, -128)) OR (ex > VAL (LONGINT, 127))
  398. END ;
  399. SetZSF (res, w) ;
  400. RETURN res
  401. END DoSub ;
  402. PROCEDURE SetLogic (r, w : CARDINAL) ;
  403. BEGIN
  404. CF := FALSE ;
  405. OFl := FALSE ;
  406. SetZSF (r, w)
  407. END SetLogic ;
  408. (* Bitwise operations. This dialect has NO bitwise operators at all - no
  409. BITAND, no BAND, no `&' - so sets stand in for them, which is the same
  410. trick Compiler.mod uses for its own constant folding (BitAnd/BitOr/BitNot
  411. there are three lines of this). `+' is union, `*' is intersection,
  412. `-` is difference, so XOR is union minus intersection. *)
  413. PROCEDURE BAnd (a, b, w : CARDINAL) : CARDINAL ;
  414. BEGIN
  415. IF w = 1 THEN
  416. RETURN CARDINAL (VAL (BITSET, a) * VAL (BITSET, b))
  417. ELSE
  418. RETURN CARDINAL (VAL (BITSET, a MOD 256) * VAL (BITSET, b MOD 256))
  419. END
  420. END BAnd ;
  421. PROCEDURE BOr (a, b, w : CARDINAL) : CARDINAL ;
  422. BEGIN
  423. IF w = 1 THEN
  424. RETURN CARDINAL (VAL (BITSET, a) + VAL (BITSET, b))
  425. ELSE
  426. RETURN CARDINAL (VAL (BITSET, a MOD 256) + VAL (BITSET, b MOD 256))
  427. END
  428. END BOr ;
  429. PROCEDURE BXor (a, b, w : CARDINAL) : CARDINAL ;
  430. VAR u, i : BITSET ;
  431. BEGIN
  432. IF w = 1 THEN
  433. u := VAL (BITSET, a) + VAL (BITSET, b) ;
  434. i := VAL (BITSET, a) * VAL (BITSET, b)
  435. ELSE
  436. u := VAL (BITSET, a MOD 256) + VAL (BITSET, b MOD 256) ;
  437. i := VAL (BITSET, a MOD 256) * VAL (BITSET, b MOD 256)
  438. END ;
  439. RETURN CARDINAL (u - i)
  440. END BXor ;
  441. PROCEDURE BNot (a, w : CARDINAL) : CARDINAL ;
  442. BEGIN
  443. IF w = 1 THEN
  444. RETURN CARDINAL (VAL (BITSET, 0FFFFH) - VAL (BITSET, a))
  445. ELSE
  446. RETURN CARDINAL (VAL (BITSET, 0FFH) - VAL (BITSET, a MOD 256))
  447. END
  448. END BNot ;
  449. PROCEDURE Alu (op, a, b, w : CARDINAL) : CARDINAL ;
  450. (* op is the group's reg field: 0 ADD 1 OR 2 ADC 3 SBB 4 AND 5 SUB 6 XOR
  451. 7 CMP. The result is returned for every op, including CMP; the caller
  452. decides whether to write it back, which is what the /r bit of the
  453. instruction already says. *)
  454. VAR cin, res : CARDINAL ;
  455. BEGIN
  456. IF (op = 2) OR (op = 3) THEN
  457. IF CF THEN cin := 1 ELSE cin := 0 END
  458. ELSE
  459. cin := 0
  460. END ;
  461. CASE op OF
  462. | 0 : res := DoAdd (a, b, cin, w)
  463. | 1 : res := BOr (a, b, w) ; SetLogic (res, w)
  464. | 2 : res := DoAdd (a, b, cin, w)
  465. | 3 : res := DoSub (a, b, cin, w)
  466. | 4 : res := BAnd (a, b, w) ; SetLogic (res, w)
  467. | 5 : res := DoSub (a, b, cin, w)
  468. | 6 : res := BXor (a, b, w) ; SetLogic (res, w)
  469. ELSE res := DoSub (a, b, cin, w)
  470. END ;
  471. RETURN res
  472. END Alu ;
  473. PROCEDURE Cond (n : CARDINAL) : BOOLEAN ;
  474. (* The 16 conditions. 0AH and 0BH need PF, which this machine does not
  475. maintain, so they fault instead of answering. *)
  476. VAR r : BOOLEAN ;
  477. BEGIN
  478. CASE n OF
  479. | 0H : r := OFl (* JO *)
  480. | 1H : r := NOT OFl (* JNO *)
  481. | 2H : r := CF (* JB *)
  482. | 3H : r := NOT CF (* JAE *)
  483. | 4H : r := ZF (* JE *)
  484. | 5H : r := NOT ZF (* JNE *)
  485. | 6H : r := CF OR ZF (* JBE *)
  486. | 7H : r := NOT (CF OR ZF) (* JA *)
  487. | 8H : r := SF (* JS *)
  488. | 9H : r := NOT SF (* JNS *)
  489. | 0AH : Fault ("parity flag is not maintained, so JP cannot be evaluated")
  490. ; r := FALSE
  491. | 0BH : Fault ("parity flag is not maintained, so JNP cannot be evaluated")
  492. ; r := FALSE
  493. | 0CH : r := SF # OFl (* JL *)
  494. | 0DH : r := SF = OFl (* JGE *)
  495. | 0EH : r := ZF OR (SF # OFl) (* JLE *)
  496. ELSE r := (NOT ZF) AND (SF = OFl) (* JG *)
  497. END ;
  498. RETURN r
  499. END Cond ;
  500. (* ----------------------------------------------------------------- INT 10h *)
  501. PROCEDURE DoInt10 ;
  502. (* The BIOS screen services this compiler emits, and nothing else. There is no
  503. display model here: the CURSOR is tracked so AH=03 can answer it, and the
  504. screen operations (AH=06 clear/scroll, AH=00 set mode) are no-ops that home
  505. the cursor. That is the honest shape, not a convenience: the interpreter
  506. captures the guest's CONSOLE output through INT 21h, which is what
  507. run_com_exec compares, and the BIOS writes to VGA memory, which no oracle
  508. here looks at. So a program that clears the screen and then prints agrees
  509. with qemu byte for byte. Anything else faults, as with INT 21h: inventing a
  510. video BIOS would be worse than saying it is not there. *)
  511. VAR ah : CARDINAL ;
  512. BEGIN
  513. ah := AX DIV 256 ;
  514. IF ah = 02H THEN (* set cursor: DH=row, DL=col *)
  515. curRow := DX DIV 256 ;
  516. curCol := DX MOD 256
  517. ELSIF ah = 03H THEN (* get cursor -> DH,DL *)
  518. DX := ((curRow MOD 256) * 256 + (curCol MOD 256)) MOD 65536
  519. ELSIF (ah = 06H) OR (ah = 00H) THEN
  520. (* clear/scroll or set mode: no display to change; the cursor homes *)
  521. curRow := 0 ;
  522. curCol := 0
  523. ELSE
  524. WrS ("exec86: INT 10h function ") ;
  525. WrHex (ah) ;
  526. WrS (" is not implemented") ;
  527. Fault ("unsupported INT 10h function")
  528. END
  529. END DoInt10 ;
  530. (* ----------------------------------------------------------------- INT 21h *)
  531. PROCEDURE DoInt21 ;
  532. (* The five services bootcom.s provides and the compiler emits, and nothing
  533. else. Anything else faults, because the alternative is inventing DOS.
  534. No flag is touched anywhere in here: IRET restores the caller's FLAGS, so
  535. neither this code nor bootcom's CLC/STC is visible to the guest. *)
  536. VAR ah, b, a, n, lim, stop : CARDINAL ;
  537. c : CHAR ;
  538. running : BOOLEAN ;
  539. BEGIN
  540. ah := AX DIV 256 ;
  541. CASE ah OF
  542. | 02H : (* display character in AL *)
  543. c := CHR (AX MOD 256) ;
  544. n := write (STDOUT, ADR (c), 1)
  545. | 09H : (* $-terminated string at DS:DX *)
  546. a := DX ;
  547. running := TRUE ;
  548. lim := 0 ;
  549. WHILE running DO
  550. b := mem [a] ;
  551. IF b = 24H THEN (* '$' *)
  552. running := FALSE
  553. ELSE
  554. c := CHR (b) ;
  555. n := write (STDOUT, ADR (c), 1) ;
  556. a := (a + 1) MOD 65536 ;
  557. lim := lim + 1 ;
  558. IF lim > 65536 THEN
  559. Fault ("INT 21h AH=09: walked the whole address space with no $") ;
  560. running := FALSE
  561. END
  562. END
  563. END
  564. | 08H : (* read a character, no echo *)
  565. IF peeked THEN
  566. c := peekCh ;
  567. peeked := FALSE ;
  568. b := ORD (c)
  569. ELSE
  570. n := read (STDIN, ADR (c), 1) ;
  571. IF n = 1 THEN
  572. b := ORD (c)
  573. ELSE
  574. b := 1AH (* end of input, as bootcom does *)
  575. END
  576. END ;
  577. AX := (AX DIV 256) * 256 + b (* AL only: AH is the function *)
  578. | 0BH : (* input status: FFh or 0 *)
  579. (* bootcom answers from its in-memory input buffer; here stdin is a
  580. pipe that already holds the whole guest input. Look one byte ahead
  581. and KEEP it: a status call must not consume the character the next
  582. AH=08h will read. A read of 0 is end of input, so nothing waits. *)
  583. IF peeked THEN
  584. b := 0FFH
  585. ELSE
  586. n := read (STDIN, ADR (c), 1) ;
  587. IF n = 1 THEN
  588. peekCh := c ;
  589. peeked := TRUE ;
  590. b := 0FFH
  591. ELSE
  592. b := 0
  593. END
  594. END ;
  595. AX := (AX DIV 256) * 256 + b
  596. | 04CH : (* terminate *)
  597. halted := TRUE ;
  598. haltCode := AX MOD 256
  599. ELSE
  600. stop := ah ;
  601. WrS ("exec86: INT 21h function ") ;
  602. WrHex (stop) ;
  603. WrS (" is not implemented") ;
  604. Fault ("unsupported INT 21h function")
  605. END
  606. END DoInt21 ;
  607. (* --------------------------------------------------------- D0-D3 shifts *)
  608. PROCEDURE DoShiftGroup (w, cnt : CARDINAL) ;
  609. (* D0-D3, after DoModRM. reg selects ROL/ROR/RCL/RCR/SHL/SHR/SAL/SAR; D0/D1
  610. shift by one, D2/D3 by CL. The count is not masked (the 8086 does not),
  611. so a count at or beyond the width shifts everything out, which the loop
  612. gives for free. CF is the last bit shifted out; OF is only defined for a
  613. count of 1 and nothing here reads it, so it is cleared. *)
  614. VAR k, i, v, sz, signbit, bit, carryin : CARDINAL ;
  615. BEGIN
  616. k := rmReg ;
  617. IF cnt = 0 THEN
  618. RETURN (* no operation, no flag change *)
  619. END ;
  620. v := RmRd (w) ;
  621. sz := Sz (w) ;
  622. signbit := sz DIV 2 ;
  623. IF (k = 4) OR (k = 6) THEN
  624. (* SHL / SAL *)
  625. FOR i := 1 TO cnt DO
  626. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  627. v := (v * 2) MOD sz ;
  628. CF := bit # 0
  629. END
  630. ELSIF k = 5 THEN
  631. (* SHR *)
  632. FOR i := 1 TO cnt DO
  633. bit := v MOD 2 ;
  634. v := v DIV 2 ;
  635. CF := bit # 0
  636. END
  637. ELSIF k = 7 THEN
  638. (* SAR *)
  639. FOR i := 1 TO cnt DO
  640. bit := v MOD 2 ;
  641. IF v >= signbit THEN
  642. v := v DIV 2 + signbit
  643. ELSE
  644. v := v DIV 2
  645. END ;
  646. CF := bit # 0
  647. END
  648. ELSIF k = 0 THEN
  649. (* ROL *)
  650. FOR i := 1 TO cnt DO
  651. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  652. v := (v * 2) MOD sz + bit ;
  653. CF := bit # 0
  654. END
  655. ELSIF k = 1 THEN
  656. (* ROR *)
  657. FOR i := 1 TO cnt DO
  658. bit := v MOD 2 ;
  659. v := v DIV 2 + bit * signbit ;
  660. CF := bit # 0
  661. END
  662. ELSIF k = 2 THEN
  663. (* RCL *)
  664. FOR i := 1 TO cnt DO
  665. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  666. IF CF THEN carryin := 1 ELSE carryin := 0 END ;
  667. v := (v * 2) MOD sz + carryin ;
  668. CF := bit # 0
  669. END
  670. ELSE
  671. (* RCR *)
  672. FOR i := 1 TO cnt DO
  673. bit := v MOD 2 ;
  674. IF CF THEN carryin := 1 ELSE carryin := 0 END ;
  675. v := v DIV 2 + carryin * signbit ;
  676. CF := bit # 0
  677. END
  678. END ;
  679. OFl := FALSE ;
  680. SetZSF (v, w) ;
  681. RmWr (v, w)
  682. END DoShiftGroup ;
  683. (* ------------------------------------------------------ F6/F7 unary group *)
  684. PROCEDURE DoUnaryGroup (w : CARDINAL) ;
  685. (* F6/F7, after DoModRM has run. reg selects:
  686. /0 /1 TEST /2 NOT /3 NEG /4 MUL /5 IMUL /6 DIV /7 IDIV *)
  687. VAR k, src, res, prod, q, remw : CARDINAL ;
  688. ma, mb, mq, mr, ldv, ldd, lq, lr : LONGINT ;
  689. neg : BOOLEAN ;
  690. BEGIN
  691. k := rmReg ;
  692. CASE k OF
  693. | 0, 1 : (* TEST r/m, imm *)
  694. IF w = 0 THEN src := Fetch8 () ELSE src := Fetch16 () END ;
  695. SetLogic (BAnd (RmRd (w), src, w), w)
  696. | 2 : (* NOT: no flags at all *)
  697. res := BNot (RmRd (w), w) ;
  698. RmWr (res, w)
  699. | 3 : (* NEG *)
  700. res := DoSub (0, RmRd (w), 0, w) ;
  701. RmWr (res, w)
  702. | 4 : (* MUL, unsigned *)
  703. src := RmRd (w) ;
  704. IF w = 1 THEN
  705. prod := AX * src ; (* max 65535^2 < 2^32 *)
  706. DX := prod DIV 65536 ;
  707. AX := prod MOD 65536 ;
  708. res := AX
  709. ELSE
  710. (* MUL r8 writes only AX: AL*src, high byte in AH, DX untouched.
  711. It is easy to write DX := 0 here out of a false tidiness. *)
  712. prod := (AX MOD 256) * src ;
  713. AX := prod MOD 65536 ;
  714. res := AX
  715. END ;
  716. (* CF and OF are the documented ones for MUL; SF and ZF are
  717. architecturally undefined and are set from the low result rather
  718. than left alone, so that they are at least deterministic. *)
  719. IF w = 1 THEN
  720. CF := DX # 0
  721. ELSE
  722. CF := prod >= 256
  723. END ;
  724. OFl := CF ;
  725. SetZSF (res, w)
  726. | 5 : (* IMUL, signed *)
  727. src := RmRd (w) ;
  728. IF w = 1 THEN
  729. ldd := S16 (AX) * S16 (src) ;
  730. lq := ldd
  731. ELSE
  732. ldd := S8 (AX MOD 256) * S8 (src) ;
  733. lq := ldd
  734. END ;
  735. (* The low word of a negative product is exactly lq MOD 65536, because
  736. ISO Modula-2's MOD always returns a non-negative remainder, which is
  737. the two's complement low word. The high word is lq DIV 65536 for
  738. the same reason: DIV floors, which is an arithmetic shift. *)
  739. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  740. IF w = 1 THEN
  741. DX := VAL (CARDINAL, (lq DIV VAL (LONGINT, 65536))
  742. MOD VAL (LONGINT, 65536)) ;
  743. CF := (lq < VAL (LONGINT, -32768))
  744. OR (lq > VAL (LONGINT, 32767)) ;
  745. res := AX
  746. ELSE
  747. CF := (lq < VAL (LONGINT, -128))
  748. OR (lq > VAL (LONGINT, 127)) ;
  749. res := AX
  750. END ;
  751. OFl := CF ;
  752. SetZSF (res, w)
  753. | 6 : (* DIV, unsigned *)
  754. src := RmRd (w) ;
  755. IF src = 0 THEN
  756. Fault ("divide by zero")
  757. ELSE
  758. IF w = 1 THEN
  759. prod := DX * 65536 + AX ;
  760. q := prod DIV src ;
  761. remw := prod MOD src ;
  762. IF q > 65535 THEN
  763. Fault ("DIV: quotient does not fit in AX")
  764. ELSE
  765. AX := q ;
  766. DX := remw ;
  767. res := AX
  768. END
  769. ELSE
  770. prod := AX MOD 65536 ;
  771. q := prod DIV src ;
  772. remw := prod MOD src ;
  773. IF q > 255 THEN
  774. Fault ("DIV: quotient does not fit in AL")
  775. ELSE
  776. AX := remw * 256 + q ;
  777. res := q
  778. END
  779. END ;
  780. IF NOT isFault THEN
  781. CF := FALSE ;
  782. OFl := FALSE ;
  783. SetZSF (res, w)
  784. END
  785. END
  786. ELSE (* /7 IDIV, signed *)
  787. src := RmRd (w) ;
  788. IF w = 1 THEN
  789. ldv := S16 (src) ;
  790. ldd := S16 (DX) * VAL (LONGINT, 65536) + VAL (LONGINT, AX)
  791. ELSE
  792. ldv := S8 (src) ;
  793. ldd := S8 (AX MOD 256)
  794. END ;
  795. IF ldv = VAL (LONGINT, 0) THEN
  796. Fault ("divide by zero")
  797. ELSE
  798. (* x86 IDIV truncates toward zero and gives the remainder the
  799. sign of the dividend. ISO Modula-2's DIV/MOD are Euclidean:
  800. the remainder is always non-negative (measured: (-7) MOD 2 = 1,
  801. 7 MOD (-2) = 1, (-7) DIV 2 = -4). So the division is redone on
  802. magnitudes, where floor and truncation coincide, and the signs
  803. are put back afterwards. *)
  804. IF ldd < VAL (LONGINT, 0) THEN ma := VAL (LONGINT, 0) - ldd
  805. ELSE ma := ldd END ;
  806. IF ldv < VAL (LONGINT, 0) THEN mb := VAL (LONGINT, 0) - ldv
  807. ELSE mb := ldv END ;
  808. mq := ma DIV mb ;
  809. mr := ma MOD mb ;
  810. neg := (ldd < VAL (LONGINT, 0)) # (ldv < VAL (LONGINT, 0)) ;
  811. IF neg THEN lq := VAL (LONGINT, 0) - mq ELSE lq := mq END ;
  812. IF ldd < VAL (LONGINT, 0) THEN lr := VAL (LONGINT, 0) - mr
  813. ELSE lr := mr END ;
  814. IF w = 1 THEN
  815. IF (lq < VAL (LONGINT, -32768)) OR (lq > VAL (LONGINT, 32767)) THEN
  816. Fault ("IDIV: quotient does not fit in AX")
  817. ELSE
  818. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  819. DX := VAL (CARDINAL, lr MOD VAL (LONGINT, 65536)) ;
  820. SetZSF (AX, 1)
  821. END
  822. ELSE
  823. IF (lq < VAL (LONGINT, -128)) OR (lq > VAL (LONGINT, 127)) THEN
  824. Fault ("IDIV: quotient does not fit in AL")
  825. ELSE
  826. (* IDIV r8 also overwrites both halves: AL := quotient,
  827. AH := remainder, so the old AH is gone. *)
  828. AX := VAL (CARDINAL, lr MOD VAL (LONGINT, 256)) * 256
  829. + VAL (CARDINAL, lq MOD VAL (LONGINT, 256)) ;
  830. SetZSF (VAL (CARDINAL, lq MOD VAL (LONGINT, 256)), 0)
  831. END
  832. END ;
  833. IF NOT isFault THEN
  834. CF := FALSE ;
  835. OFl := FALSE
  836. END
  837. END
  838. END
  839. END DoUnaryGroup ;
  840. (* --------------------------------------------------------- FF group (word) *)
  841. PROCEDURE DoFFGroup ;
  842. (* FF, after DoModRM has run with w = 1. *)
  843. VAR k, res : CARDINAL ;
  844. cfSave : BOOLEAN ;
  845. BEGIN
  846. k := rmReg ;
  847. CASE k OF
  848. | 0 : (* INC r/m: CF is preserved *)
  849. cfSave := CF ;
  850. res := DoAdd (RmRd (1), 1, 0, 1) ;
  851. CF := cfSave ;
  852. RmWr (res, 1)
  853. | 1 : (* DEC r/m: CF is preserved *)
  854. cfSave := CF ;
  855. res := DoSub (RmRd (1), 1, 0, 1) ;
  856. CF := cfSave ;
  857. RmWr (res, 1)
  858. | 2 : (* CALL near r/m *)
  859. Push (IP) ;
  860. IP := RmRd (1)
  861. | 4 : (* JMP near r/m *)
  862. IP := RmRd (1)
  863. | 6 : (* PUSH r/m *)
  864. Push (RmRd (1))
  865. ELSE
  866. Fault ("unsupported opcode in the FF group (far call/jump)")
  867. END
  868. END DoFFGroup ;
  869. (* ----------------------------------------------------------- one instruction *)
  870. PROCEDURE Step ;
  871. VAR op, w, f, alu, v, d, imm, res, n, k, cnt : CARDINAL ;
  872. cfSave : BOOLEAN ;
  873. BEGIN
  874. faultIP := IP ; (* what to report if we fault *)
  875. op := Fetch8 () ;
  876. IF (op = 06H) OR (op = 0EH) OR (op = 16H) OR (op = 1EH) THEN
  877. (* PUSH ES / CS / SS / DS. CS and SS are pushed by nothing in the
  878. corpus, but the four encodings are one instruction apart and
  879. leaving two of them out would be a gap nobody could explain. *)
  880. IF op = 06H THEN Push (ES)
  881. ELSIF op = 0EH THEN Push (CS)
  882. ELSIF op = 16H THEN Push (SS)
  883. ELSE Push (DS)
  884. END
  885. ELSIF (op = 07H) OR (op = 17H) OR (op = 1FH) THEN
  886. IF op = 07H THEN ES := Pop ()
  887. ELSIF op = 17H THEN SS := Pop ()
  888. ELSE DS := Pop ()
  889. END
  890. ELSIF (op = 26H) OR (op = 2EH) OR (op = 36H) OR (op = 3EH) THEN
  891. Fault ("segment override prefix: this interpreter is 64 KB flat")
  892. ELSIF op < 40H THEN
  893. (* The 00-3D family: eight ALU operations in six encodings each.
  894. `alu' is the group number, `f' the form.
  895. f = 0,1 op r/m, r f = 2,3 op r, r/m
  896. f = 4 op AL, imm8 f = 5 op AX, imm16
  897. f = 6,7 are the prefixes and DAA/DAS/AAA/AAS, all consumed above
  898. or unreachable, so reaching here is a real unknown. *)
  899. alu := (op DIV 8) MOD 8 ;
  900. f := op MOD 8 ;
  901. IF f <= 3 THEN
  902. w := f MOD 2 ;
  903. DoModRM (w) ;
  904. IF f <= 1 THEN
  905. res := Alu (alu, RmRd (w), GetReg (rmReg, w), w) ;
  906. IF alu # 7 THEN RmWr (res, w) END
  907. ELSE
  908. res := Alu (alu, GetReg (rmReg, w), RmRd (w), w) ;
  909. IF alu # 7 THEN PutReg (rmReg, w, res) END
  910. END
  911. ELSIF f = 4 THEN
  912. v := Fetch8 () ;
  913. res := Alu (alu, AX MOD 256, v, 0) ;
  914. IF alu # 7 THEN PutReg (0, 0, res) END
  915. ELSIF f = 5 THEN
  916. v := Fetch16 () ;
  917. res := Alu (alu, AX, v, 1) ;
  918. IF alu # 7 THEN PutReg (0, 1, res) END
  919. ELSE
  920. Fault ("unknown opcode in the ALU family")
  921. END
  922. ELSIF op < 50H THEN
  923. (* INC r16 / DEC r16. These do NOT affect CF, which is easy to lose:
  924. going through DoAdd sets it, so it is saved and restored. *)
  925. k := op - 40H ;
  926. cfSave := CF ;
  927. IF k < 8 THEN
  928. res := DoAdd (GetReg (k, 1), 1, 0, 1) ;
  929. PutReg (k, 1, res)
  930. ELSE
  931. res := DoSub (GetReg (k - 8, 1), 1, 0, 1) ;
  932. PutReg (k - 8, 1, res)
  933. END ;
  934. CF := cfSave
  935. ELSIF op < 60H THEN
  936. IF op < 58H THEN
  937. Push (GetReg (op - 50H, 1))
  938. ELSE
  939. PutReg (op - 58H, 1, Pop ())
  940. END
  941. ELSIF op < 70H THEN
  942. Fault ("opcode not implemented (60H-6FH is 80186 and later)")
  943. ELSIF op < 80H THEN
  944. d := Fetch8 () ;
  945. IF Cond (op - 70H) THEN
  946. IP := (IP + SE8 (d)) MOD 65536
  947. END
  948. ELSIF (op = 80H) OR (op = 81H) OR (op = 83H) THEN
  949. IF op = 80H THEN w := 0 ELSE w := 1 END ;
  950. DoModRM (w) ;
  951. k := rmReg ;
  952. IF op = 83H THEN
  953. imm := SE8 (Fetch8 ()) (* sign-extended to the full width *)
  954. ELSIF w = 0 THEN
  955. imm := Fetch8 ()
  956. ELSE
  957. imm := Fetch16 ()
  958. END ;
  959. res := Alu (k, RmRd (w), imm, w) ;
  960. IF k # 7 THEN RmWr (res, w) END
  961. ELSIF (op = 84H) OR (op = 85H) THEN
  962. (* TEST r/m, r. Same AND and same flag rule as F6/F7 /0; only the
  963. encoding differs, and leaving it out while having the other one
  964. would be a gap with no reason behind it. *)
  965. w := op - 84H ;
  966. DoModRM (w) ;
  967. SetLogic (BAnd (RmRd (w), GetReg (rmReg, w), w), w)
  968. ELSIF (op = 86H) OR (op = 87H) THEN
  969. w := op - 86H ;
  970. DoModRM (w) ;
  971. v := RmRd (w) ;
  972. RmWr (GetReg (rmReg, w), w) ;
  973. PutReg (rmReg, w, v)
  974. ELSIF (op >= 88H) AND (op <= 8BH) THEN
  975. w := op MOD 2 ;
  976. DoModRM (w) ;
  977. IF op >= 8AH THEN
  978. PutReg (rmReg, w, RmRd (w))
  979. ELSE
  980. RmWr (GetReg (rmReg, w), w)
  981. END
  982. ELSIF (op = 0C6H) OR (op = 0C7H) THEN
  983. (* MOV r/m,imm. The compiler emits C6 06 lo hi imm for the CRT
  984. attribute byte; C7 is the 16-bit form. The ModRM reg field is
  985. reserved (must be 0) and a register destination is not this opcode
  986. (8A/8B or B0+/B8+ are). *)
  987. w := op - 0C6H ;
  988. DoModRM (w) ;
  989. IF eaIsReg THEN
  990. Fault ("MOV r/m,imm needs a memory destination")
  991. ELSE
  992. IF w = 0 THEN
  993. v := Fetch8 ()
  994. ELSE
  995. v := Fetch16 ()
  996. END ;
  997. RmWr (v, w)
  998. END
  999. ELSIF op = 8DH THEN
  1000. DoModRM (1) ;
  1001. IF eaIsReg THEN
  1002. Fault ("LEA with a register operand is not an address")
  1003. ELSE
  1004. PutReg (rmReg, 1, eaAddr)
  1005. END
  1006. ELSIF (op >= 90H) AND (op <= 97H) THEN
  1007. k := op - 90H ;
  1008. IF k # 0 THEN (* 90 is NOP *)
  1009. v := AX ;
  1010. AX := GetReg (k, 1) ;
  1011. PutReg (k, 1, v)
  1012. END
  1013. ELSIF op = 98H THEN (* CBW: sign-extend AL into AX *)
  1014. IF (AX MOD 256) >= 80H THEN
  1015. AX := 65280 + (AX MOD 256)
  1016. ELSE
  1017. AX := AX MOD 256
  1018. END
  1019. ELSIF op = 99H THEN (* CWD: sign-extend AX into DX *)
  1020. IF AX >= 8000H THEN DX := 0FFFFH ELSE DX := 0 END
  1021. ELSIF (op >= 0A0H) AND (op <= 0A3H) THEN
  1022. d := Fetch16 () ; (* moffs: DS is 0, checked in Run86 *)
  1023. CASE op OF
  1024. | 0A0H : AX := (AX DIV 256) * 256 + mem [d]
  1025. | 0A1H : AX := mem [d] + 256 * mem [(d + 1) MOD 65536]
  1026. | 0A2H : mem [d] := AX MOD 256
  1027. ELSE mem [d] := AX MOD 256 ;
  1028. mem [(d + 1) MOD 65536] := (AX DIV 256) MOD 256
  1029. END
  1030. ELSIF (op >= 0B0H) AND (op <= 0BFH) THEN
  1031. IF op < 0B8H THEN
  1032. PutReg (op - 0B0H, 0, Fetch8 ())
  1033. ELSE
  1034. PutReg (op - 0B8H, 1, Fetch16 ())
  1035. END
  1036. ELSIF op = 0C3H THEN (* RET *)
  1037. IP := Pop ()
  1038. ELSIF op = 0C9H THEN (* LEAVE: SP := BP; BP := POP *)
  1039. SP := BP ;
  1040. BP := Pop ()
  1041. ELSIF op = 0CDH THEN (* INT *)
  1042. n := Fetch8 () ;
  1043. IF n = 21H THEN
  1044. DoInt21 ()
  1045. ELSIF n = 10H THEN
  1046. DoInt10 ()
  1047. ELSE
  1048. Fault ("only INT 21h and INT 10h are provided; this is not a real-mode machine")
  1049. END
  1050. ELSIF op = 0E2H THEN (* LOOP: CX is not a flag *)
  1051. d := Fetch8 () ;
  1052. CX := (CX + 65535) MOD 65536 ;
  1053. IF CX # 0 THEN
  1054. IP := (IP + SE8 (d)) MOD 65536
  1055. END
  1056. ELSIF op = 0E3H THEN (* JCXZ *)
  1057. d := Fetch8 () ;
  1058. IF CX = 0 THEN
  1059. IP := (IP + SE8 (d)) MOD 65536
  1060. END
  1061. ELSIF op = 0E8H THEN (* CALL rel16 *)
  1062. d := Fetch16 () ;
  1063. Push (IP) ;
  1064. IP := (IP + d) MOD 65536
  1065. ELSIF op = 0E9H THEN (* JMP rel16 *)
  1066. d := Fetch16 () ;
  1067. IP := (IP + d) MOD 65536
  1068. ELSIF op = 0EBH THEN (* JMP rel8 *)
  1069. d := Fetch8 () ;
  1070. IP := (IP + SE8 (d)) MOD 65536
  1071. ELSIF (op >= 0D0H) AND (op <= 0D3H) THEN
  1072. w := op MOD 2 ;
  1073. IF op < 0D2H THEN
  1074. cnt := 1
  1075. ELSE
  1076. cnt := CX MOD 32
  1077. END ;
  1078. DoModRM (w) ;
  1079. DoShiftGroup (w, cnt)
  1080. ELSIF (op = 0F6H) OR (op = 0F7H) THEN
  1081. DoModRM (op - 0F6H) ;
  1082. DoUnaryGroup (op - 0F6H)
  1083. ELSIF op = 0FFH THEN
  1084. DoModRM (1) ;
  1085. DoFFGroup
  1086. ELSE
  1087. WrS ("exec86: unknown opcode ") ;
  1088. WrHex (op) ;
  1089. WrS (" at ") ;
  1090. WrHex (faultIP) ;
  1091. WrErr1 (CHR (10)) ;
  1092. Fault ("unknown opcode")
  1093. END
  1094. END Step ;
  1095. (* ------------------------------------------------------------------ public *)
  1096. PROCEDURE Clear86 ;
  1097. VAR i : CARDINAL ;
  1098. BEGIN
  1099. FOR i := 0 TO 65535 DO
  1100. mem [i] := 0
  1101. END ;
  1102. AX := 0 ; BX := 0 ; CX := 0 ; DX := 0 ;
  1103. SI := 0 ; DI := 0 ; BP := 0 ;
  1104. SP := 0FFFEH ; (* bootcom's SS:SP = 0000:FFFE *)
  1105. CS := 0 ; DS := 0 ; ES := 0 ; SS := 0 ;
  1106. IP := LoadAt ; (* bootcom's JMP 0000:0100 *)
  1107. CF := FALSE ; ZF := FALSE ; SF := FALSE ; OFl := FALSE ;
  1108. halted := FALSE ;
  1109. haltCode := 0 ;
  1110. isFault := FALSE ;
  1111. faultIP := LoadAt ;
  1112. loadHi := LoadAt ;
  1113. stepCnt := 0 ;
  1114. eaAddr := 0 ; eaReg := 0 ; eaIsReg := FALSE ; rmReg := 0
  1115. END Clear86 ;
  1116. PROCEDURE Poke86 (addr, value : CARDINAL) ;
  1117. BEGIN
  1118. IF addr > 65535 THEN
  1119. faultIP := IP ;
  1120. Fault ("Poke address is outside the 64 KB machine")
  1121. ELSE
  1122. mem [addr] := value MOD 256 ;
  1123. IF addr >= loadHi THEN
  1124. loadHi := addr + 1
  1125. END
  1126. END
  1127. END Poke86 ;
  1128. PROCEDURE Run86 (VAR exitCode : CARDINAL; VAR steps : LONGCARD) : CARDINAL ;
  1129. VAR cap : LONGCARD ;
  1130. BEGIN
  1131. exitCode := 0 ;
  1132. steps := 0 ;
  1133. isFault := FALSE ;
  1134. halted := FALSE ;
  1135. cap := VAL (LONGCARD, MaxSteps) ;
  1136. LOOP
  1137. IF halted THEN
  1138. exitCode := haltCode ;
  1139. steps := stepCnt ;
  1140. RETURN 0
  1141. END ;
  1142. IF isFault THEN
  1143. steps := stepCnt ;
  1144. RETURN 1
  1145. END ;
  1146. IF stepCnt >= cap THEN
  1147. faultIP := IP ;
  1148. WrS ("exec86: step limit ") ;
  1149. WrDec (MaxSteps) ; (* runaway guard, Exec86.def *)
  1150. WrS (" reached at IP=") ;
  1151. WrHex (IP) ;
  1152. WrErr1 (CHR (10)) ;
  1153. steps := stepCnt ;
  1154. RETURN 2
  1155. END ;
  1156. (* Checked before every step, not once at the start: the machine is
  1157. 64 KB flat, so a non-zero segment would silently alias onto the
  1158. same memory instead of faulting. *)
  1159. IF (CS # 0) OR (DS # 0) OR (ES # 0) OR (SS # 0) THEN
  1160. faultIP := IP ;
  1161. Fault ("a segment register is not zero; this machine is 64 KB flat") ;
  1162. steps := stepCnt ;
  1163. RETURN 1
  1164. END ;
  1165. IF (IP < LoadAt) OR (IP >= loadHi) THEN
  1166. faultIP := IP ;
  1167. Fault ("execution left the loaded image") ;
  1168. steps := stepCnt ;
  1169. RETURN 1
  1170. END ;
  1171. Step () ;
  1172. stepCnt := stepCnt + 1
  1173. END
  1174. END Run86 ;
  1175. END Exec86.