Exec86.mod 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  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. The console services leave the flags alone, matching an IRET that restores
  535. the caller's FLAGS. AH=3Dh is different: the runtime reads CF as the DOS
  536. error flag, so the file-service path sets (or clears) it explicitly. This
  537. interpreter executes INT inline rather than modelling the pushed FLAGS. *)
  538. VAR ah, b, a, n, lim, stop : CARDINAL ;
  539. c : CHAR ;
  540. running : BOOLEAN ;
  541. BEGIN
  542. ah := AX DIV 256 ;
  543. CASE ah OF
  544. | 02H : (* display character in AL *)
  545. c := CHR (AX MOD 256) ;
  546. n := write (STDOUT, ADR (c), 1)
  547. | 09H : (* $-terminated string at DS:DX *)
  548. a := DX ;
  549. running := TRUE ;
  550. lim := 0 ;
  551. WHILE running DO
  552. b := mem [a] ;
  553. IF b = 24H THEN (* '$' *)
  554. running := FALSE
  555. ELSE
  556. c := CHR (b) ;
  557. n := write (STDOUT, ADR (c), 1) ;
  558. a := (a + 1) MOD 65536 ;
  559. lim := lim + 1 ;
  560. IF lim > 65536 THEN
  561. Fault ("INT 21h AH=09: walked the whole address space with no $") ;
  562. running := FALSE
  563. END
  564. END
  565. END
  566. | 08H : (* read a character, no echo *)
  567. IF peeked THEN
  568. c := peekCh ;
  569. peeked := FALSE ;
  570. b := ORD (c)
  571. ELSE
  572. n := read (STDIN, ADR (c), 1) ;
  573. IF n = 1 THEN
  574. b := ORD (c)
  575. ELSE
  576. b := 1AH (* end of input, as bootcom does *)
  577. END
  578. END ;
  579. AX := (AX DIV 256) * 256 + b (* AL only: AH is the function *)
  580. | 0BH : (* input status: FFh or 0 *)
  581. (* bootcom answers from its in-memory input buffer; here stdin is a
  582. pipe that already holds the whole guest input. Look one byte ahead
  583. and KEEP it: a status call must not consume the character the next
  584. AH=08h will read. A read of 0 is end of input, so nothing waits. *)
  585. IF peeked THEN
  586. b := 0FFH
  587. ELSE
  588. n := read (STDIN, ADR (c), 1) ;
  589. IF n = 1 THEN
  590. peekCh := c ;
  591. peeked := TRUE ;
  592. b := 0FFH
  593. ELSE
  594. b := 0
  595. END
  596. END ;
  597. AX := (AX DIV 256) * 256 + b
  598. | 3DH : (* open file: no filesystem *)
  599. (* The runtime uses real DOS AH=3Dh. This interpreter has no
  600. filesystem either; answer "File not found" the way DOS does:
  601. CF=1 and AX=2. The file-operation fixture checks that path. *)
  602. AX := 2 ;
  603. CF := TRUE
  604. | 04CH : (* terminate *)
  605. halted := TRUE ;
  606. haltCode := AX MOD 256
  607. ELSE
  608. stop := ah ;
  609. WrS ("exec86: INT 21h function ") ;
  610. WrHex (stop) ;
  611. WrS (" is not implemented") ;
  612. Fault ("unsupported INT 21h function")
  613. END
  614. END DoInt21 ;
  615. (* --------------------------------------------------------- D0-D3 shifts *)
  616. PROCEDURE DoShiftGroup (w, cnt : CARDINAL) ;
  617. (* D0-D3, after DoModRM. reg selects ROL/ROR/RCL/RCR/SHL/SHR/SAL/SAR; D0/D1
  618. shift by one, D2/D3 by CL. The count is not masked (the 8086 does not),
  619. so a count at or beyond the width shifts everything out, which the loop
  620. gives for free. CF is the last bit shifted out; OF is only defined for a
  621. count of 1 and nothing here reads it, so it is cleared. *)
  622. VAR k, i, v, sz, signbit, bit, carryin : CARDINAL ;
  623. BEGIN
  624. k := rmReg ;
  625. IF cnt = 0 THEN
  626. RETURN (* no operation, no flag change *)
  627. END ;
  628. v := RmRd (w) ;
  629. sz := Sz (w) ;
  630. signbit := sz DIV 2 ;
  631. IF (k = 4) OR (k = 6) THEN
  632. (* SHL / SAL *)
  633. FOR i := 1 TO cnt DO
  634. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  635. v := (v * 2) MOD sz ;
  636. CF := bit # 0
  637. END
  638. ELSIF k = 5 THEN
  639. (* SHR *)
  640. FOR i := 1 TO cnt DO
  641. bit := v MOD 2 ;
  642. v := v DIV 2 ;
  643. CF := bit # 0
  644. END
  645. ELSIF k = 7 THEN
  646. (* SAR *)
  647. FOR i := 1 TO cnt DO
  648. bit := v MOD 2 ;
  649. IF v >= signbit THEN
  650. v := v DIV 2 + signbit
  651. ELSE
  652. v := v DIV 2
  653. END ;
  654. CF := bit # 0
  655. END
  656. ELSIF k = 0 THEN
  657. (* ROL *)
  658. FOR i := 1 TO cnt DO
  659. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  660. v := (v * 2) MOD sz + bit ;
  661. CF := bit # 0
  662. END
  663. ELSIF k = 1 THEN
  664. (* ROR *)
  665. FOR i := 1 TO cnt DO
  666. bit := v MOD 2 ;
  667. v := v DIV 2 + bit * signbit ;
  668. CF := bit # 0
  669. END
  670. ELSIF k = 2 THEN
  671. (* RCL *)
  672. FOR i := 1 TO cnt DO
  673. IF v >= signbit THEN bit := 1 ELSE bit := 0 END ;
  674. IF CF THEN carryin := 1 ELSE carryin := 0 END ;
  675. v := (v * 2) MOD sz + carryin ;
  676. CF := bit # 0
  677. END
  678. ELSE
  679. (* RCR *)
  680. FOR i := 1 TO cnt DO
  681. bit := v MOD 2 ;
  682. IF CF THEN carryin := 1 ELSE carryin := 0 END ;
  683. v := v DIV 2 + carryin * signbit ;
  684. CF := bit # 0
  685. END
  686. END ;
  687. OFl := FALSE ;
  688. SetZSF (v, w) ;
  689. RmWr (v, w)
  690. END DoShiftGroup ;
  691. (* ------------------------------------------------------ F6/F7 unary group *)
  692. PROCEDURE DoUnaryGroup (w : CARDINAL) ;
  693. (* F6/F7, after DoModRM has run. reg selects:
  694. /0 /1 TEST /2 NOT /3 NEG /4 MUL /5 IMUL /6 DIV /7 IDIV *)
  695. VAR k, src, res, prod, q, remw : CARDINAL ;
  696. ma, mb, mq, mr, ldv, ldd, lq, lr : LONGINT ;
  697. neg : BOOLEAN ;
  698. BEGIN
  699. k := rmReg ;
  700. CASE k OF
  701. | 0, 1 : (* TEST r/m, imm *)
  702. IF w = 0 THEN src := Fetch8 () ELSE src := Fetch16 () END ;
  703. SetLogic (BAnd (RmRd (w), src, w), w)
  704. | 2 : (* NOT: no flags at all *)
  705. res := BNot (RmRd (w), w) ;
  706. RmWr (res, w)
  707. | 3 : (* NEG *)
  708. res := DoSub (0, RmRd (w), 0, w) ;
  709. RmWr (res, w)
  710. | 4 : (* MUL, unsigned *)
  711. src := RmRd (w) ;
  712. IF w = 1 THEN
  713. prod := AX * src ; (* max 65535^2 < 2^32 *)
  714. DX := prod DIV 65536 ;
  715. AX := prod MOD 65536 ;
  716. res := AX
  717. ELSE
  718. (* MUL r8 writes only AX: AL*src, high byte in AH, DX untouched.
  719. It is easy to write DX := 0 here out of a false tidiness. *)
  720. prod := (AX MOD 256) * src ;
  721. AX := prod MOD 65536 ;
  722. res := AX
  723. END ;
  724. (* CF and OF are the documented ones for MUL; SF and ZF are
  725. architecturally undefined and are set from the low result rather
  726. than left alone, so that they are at least deterministic. *)
  727. IF w = 1 THEN
  728. CF := DX # 0
  729. ELSE
  730. CF := prod >= 256
  731. END ;
  732. OFl := CF ;
  733. SetZSF (res, w)
  734. | 5 : (* IMUL, signed *)
  735. src := RmRd (w) ;
  736. IF w = 1 THEN
  737. ldd := S16 (AX) * S16 (src) ;
  738. lq := ldd
  739. ELSE
  740. ldd := S8 (AX MOD 256) * S8 (src) ;
  741. lq := ldd
  742. END ;
  743. (* The low word of a negative product is exactly lq MOD 65536, because
  744. ISO Modula-2's MOD always returns a non-negative remainder, which is
  745. the two's complement low word. The high word is lq DIV 65536 for
  746. the same reason: DIV floors, which is an arithmetic shift. *)
  747. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  748. IF w = 1 THEN
  749. DX := VAL (CARDINAL, (lq DIV VAL (LONGINT, 65536))
  750. MOD VAL (LONGINT, 65536)) ;
  751. CF := (lq < VAL (LONGINT, -32768))
  752. OR (lq > VAL (LONGINT, 32767)) ;
  753. res := AX
  754. ELSE
  755. CF := (lq < VAL (LONGINT, -128))
  756. OR (lq > VAL (LONGINT, 127)) ;
  757. res := AX
  758. END ;
  759. OFl := CF ;
  760. SetZSF (res, w)
  761. | 6 : (* DIV, unsigned *)
  762. src := RmRd (w) ;
  763. IF src = 0 THEN
  764. Fault ("divide by zero")
  765. ELSE
  766. IF w = 1 THEN
  767. prod := DX * 65536 + AX ;
  768. q := prod DIV src ;
  769. remw := prod MOD src ;
  770. IF q > 65535 THEN
  771. Fault ("DIV: quotient does not fit in AX")
  772. ELSE
  773. AX := q ;
  774. DX := remw ;
  775. res := AX
  776. END
  777. ELSE
  778. prod := AX MOD 65536 ;
  779. q := prod DIV src ;
  780. remw := prod MOD src ;
  781. IF q > 255 THEN
  782. Fault ("DIV: quotient does not fit in AL")
  783. ELSE
  784. AX := remw * 256 + q ;
  785. res := q
  786. END
  787. END ;
  788. IF NOT isFault THEN
  789. CF := FALSE ;
  790. OFl := FALSE ;
  791. SetZSF (res, w)
  792. END
  793. END
  794. ELSE (* /7 IDIV, signed *)
  795. src := RmRd (w) ;
  796. IF w = 1 THEN
  797. ldv := S16 (src) ;
  798. ldd := S16 (DX) * VAL (LONGINT, 65536) + VAL (LONGINT, AX)
  799. ELSE
  800. ldv := S8 (src) ;
  801. ldd := S8 (AX MOD 256)
  802. END ;
  803. IF ldv = VAL (LONGINT, 0) THEN
  804. Fault ("divide by zero")
  805. ELSE
  806. (* x86 IDIV truncates toward zero and gives the remainder the
  807. sign of the dividend. ISO Modula-2's DIV/MOD are Euclidean:
  808. the remainder is always non-negative (measured: (-7) MOD 2 = 1,
  809. 7 MOD (-2) = 1, (-7) DIV 2 = -4). So the division is redone on
  810. magnitudes, where floor and truncation coincide, and the signs
  811. are put back afterwards. *)
  812. IF ldd < VAL (LONGINT, 0) THEN ma := VAL (LONGINT, 0) - ldd
  813. ELSE ma := ldd END ;
  814. IF ldv < VAL (LONGINT, 0) THEN mb := VAL (LONGINT, 0) - ldv
  815. ELSE mb := ldv END ;
  816. mq := ma DIV mb ;
  817. mr := ma MOD mb ;
  818. neg := (ldd < VAL (LONGINT, 0)) # (ldv < VAL (LONGINT, 0)) ;
  819. IF neg THEN lq := VAL (LONGINT, 0) - mq ELSE lq := mq END ;
  820. IF ldd < VAL (LONGINT, 0) THEN lr := VAL (LONGINT, 0) - mr
  821. ELSE lr := mr END ;
  822. IF w = 1 THEN
  823. IF (lq < VAL (LONGINT, -32768)) OR (lq > VAL (LONGINT, 32767)) THEN
  824. Fault ("IDIV: quotient does not fit in AX")
  825. ELSE
  826. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  827. DX := VAL (CARDINAL, lr MOD VAL (LONGINT, 65536)) ;
  828. SetZSF (AX, 1)
  829. END
  830. ELSE
  831. IF (lq < VAL (LONGINT, -128)) OR (lq > VAL (LONGINT, 127)) THEN
  832. Fault ("IDIV: quotient does not fit in AL")
  833. ELSE
  834. (* IDIV r8 also overwrites both halves: AL := quotient,
  835. AH := remainder, so the old AH is gone. *)
  836. AX := VAL (CARDINAL, lr MOD VAL (LONGINT, 256)) * 256
  837. + VAL (CARDINAL, lq MOD VAL (LONGINT, 256)) ;
  838. SetZSF (VAL (CARDINAL, lq MOD VAL (LONGINT, 256)), 0)
  839. END
  840. END ;
  841. IF NOT isFault THEN
  842. CF := FALSE ;
  843. OFl := FALSE
  844. END
  845. END
  846. END
  847. END DoUnaryGroup ;
  848. (* --------------------------------------------------------- FF group (word) *)
  849. PROCEDURE DoFFGroup ;
  850. (* FF, after DoModRM has run with w = 1. *)
  851. VAR k, res : CARDINAL ;
  852. cfSave : BOOLEAN ;
  853. BEGIN
  854. k := rmReg ;
  855. CASE k OF
  856. | 0 : (* INC r/m: CF is preserved *)
  857. cfSave := CF ;
  858. res := DoAdd (RmRd (1), 1, 0, 1) ;
  859. CF := cfSave ;
  860. RmWr (res, 1)
  861. | 1 : (* DEC r/m: CF is preserved *)
  862. cfSave := CF ;
  863. res := DoSub (RmRd (1), 1, 0, 1) ;
  864. CF := cfSave ;
  865. RmWr (res, 1)
  866. | 2 : (* CALL near r/m *)
  867. Push (IP) ;
  868. IP := RmRd (1)
  869. | 4 : (* JMP near r/m *)
  870. IP := RmRd (1)
  871. | 6 : (* PUSH r/m *)
  872. Push (RmRd (1))
  873. ELSE
  874. Fault ("unsupported opcode in the FF group (far call/jump)")
  875. END
  876. END DoFFGroup ;
  877. (* ----------------------------------------------------------- one instruction *)
  878. PROCEDURE Step ;
  879. VAR op, w, f, alu, v, d, imm, res, n, k, cnt : CARDINAL ;
  880. cfSave : BOOLEAN ;
  881. BEGIN
  882. faultIP := IP ; (* what to report if we fault *)
  883. op := Fetch8 () ;
  884. IF (op = 06H) OR (op = 0EH) OR (op = 16H) OR (op = 1EH) THEN
  885. (* PUSH ES / CS / SS / DS. CS and SS are pushed by nothing in the
  886. corpus, but the four encodings are one instruction apart and
  887. leaving two of them out would be a gap nobody could explain. *)
  888. IF op = 06H THEN Push (ES)
  889. ELSIF op = 0EH THEN Push (CS)
  890. ELSIF op = 16H THEN Push (SS)
  891. ELSE Push (DS)
  892. END
  893. ELSIF (op = 07H) OR (op = 17H) OR (op = 1FH) THEN
  894. IF op = 07H THEN ES := Pop ()
  895. ELSIF op = 17H THEN SS := Pop ()
  896. ELSE DS := Pop ()
  897. END
  898. ELSIF (op = 26H) OR (op = 2EH) OR (op = 36H) OR (op = 3EH) THEN
  899. Fault ("segment override prefix: this interpreter is 64 KB flat")
  900. ELSIF op < 40H THEN
  901. (* The 00-3D family: eight ALU operations in six encodings each.
  902. `alu' is the group number, `f' the form.
  903. f = 0,1 op r/m, r f = 2,3 op r, r/m
  904. f = 4 op AL, imm8 f = 5 op AX, imm16
  905. f = 6,7 are the prefixes and DAA/DAS/AAA/AAS, all consumed above
  906. or unreachable, so reaching here is a real unknown. *)
  907. alu := (op DIV 8) MOD 8 ;
  908. f := op MOD 8 ;
  909. IF f <= 3 THEN
  910. w := f MOD 2 ;
  911. DoModRM (w) ;
  912. IF f <= 1 THEN
  913. res := Alu (alu, RmRd (w), GetReg (rmReg, w), w) ;
  914. IF alu # 7 THEN RmWr (res, w) END
  915. ELSE
  916. res := Alu (alu, GetReg (rmReg, w), RmRd (w), w) ;
  917. IF alu # 7 THEN PutReg (rmReg, w, res) END
  918. END
  919. ELSIF f = 4 THEN
  920. v := Fetch8 () ;
  921. res := Alu (alu, AX MOD 256, v, 0) ;
  922. IF alu # 7 THEN PutReg (0, 0, res) END
  923. ELSIF f = 5 THEN
  924. v := Fetch16 () ;
  925. res := Alu (alu, AX, v, 1) ;
  926. IF alu # 7 THEN PutReg (0, 1, res) END
  927. ELSE
  928. Fault ("unknown opcode in the ALU family")
  929. END
  930. ELSIF op < 50H THEN
  931. (* INC r16 / DEC r16. These do NOT affect CF, which is easy to lose:
  932. going through DoAdd sets it, so it is saved and restored. *)
  933. k := op - 40H ;
  934. cfSave := CF ;
  935. IF k < 8 THEN
  936. res := DoAdd (GetReg (k, 1), 1, 0, 1) ;
  937. PutReg (k, 1, res)
  938. ELSE
  939. res := DoSub (GetReg (k - 8, 1), 1, 0, 1) ;
  940. PutReg (k - 8, 1, res)
  941. END ;
  942. CF := cfSave
  943. ELSIF op < 60H THEN
  944. IF op < 58H THEN
  945. Push (GetReg (op - 50H, 1))
  946. ELSE
  947. PutReg (op - 58H, 1, Pop ())
  948. END
  949. ELSIF op < 70H THEN
  950. Fault ("opcode not implemented (60H-6FH is 80186 and later)")
  951. ELSIF op < 80H THEN
  952. d := Fetch8 () ;
  953. IF Cond (op - 70H) THEN
  954. IP := (IP + SE8 (d)) MOD 65536
  955. END
  956. ELSIF (op = 80H) OR (op = 81H) OR (op = 83H) THEN
  957. IF op = 80H THEN w := 0 ELSE w := 1 END ;
  958. DoModRM (w) ;
  959. k := rmReg ;
  960. IF op = 83H THEN
  961. imm := SE8 (Fetch8 ()) (* sign-extended to the full width *)
  962. ELSIF w = 0 THEN
  963. imm := Fetch8 ()
  964. ELSE
  965. imm := Fetch16 ()
  966. END ;
  967. res := Alu (k, RmRd (w), imm, w) ;
  968. IF k # 7 THEN RmWr (res, w) END
  969. ELSIF (op = 84H) OR (op = 85H) THEN
  970. (* TEST r/m, r. Same AND and same flag rule as F6/F7 /0; only the
  971. encoding differs, and leaving it out while having the other one
  972. would be a gap with no reason behind it. *)
  973. w := op - 84H ;
  974. DoModRM (w) ;
  975. SetLogic (BAnd (RmRd (w), GetReg (rmReg, w), w), w)
  976. ELSIF (op = 86H) OR (op = 87H) THEN
  977. w := op - 86H ;
  978. DoModRM (w) ;
  979. v := RmRd (w) ;
  980. RmWr (GetReg (rmReg, w), w) ;
  981. PutReg (rmReg, w, v)
  982. ELSIF (op >= 88H) AND (op <= 8BH) THEN
  983. w := op MOD 2 ;
  984. DoModRM (w) ;
  985. IF op >= 8AH THEN
  986. PutReg (rmReg, w, RmRd (w))
  987. ELSE
  988. RmWr (GetReg (rmReg, w), w)
  989. END
  990. ELSIF (op = 0C6H) OR (op = 0C7H) THEN
  991. (* MOV r/m,imm. The compiler emits C6 06 lo hi imm for the CRT
  992. attribute byte; C7 is the 16-bit form. The ModRM reg field is
  993. reserved (must be 0) and a register destination is not this opcode
  994. (8A/8B or B0+/B8+ are). *)
  995. w := op - 0C6H ;
  996. DoModRM (w) ;
  997. IF eaIsReg THEN
  998. Fault ("MOV r/m,imm needs a memory destination")
  999. ELSE
  1000. IF w = 0 THEN
  1001. v := Fetch8 ()
  1002. ELSE
  1003. v := Fetch16 ()
  1004. END ;
  1005. RmWr (v, w)
  1006. END
  1007. ELSIF op = 8DH THEN
  1008. DoModRM (1) ;
  1009. IF eaIsReg THEN
  1010. Fault ("LEA with a register operand is not an address")
  1011. ELSE
  1012. PutReg (rmReg, 1, eaAddr)
  1013. END
  1014. ELSIF (op >= 90H) AND (op <= 97H) THEN
  1015. k := op - 90H ;
  1016. IF k # 0 THEN (* 90 is NOP *)
  1017. v := AX ;
  1018. AX := GetReg (k, 1) ;
  1019. PutReg (k, 1, v)
  1020. END
  1021. ELSIF op = 98H THEN (* CBW: sign-extend AL into AX *)
  1022. IF (AX MOD 256) >= 80H THEN
  1023. AX := 65280 + (AX MOD 256)
  1024. ELSE
  1025. AX := AX MOD 256
  1026. END
  1027. ELSIF op = 99H THEN (* CWD: sign-extend AX into DX *)
  1028. IF AX >= 8000H THEN DX := 0FFFFH ELSE DX := 0 END
  1029. ELSIF (op = 0F5H) OR (op = 0F8H) OR (op = 0F9H) THEN
  1030. (* CMC / CLC / STC. The real arithmetic and the decimal conversion
  1031. toggle the carry across multi-word shifts; bootcom's handler also
  1032. uses CLC/STC, though those are outside the guest's view. *)
  1033. IF op = 0F5H THEN CF := NOT CF
  1034. ELSIF op = 0F8H THEN CF := FALSE
  1035. ELSE CF := TRUE
  1036. END
  1037. ELSIF op = 09FH THEN (* LAHF: flags -> AH *)
  1038. (* PF and AF are deliberately not maintained; the runtime's LAHF/SAHF
  1039. pair only needs CF/ZF/SF, which are the bits it round-trips. *)
  1040. v := 0 ;
  1041. IF CF THEN v := v + 1 END ;
  1042. IF ZF THEN v := v + 64 END ;
  1043. IF SF THEN v := v + 128 END ;
  1044. AX := (AX DIV 256) * 256 + v
  1045. ELSIF op = 09EH THEN (* SAHF: AH -> flags *)
  1046. v := AX DIV 256 ;
  1047. CF := (v MOD 2) = 1 ;
  1048. ZF := ((v DIV 64) MOD 2) = 1 ;
  1049. SF := v >= 128
  1050. ELSIF (op >= 0A0H) AND (op <= 0A3H) THEN
  1051. d := Fetch16 () ; (* moffs: DS is 0, checked in Run86 *)
  1052. CASE op OF
  1053. | 0A0H : AX := (AX DIV 256) * 256 + mem [d]
  1054. | 0A1H : AX := mem [d] + 256 * mem [(d + 1) MOD 65536]
  1055. | 0A2H : mem [d] := AX MOD 256
  1056. ELSE mem [d] := AX MOD 256 ;
  1057. mem [(d + 1) MOD 65536] := (AX DIV 256) MOD 256
  1058. END
  1059. ELSIF (op >= 0B0H) AND (op <= 0BFH) THEN
  1060. IF op < 0B8H THEN
  1061. PutReg (op - 0B0H, 0, Fetch8 ())
  1062. ELSE
  1063. PutReg (op - 0B8H, 1, Fetch16 ())
  1064. END
  1065. ELSIF op = 0C3H THEN (* RET *)
  1066. IP := Pop ()
  1067. ELSIF op = 0C9H THEN (* LEAVE: SP := BP; BP := POP *)
  1068. SP := BP ;
  1069. BP := Pop ()
  1070. ELSIF op = 0CDH THEN (* INT *)
  1071. n := Fetch8 () ;
  1072. IF n = 21H THEN
  1073. DoInt21 ()
  1074. ELSIF n = 10H THEN
  1075. DoInt10 ()
  1076. ELSE
  1077. Fault ("only INT 21h and INT 10h are provided; this is not a real-mode machine")
  1078. END
  1079. ELSIF op = 0E2H THEN (* LOOP: CX is not a flag *)
  1080. d := Fetch8 () ;
  1081. CX := (CX + 65535) MOD 65536 ;
  1082. IF CX # 0 THEN
  1083. IP := (IP + SE8 (d)) MOD 65536
  1084. END
  1085. ELSIF op = 0E3H THEN (* JCXZ *)
  1086. d := Fetch8 () ;
  1087. IF CX = 0 THEN
  1088. IP := (IP + SE8 (d)) MOD 65536
  1089. END
  1090. ELSIF op = 0E8H THEN (* CALL rel16 *)
  1091. d := Fetch16 () ;
  1092. Push (IP) ;
  1093. IP := (IP + d) MOD 65536
  1094. ELSIF op = 0E9H THEN (* JMP rel16 *)
  1095. d := Fetch16 () ;
  1096. IP := (IP + d) MOD 65536
  1097. ELSIF op = 0EBH THEN (* JMP rel8 *)
  1098. d := Fetch8 () ;
  1099. IP := (IP + SE8 (d)) MOD 65536
  1100. ELSIF (op >= 0D0H) AND (op <= 0D3H) THEN
  1101. w := op MOD 2 ;
  1102. IF op < 0D2H THEN
  1103. cnt := 1
  1104. ELSE
  1105. cnt := CX MOD 32
  1106. END ;
  1107. DoModRM (w) ;
  1108. DoShiftGroup (w, cnt)
  1109. ELSIF op = 0FEH THEN
  1110. (* INC/DEC r/m8. The 8-bit form of the 40-4F group; realdec uses
  1111. INC AL / DEC AL and the set-index path uses INC/DEC [BX]. CF is
  1112. preserved, as on the 8086, and the other flags come from the result. *)
  1113. DoModRM (0) ;
  1114. k := rmReg ;
  1115. res := RmRd (0) ;
  1116. cfSave := CF ;
  1117. IF k = 0 THEN
  1118. OFl := res = 7FH ;
  1119. res := (res + 1) MOD 256
  1120. ELSIF k = 1 THEN
  1121. OFl := res = 80H ;
  1122. res := (res + 255) MOD 256
  1123. ELSE
  1124. Fault ("FE group: only INC (/0) and DEC (/1) exist")
  1125. END ;
  1126. RmWr (res, 0) ;
  1127. SetZSF (res, 0) ;
  1128. CF := cfSave
  1129. ELSIF (op = 0F6H) OR (op = 0F7H) THEN
  1130. DoModRM (op - 0F6H) ;
  1131. DoUnaryGroup (op - 0F6H)
  1132. ELSIF op = 0FFH THEN
  1133. DoModRM (1) ;
  1134. DoFFGroup
  1135. ELSE
  1136. WrS ("exec86: unknown opcode ") ;
  1137. WrHex (op) ;
  1138. WrS (" at ") ;
  1139. WrHex (faultIP) ;
  1140. WrErr1 (CHR (10)) ;
  1141. Fault ("unknown opcode")
  1142. END
  1143. END Step ;
  1144. (* ------------------------------------------------------------------ public *)
  1145. PROCEDURE Clear86 ;
  1146. VAR i : CARDINAL ;
  1147. BEGIN
  1148. FOR i := 0 TO 65535 DO
  1149. mem [i] := 0
  1150. END ;
  1151. AX := 0 ; BX := 0 ; CX := 0 ; DX := 0 ;
  1152. SI := 0 ; DI := 0 ; BP := 0 ;
  1153. SP := 0FFFEH ; (* bootcom's SS:SP = 0000:FFFE *)
  1154. CS := 0 ; DS := 0 ; ES := 0 ; SS := 0 ;
  1155. IP := LoadAt ; (* bootcom's JMP 0000:0100 *)
  1156. CF := FALSE ; ZF := FALSE ; SF := FALSE ; OFl := FALSE ;
  1157. halted := FALSE ;
  1158. haltCode := 0 ;
  1159. isFault := FALSE ;
  1160. faultIP := LoadAt ;
  1161. loadHi := LoadAt ;
  1162. stepCnt := 0 ;
  1163. eaAddr := 0 ; eaReg := 0 ; eaIsReg := FALSE ; rmReg := 0
  1164. END Clear86 ;
  1165. PROCEDURE Poke86 (addr, value : CARDINAL) ;
  1166. BEGIN
  1167. IF addr > 65535 THEN
  1168. faultIP := IP ;
  1169. Fault ("Poke address is outside the 64 KB machine")
  1170. ELSE
  1171. mem [addr] := value MOD 256 ;
  1172. IF addr >= loadHi THEN
  1173. loadHi := addr + 1
  1174. END
  1175. END
  1176. END Poke86 ;
  1177. PROCEDURE Run86 (VAR exitCode : CARDINAL; VAR steps : LONGCARD) : CARDINAL ;
  1178. VAR cap : LONGCARD ;
  1179. BEGIN
  1180. exitCode := 0 ;
  1181. steps := 0 ;
  1182. isFault := FALSE ;
  1183. halted := FALSE ;
  1184. cap := VAL (LONGCARD, MaxSteps) ;
  1185. LOOP
  1186. IF halted THEN
  1187. exitCode := haltCode ;
  1188. steps := stepCnt ;
  1189. RETURN 0
  1190. END ;
  1191. IF isFault THEN
  1192. steps := stepCnt ;
  1193. RETURN 1
  1194. END ;
  1195. IF stepCnt >= cap THEN
  1196. faultIP := IP ;
  1197. WrS ("exec86: step limit ") ;
  1198. WrDec (MaxSteps) ; (* runaway guard, Exec86.def *)
  1199. WrS (" reached at IP=") ;
  1200. WrHex (IP) ;
  1201. WrErr1 (CHR (10)) ;
  1202. steps := stepCnt ;
  1203. RETURN 2
  1204. END ;
  1205. (* Checked before every step, not once at the start: the machine is
  1206. 64 KB flat, so a non-zero segment would silently alias onto the
  1207. same memory instead of faulting. *)
  1208. IF (CS # 0) OR (DS # 0) OR (ES # 0) OR (SS # 0) THEN
  1209. faultIP := IP ;
  1210. Fault ("a segment register is not zero; this machine is 64 KB flat") ;
  1211. steps := stepCnt ;
  1212. RETURN 1
  1213. END ;
  1214. IF (IP < LoadAt) OR (IP >= loadHi) THEN
  1215. faultIP := IP ;
  1216. Fault ("execution left the loaded image") ;
  1217. steps := stepCnt ;
  1218. RETURN 1
  1219. END ;
  1220. Step () ;
  1221. stepCnt := stepCnt + 1
  1222. END
  1223. END Run86 ;
  1224. END Exec86.