x86 is the general architecture, eight registers (A*, B*, C*, D*, SI, DI, SP,
BP), one code segment / instruction pointer combination (CS:IP) and three to
five data segments (DS, ES, SS, FS, GS), and flags, and if you choose to go
there, a relatively complex stack-based floating point unit with eight STx
80-bit "registers" which support 32-bit, 64-bit, 80-bit floating point, as
well as 16-bit, 32-bit, 64-bit, and 80-bit binary coded decimal integer
values.
It is actually a very easy processor to understand in those concepts. It
becomes complex in the details of the specifics can become overwhelming
when you get into virtual x86 modes, or any of the protected modes from
80286 and up.
it can also be made easier in another way:
if a person implements a variant which *only* does 32-bit protected mode
with flat addressing, and ignores most OS level functionality (this can
be viable for an interpreter which does not need to run a full OS, say
because all the OS system calls are handled in native code).
oddly, in this case, a large part of the opcode map is left empty as well.
though, x86 does have a big drawback for interpreters:
there is no real good way to really gloss over word-size / endianess /
memory layout / ... issues, and apart from "hey, I can run C code
compiled with GCC or similar in this interpreter", there isn't a whole
lot of gain.
the constraints of x86 in this sense make it not particularly viable for
interpreters which operate in the same address space as the host program
(and directly share data-structures, ...).
instead, effectively, the interpreted program largely has to run off in
its own self-contained address space which does everything as the
compiled x86 code expects (may still be viable for some use-cases).
and, for a plain interpreter, it is still a fair bit more hairy and
complex than a typical bytecode format.
if the goal is to have an interpreter, say, to run a scripting language
or similar, a person is probably better off with a more specialized
bytecode design.
IME, for bytecode, stack machine designs are fairly popular and pure
interpreters for them are generally simpler to implement (and generate
code for).
if performance is more the goal (for a plain C interpreter), or the
design is more focused on JIT compilation (*), a "register machine"
design can offer some advantages, though granted in many regards is a
little more complicated (some added complexity in terms of the ISA
design and compiler logic).
*: a register machine can allow better performance for a simple/naive JIT.
it doesn't matter as much for a more advanced JIT, but then basically
one is climbing great walls of complexity for slight gains in performance.
the "easy case" here is basically to use a statically-typed register
machine as the IR, at which point one can produce output from a naive
JIT roughly on parity with unoptimized C.
IME, a naive JIT for a stack-machine will be about 2x-3x slower than
unoptimized C.
now as for dynamic types... well, this is its own big-complex issues.
simple answer: actually using dynamic types at run-time: very expensive.
so, there tends to be some amount of taking dynamically-typed input
languages and compiling them into a statically-typed form at the IR level.
though, for simple if albeit slow languages and interpreters, dynamic
types can work well.