Exec86.mod 34 KB

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