Exec86.mod 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  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. (* ------------------------------------------------------ F6/F7 unary group *)
  608. PROCEDURE DoUnaryGroup (w : CARDINAL) ;
  609. (* F6/F7, after DoModRM has run. reg selects:
  610. /0 /1 TEST /2 NOT /3 NEG /4 MUL /5 IMUL /6 DIV /7 IDIV *)
  611. VAR k, src, res, prod, q, remw : CARDINAL ;
  612. ma, mb, mq, mr, ldv, ldd, lq, lr : LONGINT ;
  613. neg : BOOLEAN ;
  614. BEGIN
  615. k := rmReg ;
  616. CASE k OF
  617. | 0, 1 : (* TEST r/m, imm *)
  618. IF w = 0 THEN src := Fetch8 () ELSE src := Fetch16 () END ;
  619. SetLogic (BAnd (RmRd (w), src, w), w)
  620. | 2 : (* NOT: no flags at all *)
  621. res := BNot (RmRd (w), w) ;
  622. RmWr (res, w)
  623. | 3 : (* NEG *)
  624. res := DoSub (0, RmRd (w), 0, w) ;
  625. RmWr (res, w)
  626. | 4 : (* MUL, unsigned *)
  627. src := RmRd (w) ;
  628. IF w = 1 THEN
  629. prod := AX * src ; (* max 65535^2 < 2^32 *)
  630. DX := prod DIV 65536 ;
  631. AX := prod MOD 65536 ;
  632. res := AX
  633. ELSE
  634. (* MUL r8 writes only AX: AL*src, high byte in AH, DX untouched.
  635. It is easy to write DX := 0 here out of a false tidiness. *)
  636. prod := (AX MOD 256) * src ;
  637. AX := prod MOD 65536 ;
  638. res := AX
  639. END ;
  640. (* CF and OF are the documented ones for MUL; SF and ZF are
  641. architecturally undefined and are set from the low result rather
  642. than left alone, so that they are at least deterministic. *)
  643. IF w = 1 THEN
  644. CF := DX # 0
  645. ELSE
  646. CF := prod >= 256
  647. END ;
  648. OFl := CF ;
  649. SetZSF (res, w)
  650. | 5 : (* IMUL, signed *)
  651. src := RmRd (w) ;
  652. IF w = 1 THEN
  653. ldd := S16 (AX) * S16 (src) ;
  654. lq := ldd
  655. ELSE
  656. ldd := S8 (AX MOD 256) * S8 (src) ;
  657. lq := ldd
  658. END ;
  659. (* The low word of a negative product is exactly lq MOD 65536, because
  660. ISO Modula-2's MOD always returns a non-negative remainder, which is
  661. the two's complement low word. The high word is lq DIV 65536 for
  662. the same reason: DIV floors, which is an arithmetic shift. *)
  663. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  664. IF w = 1 THEN
  665. DX := VAL (CARDINAL, (lq DIV VAL (LONGINT, 65536))
  666. MOD VAL (LONGINT, 65536)) ;
  667. CF := (lq < VAL (LONGINT, -32768))
  668. OR (lq > VAL (LONGINT, 32767)) ;
  669. res := AX
  670. ELSE
  671. CF := (lq < VAL (LONGINT, -128))
  672. OR (lq > VAL (LONGINT, 127)) ;
  673. res := AX
  674. END ;
  675. OFl := CF ;
  676. SetZSF (res, w)
  677. | 6 : (* DIV, unsigned *)
  678. src := RmRd (w) ;
  679. IF src = 0 THEN
  680. Fault ("divide by zero")
  681. ELSE
  682. IF w = 1 THEN
  683. prod := DX * 65536 + AX ;
  684. q := prod DIV src ;
  685. remw := prod MOD src ;
  686. IF q > 65535 THEN
  687. Fault ("DIV: quotient does not fit in AX")
  688. ELSE
  689. AX := q ;
  690. DX := remw ;
  691. res := AX
  692. END
  693. ELSE
  694. prod := AX MOD 65536 ;
  695. q := prod DIV src ;
  696. remw := prod MOD src ;
  697. IF q > 255 THEN
  698. Fault ("DIV: quotient does not fit in AL")
  699. ELSE
  700. AX := remw * 256 + q ;
  701. res := q
  702. END
  703. END ;
  704. IF NOT isFault THEN
  705. CF := FALSE ;
  706. OFl := FALSE ;
  707. SetZSF (res, w)
  708. END
  709. END
  710. ELSE (* /7 IDIV, signed *)
  711. src := RmRd (w) ;
  712. IF w = 1 THEN
  713. ldv := S16 (src) ;
  714. ldd := S16 (DX) * VAL (LONGINT, 65536) + VAL (LONGINT, AX)
  715. ELSE
  716. ldv := S8 (src) ;
  717. ldd := S8 (AX MOD 256)
  718. END ;
  719. IF ldv = VAL (LONGINT, 0) THEN
  720. Fault ("divide by zero")
  721. ELSE
  722. (* x86 IDIV truncates toward zero and gives the remainder the
  723. sign of the dividend. ISO Modula-2's DIV/MOD are Euclidean:
  724. the remainder is always non-negative (measured: (-7) MOD 2 = 1,
  725. 7 MOD (-2) = 1, (-7) DIV 2 = -4). So the division is redone on
  726. magnitudes, where floor and truncation coincide, and the signs
  727. are put back afterwards. *)
  728. IF ldd < VAL (LONGINT, 0) THEN ma := VAL (LONGINT, 0) - ldd
  729. ELSE ma := ldd END ;
  730. IF ldv < VAL (LONGINT, 0) THEN mb := VAL (LONGINT, 0) - ldv
  731. ELSE mb := ldv END ;
  732. mq := ma DIV mb ;
  733. mr := ma MOD mb ;
  734. neg := (ldd < VAL (LONGINT, 0)) # (ldv < VAL (LONGINT, 0)) ;
  735. IF neg THEN lq := VAL (LONGINT, 0) - mq ELSE lq := mq END ;
  736. IF ldd < VAL (LONGINT, 0) THEN lr := VAL (LONGINT, 0) - mr
  737. ELSE lr := mr END ;
  738. IF w = 1 THEN
  739. IF (lq < VAL (LONGINT, -32768)) OR (lq > VAL (LONGINT, 32767)) THEN
  740. Fault ("IDIV: quotient does not fit in AX")
  741. ELSE
  742. AX := VAL (CARDINAL, lq MOD VAL (LONGINT, 65536)) ;
  743. DX := VAL (CARDINAL, lr MOD VAL (LONGINT, 65536)) ;
  744. SetZSF (AX, 1)
  745. END
  746. ELSE
  747. IF (lq < VAL (LONGINT, -128)) OR (lq > VAL (LONGINT, 127)) THEN
  748. Fault ("IDIV: quotient does not fit in AL")
  749. ELSE
  750. (* IDIV r8 also overwrites both halves: AL := quotient,
  751. AH := remainder, so the old AH is gone. *)
  752. AX := VAL (CARDINAL, lr MOD VAL (LONGINT, 256)) * 256
  753. + VAL (CARDINAL, lq MOD VAL (LONGINT, 256)) ;
  754. SetZSF (VAL (CARDINAL, lq MOD VAL (LONGINT, 256)), 0)
  755. END
  756. END ;
  757. IF NOT isFault THEN
  758. CF := FALSE ;
  759. OFl := FALSE
  760. END
  761. END
  762. END
  763. END DoUnaryGroup ;
  764. (* --------------------------------------------------------- FF group (word) *)
  765. PROCEDURE DoFFGroup ;
  766. (* FF, after DoModRM has run with w = 1. *)
  767. VAR k, res : CARDINAL ;
  768. cfSave : BOOLEAN ;
  769. BEGIN
  770. k := rmReg ;
  771. CASE k OF
  772. | 0 : (* INC r/m: CF is preserved *)
  773. cfSave := CF ;
  774. res := DoAdd (RmRd (1), 1, 0, 1) ;
  775. CF := cfSave ;
  776. RmWr (res, 1)
  777. | 1 : (* DEC r/m: CF is preserved *)
  778. cfSave := CF ;
  779. res := DoSub (RmRd (1), 1, 0, 1) ;
  780. CF := cfSave ;
  781. RmWr (res, 1)
  782. | 2 : (* CALL near r/m *)
  783. Push (IP) ;
  784. IP := RmRd (1)
  785. | 4 : (* JMP near r/m *)
  786. IP := RmRd (1)
  787. | 6 : (* PUSH r/m *)
  788. Push (RmRd (1))
  789. ELSE
  790. Fault ("unsupported opcode in the FF group (far call/jump)")
  791. END
  792. END DoFFGroup ;
  793. (* ----------------------------------------------------------- one instruction *)
  794. PROCEDURE Step ;
  795. VAR op, w, f, alu, v, d, imm, res, n, k : CARDINAL ;
  796. cfSave : BOOLEAN ;
  797. BEGIN
  798. faultIP := IP ; (* what to report if we fault *)
  799. op := Fetch8 () ;
  800. IF (op = 06H) OR (op = 0EH) OR (op = 16H) OR (op = 1EH) THEN
  801. (* PUSH ES / CS / SS / DS. CS and SS are pushed by nothing in the
  802. corpus, but the four encodings are one instruction apart and
  803. leaving two of them out would be a gap nobody could explain. *)
  804. IF op = 06H THEN Push (ES)
  805. ELSIF op = 0EH THEN Push (CS)
  806. ELSIF op = 16H THEN Push (SS)
  807. ELSE Push (DS)
  808. END
  809. ELSIF (op = 07H) OR (op = 17H) OR (op = 1FH) THEN
  810. IF op = 07H THEN ES := Pop ()
  811. ELSIF op = 17H THEN SS := Pop ()
  812. ELSE DS := Pop ()
  813. END
  814. ELSIF (op = 26H) OR (op = 2EH) OR (op = 36H) OR (op = 3EH) THEN
  815. Fault ("segment override prefix: this interpreter is 64 KB flat")
  816. ELSIF op < 40H THEN
  817. (* The 00-3D family: eight ALU operations in six encodings each.
  818. `alu' is the group number, `f' the form.
  819. f = 0,1 op r/m, r f = 2,3 op r, r/m
  820. f = 4 op AL, imm8 f = 5 op AX, imm16
  821. f = 6,7 are the prefixes and DAA/DAS/AAA/AAS, all consumed above
  822. or unreachable, so reaching here is a real unknown. *)
  823. alu := (op DIV 8) MOD 8 ;
  824. f := op MOD 8 ;
  825. IF f <= 3 THEN
  826. w := f MOD 2 ;
  827. DoModRM (w) ;
  828. IF f <= 1 THEN
  829. res := Alu (alu, RmRd (w), GetReg (rmReg, w), w) ;
  830. IF alu # 7 THEN RmWr (res, w) END
  831. ELSE
  832. res := Alu (alu, GetReg (rmReg, w), RmRd (w), w) ;
  833. IF alu # 7 THEN PutReg (rmReg, w, res) END
  834. END
  835. ELSIF f = 4 THEN
  836. v := Fetch8 () ;
  837. res := Alu (alu, AX MOD 256, v, 0) ;
  838. IF alu # 7 THEN PutReg (0, 0, res) END
  839. ELSIF f = 5 THEN
  840. v := Fetch16 () ;
  841. res := Alu (alu, AX, v, 1) ;
  842. IF alu # 7 THEN PutReg (0, 1, res) END
  843. ELSE
  844. Fault ("unknown opcode in the ALU family")
  845. END
  846. ELSIF op < 50H THEN
  847. (* INC r16 / DEC r16. These do NOT affect CF, which is easy to lose:
  848. going through DoAdd sets it, so it is saved and restored. *)
  849. k := op - 40H ;
  850. cfSave := CF ;
  851. IF k < 8 THEN
  852. res := DoAdd (GetReg (k, 1), 1, 0, 1) ;
  853. PutReg (k, 1, res)
  854. ELSE
  855. res := DoSub (GetReg (k - 8, 1), 1, 0, 1) ;
  856. PutReg (k - 8, 1, res)
  857. END ;
  858. CF := cfSave
  859. ELSIF op < 60H THEN
  860. IF op < 58H THEN
  861. Push (GetReg (op - 50H, 1))
  862. ELSE
  863. PutReg (op - 58H, 1, Pop ())
  864. END
  865. ELSIF op < 70H THEN
  866. Fault ("opcode not implemented (60H-6FH is 80186 and later)")
  867. ELSIF op < 80H THEN
  868. d := Fetch8 () ;
  869. IF Cond (op - 70H) THEN
  870. IP := (IP + SE8 (d)) MOD 65536
  871. END
  872. ELSIF (op = 80H) OR (op = 81H) OR (op = 83H) THEN
  873. IF op = 80H THEN w := 0 ELSE w := 1 END ;
  874. DoModRM (w) ;
  875. k := rmReg ;
  876. IF op = 83H THEN
  877. imm := SE8 (Fetch8 ()) (* sign-extended to the full width *)
  878. ELSIF w = 0 THEN
  879. imm := Fetch8 ()
  880. ELSE
  881. imm := Fetch16 ()
  882. END ;
  883. res := Alu (k, RmRd (w), imm, w) ;
  884. IF k # 7 THEN RmWr (res, w) END
  885. ELSIF (op = 84H) OR (op = 85H) THEN
  886. (* TEST r/m, r. Same AND and same flag rule as F6/F7 /0; only the
  887. encoding differs, and leaving it out while having the other one
  888. would be a gap with no reason behind it. *)
  889. w := op - 84H ;
  890. DoModRM (w) ;
  891. SetLogic (BAnd (RmRd (w), GetReg (rmReg, w), w), w)
  892. ELSIF (op = 86H) OR (op = 87H) THEN
  893. w := op - 86H ;
  894. DoModRM (w) ;
  895. v := RmRd (w) ;
  896. RmWr (GetReg (rmReg, w), w) ;
  897. PutReg (rmReg, w, v)
  898. ELSIF (op >= 88H) AND (op <= 8BH) THEN
  899. w := op MOD 2 ;
  900. DoModRM (w) ;
  901. IF op >= 8AH THEN
  902. PutReg (rmReg, w, RmRd (w))
  903. ELSE
  904. RmWr (GetReg (rmReg, w), w)
  905. END
  906. ELSIF (op = 0C6H) OR (op = 0C7H) THEN
  907. (* MOV r/m,imm. The compiler emits C6 06 lo hi imm for the CRT
  908. attribute byte; C7 is the 16-bit form. The ModRM reg field is
  909. reserved (must be 0) and a register destination is not this opcode
  910. (8A/8B or B0+/B8+ are). *)
  911. w := op - 0C6H ;
  912. DoModRM (w) ;
  913. IF eaIsReg THEN
  914. Fault ("MOV r/m,imm needs a memory destination")
  915. ELSE
  916. IF w = 0 THEN
  917. v := Fetch8 ()
  918. ELSE
  919. v := Fetch16 ()
  920. END ;
  921. RmWr (v, w)
  922. END
  923. ELSIF op = 8DH THEN
  924. DoModRM (1) ;
  925. IF eaIsReg THEN
  926. Fault ("LEA with a register operand is not an address")
  927. ELSE
  928. PutReg (rmReg, 1, eaAddr)
  929. END
  930. ELSIF (op >= 90H) AND (op <= 97H) THEN
  931. k := op - 90H ;
  932. IF k # 0 THEN (* 90 is NOP *)
  933. v := AX ;
  934. AX := GetReg (k, 1) ;
  935. PutReg (k, 1, v)
  936. END
  937. ELSIF op = 98H THEN (* CBW: sign-extend AL into AX *)
  938. IF (AX MOD 256) >= 80H THEN
  939. AX := 65280 + (AX MOD 256)
  940. ELSE
  941. AX := AX MOD 256
  942. END
  943. ELSIF op = 99H THEN (* CWD: sign-extend AX into DX *)
  944. IF AX >= 8000H THEN DX := 0FFFFH ELSE DX := 0 END
  945. ELSIF (op >= 0A0H) AND (op <= 0A3H) THEN
  946. d := Fetch16 () ; (* moffs: DS is 0, checked in Run86 *)
  947. CASE op OF
  948. | 0A0H : AX := (AX DIV 256) * 256 + mem [d]
  949. | 0A1H : AX := mem [d] + 256 * mem [(d + 1) MOD 65536]
  950. | 0A2H : mem [d] := AX MOD 256
  951. ELSE mem [d] := AX MOD 256 ;
  952. mem [(d + 1) MOD 65536] := (AX DIV 256) MOD 256
  953. END
  954. ELSIF (op >= 0B0H) AND (op <= 0BFH) THEN
  955. IF op < 0B8H THEN
  956. PutReg (op - 0B0H, 0, Fetch8 ())
  957. ELSE
  958. PutReg (op - 0B8H, 1, Fetch16 ())
  959. END
  960. ELSIF op = 0C3H THEN (* RET *)
  961. IP := Pop ()
  962. ELSIF op = 0C9H THEN (* LEAVE: SP := BP; BP := POP *)
  963. SP := BP ;
  964. BP := Pop ()
  965. ELSIF op = 0CDH THEN (* INT *)
  966. n := Fetch8 () ;
  967. IF n = 21H THEN
  968. DoInt21 ()
  969. ELSIF n = 10H THEN
  970. DoInt10 ()
  971. ELSE
  972. Fault ("only INT 21h and INT 10h are provided; this is not a real-mode machine")
  973. END
  974. ELSIF op = 0E2H THEN (* LOOP: CX is not a flag *)
  975. d := Fetch8 () ;
  976. CX := (CX + 65535) MOD 65536 ;
  977. IF CX # 0 THEN
  978. IP := (IP + SE8 (d)) MOD 65536
  979. END
  980. ELSIF op = 0E3H THEN (* JCXZ *)
  981. d := Fetch8 () ;
  982. IF CX = 0 THEN
  983. IP := (IP + SE8 (d)) MOD 65536
  984. END
  985. ELSIF op = 0E8H THEN (* CALL rel16 *)
  986. d := Fetch16 () ;
  987. Push (IP) ;
  988. IP := (IP + d) MOD 65536
  989. ELSIF op = 0E9H THEN (* JMP rel16 *)
  990. d := Fetch16 () ;
  991. IP := (IP + d) MOD 65536
  992. ELSIF op = 0EBH THEN (* JMP rel8 *)
  993. d := Fetch8 () ;
  994. IP := (IP + SE8 (d)) MOD 65536
  995. ELSIF (op = 0F6H) OR (op = 0F7H) THEN
  996. DoModRM (op - 0F6H) ;
  997. DoUnaryGroup (op - 0F6H)
  998. ELSIF op = 0FFH THEN
  999. DoModRM (1) ;
  1000. DoFFGroup
  1001. ELSE
  1002. WrS ("exec86: unknown opcode ") ;
  1003. WrHex (op) ;
  1004. WrS (" at ") ;
  1005. WrHex (faultIP) ;
  1006. WrErr1 (CHR (10)) ;
  1007. Fault ("unknown opcode")
  1008. END
  1009. END Step ;
  1010. (* ------------------------------------------------------------------ public *)
  1011. PROCEDURE Clear86 ;
  1012. VAR i : CARDINAL ;
  1013. BEGIN
  1014. FOR i := 0 TO 65535 DO
  1015. mem [i] := 0
  1016. END ;
  1017. AX := 0 ; BX := 0 ; CX := 0 ; DX := 0 ;
  1018. SI := 0 ; DI := 0 ; BP := 0 ;
  1019. SP := 0FFFEH ; (* bootcom's SS:SP = 0000:FFFE *)
  1020. CS := 0 ; DS := 0 ; ES := 0 ; SS := 0 ;
  1021. IP := LoadAt ; (* bootcom's JMP 0000:0100 *)
  1022. CF := FALSE ; ZF := FALSE ; SF := FALSE ; OFl := FALSE ;
  1023. halted := FALSE ;
  1024. haltCode := 0 ;
  1025. isFault := FALSE ;
  1026. faultIP := LoadAt ;
  1027. loadHi := LoadAt ;
  1028. stepCnt := 0 ;
  1029. eaAddr := 0 ; eaReg := 0 ; eaIsReg := FALSE ; rmReg := 0
  1030. END Clear86 ;
  1031. PROCEDURE Poke86 (addr, value : CARDINAL) ;
  1032. BEGIN
  1033. IF addr > 65535 THEN
  1034. faultIP := IP ;
  1035. Fault ("Poke address is outside the 64 KB machine")
  1036. ELSE
  1037. mem [addr] := value MOD 256 ;
  1038. IF addr >= loadHi THEN
  1039. loadHi := addr + 1
  1040. END
  1041. END
  1042. END Poke86 ;
  1043. PROCEDURE Run86 (VAR exitCode : CARDINAL; VAR steps : LONGCARD) : CARDINAL ;
  1044. VAR cap : LONGCARD ;
  1045. BEGIN
  1046. exitCode := 0 ;
  1047. steps := 0 ;
  1048. isFault := FALSE ;
  1049. halted := FALSE ;
  1050. cap := VAL (LONGCARD, MaxSteps) ;
  1051. LOOP
  1052. IF halted THEN
  1053. exitCode := haltCode ;
  1054. steps := stepCnt ;
  1055. RETURN 0
  1056. END ;
  1057. IF isFault THEN
  1058. steps := stepCnt ;
  1059. RETURN 1
  1060. END ;
  1061. IF stepCnt >= cap THEN
  1062. faultIP := IP ;
  1063. WrS ("exec86: step limit ") ;
  1064. WrDec (MaxSteps) ; (* runaway guard, Exec86.def *)
  1065. WrS (" reached at IP=") ;
  1066. WrHex (IP) ;
  1067. WrErr1 (CHR (10)) ;
  1068. steps := stepCnt ;
  1069. RETURN 2
  1070. END ;
  1071. (* Checked before every step, not once at the start: the machine is
  1072. 64 KB flat, so a non-zero segment would silently alias onto the
  1073. same memory instead of faulting. *)
  1074. IF (CS # 0) OR (DS # 0) OR (ES # 0) OR (SS # 0) THEN
  1075. faultIP := IP ;
  1076. Fault ("a segment register is not zero; this machine is 64 KB flat") ;
  1077. steps := stepCnt ;
  1078. RETURN 1
  1079. END ;
  1080. IF (IP < LoadAt) OR (IP >= loadHi) THEN
  1081. faultIP := IP ;
  1082. Fault ("execution left the loaded image") ;
  1083. steps := stepCnt ;
  1084. RETURN 1
  1085. END ;
  1086. Step () ;
  1087. stepCnt := stepCnt + 1
  1088. END
  1089. END Run86 ;
  1090. END Exec86.