| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- DEFINITION MODULE Exec86 ;
- (* Exec86 -- an in-process 8086 interpreter.
- This is what the shell's `R` key runs: the compiled image is copied into
- the interpreter's own 64 KB and executed here, with no DOS, no loader and
- no emulator outside this program. TP3's `R` does the same thing - it runs
- the generated code in place in the same 64 KB - so this is also the shape
- the original had.
- The reason to want one at all is documented in SUMMARY.md: qemu-system-i386
- cannot be an 8086 oracle, because its lowest CPU model is a 486, where the
- opcodes that were the `0F 8x' bug are ordinary instructions. An
- interpreter written against the 8086's own reference and then required to
- agree with qemu byte-for-byte on the same images is a second, independent
- execution oracle - and the one that can actually see an 8086-only fault.
- The interface is deliberately three calls: zero the machine, poke the
- image in, run it. Everything the guest can do outside its own code - the
- INT 21h services, where its input comes from, where its output goes - is
- inside, because those are part of "running it", not part of setting it up.
- The names carry an 86 suffix, which reads as redundant inside Exec86 and
- is not. ISO Modula-2 has no import renaming - gm2 -fiso rejects
- `FROM M IMPORT x AS y' outright - and an import cannot be scoped to a
- procedure either, so every module that imports Exec86 shares one flat
- namespace with everything else it imports. Shell needs TextBuf.Clear,
- Editor.Run, Exec86.Clear and Exec86.Run at the same time, and the first
- two were spoken for by modules that predate this one. This is the module
- that moved: Clear86, Poke86, Run86.
- NOTE, because Runtime.def records the same trap: this .def is
- HAND-MAINTAINED. gm2 resolves `FROM Exec86 IMPORT ...' against it and
- checks the implementation against it, but never rewrites it. An interface
- change means editing this file by hand in the same commit. Keep changes
- ADDITIVE where possible. *)
- PROCEDURE Clear86 ;
- (* Reset the machine to the state a DOS .COM starts in: all of memory zeroed,
- CS = DS = ES = SS = 0, IP = 0100H (where DOS puts a .COM), SP = 0FFFEH
- (the top of the segment, where DOS puts the stack), every general register
- zero, DF clear, every other flag clear.
- Zeroing the general registers is a CHOICE, not a fact about DOS: bootcom.s
- leaves them as the BIOS found them. All 31 fixtures are deterministic
- under qemu, so no guest of ours reads an undefined initial register - and
- this is the first place that would show up if one ever did. Memory below
- 0100H is left zero rather than holding bootcom's interrupt vector, because
- the interpreter services INT 21h itself and never looks at the vector. *)
- PROCEDURE Poke86 (addr, value : CARDINAL) ;
- (* Store one byte at flat address `addr' (value is taken modulo 256).
- Poking also records how far up memory has been written, and execution
- outside that region is a fault: a guest that jumps out of its own loaded
- image has gone somewhere it cannot come back from, and running on into
- whatever happens to be there is how a wild jump turns into a wild hang. *)
- PROCEDURE Run86 (VAR exitCode : CARDINAL; VAR steps : LONGCARD) : CARDINAL ;
- (* Execute until something stops it. Returns
- 0 the program terminated with INT 21h AH=4Ch, and exitCode is its AL
- 1 the interpreter faulted - a diagnostic is already on stderr
- 2 the step limit was reached; steps says how many
- `steps' is always the number of instructions actually executed.
- The step limit is a runaway guard, and it is a limit rather than a
- solution: a program that idles in a tight loop will reach it. It exists
- so that a broken image fails instead of never returning. *)
- END Exec86.
|