Exec86.mod 36 KB

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