Summary: M68k emulator
This is a summary - basically cut and paste - of the "m68k emulator" thread
on the aros-dev mailing list during Februari 2001.
AROS is pretty stable and pretty complete too, as far as I can see.
There are tons of software out of there that are waiting to be
executed under our wonderful AmigaOS(tm) replacement, so why don't we
allow them to have fun? :)
- Emulator to be more compatible with Amiga computers.
- Amiga computers have special custom chips.
- Amiga computers uses 68k CPUs.
- Allowing native Amiga applications running on AROS/i386 etc.
- Emulator for big-endian and little-endian machines.
- Software using the hardware (games).
- Software depending on Amiga 500 timings.
- Software using bitplane formats for graphics.
- System-friendly software which uses features which are not easy to implement
on other systems (copper, own viewports, 880K floppy disks).
- Mostly system-friendly software which uses some hardware 'hacks', like macros
to disable interrupts/display.
- System-friendly software only avaible in binary format.
- System-friendly software avaible as sourcecode.
- Interpreter
- JIT
- Compiler (compiling of whole executable before start)
- It should be easy to use the system on different hardware (CPUs).
- Only 68k binaries.
- AROS in native code of machine
- When UAE calls the setup routines of AROS, AROS must disable the emulation
of all these parts of UAE that are not necessary (video chips, for example)
and do these things directly.
- When all chips have been disabled, we can switch of the code that tries to
synchronize the virtual chips with the virtual CPU.
- Rigth now, with the picasso emulation, all that you need to emulate are the
cia's and paula. Is not that big effort. After the AHI emulation is finished
it'll be necessary only the cia emulation.
Example:
- m68k code calls OpenLibrary()
- UAE read SysBase from virtual address 0x4
- SysBase must point to real SysBase in AROS (which is always outside of UAE's
virtual address space).
I see no way to make UAE work inside AROS under the assumption that UAE
directly calls something in the AROS kernel.
You must prevent the chipset from running too fast. You could do this by using
a timer so that the chipset stops when it doesn't need to run, but you would
need a very high resolution timer, just like the cia's one, and not all
computers have got it, specially the PC's. So, once again, you'll have to
synchronize the 68k with the chipset.
By running native code inside the 68k emulator you stop the emulation. The
multitasking stops, the chipset emulation stops, ALL stop but the native code.
It's not faster. Look at how picasso can slow down the whole emulation: it's
coded using such a technique you mentioned.
How do we allow native AROS code to call 68k code without knowing it's 68k
code?
Hooks.
Recompilation of 68k code? We could do that in the same way as the
AmigaDE's VP does, the only difference would be that our VP code is the 68k
code.
We could do this only once and then store the translated software in a sort
of cache. We could implement performance tracking, so that the code could
get more optimized every time it's executed. We could use, in order to do
this, a modified version of the UAE JIT compiler, with the help of the
author, of course.
Last year I spent about three months seeing how possible this was and wrote a
small program that would convert 68K instructions into a C type language
(sort of intermediate between C and 68K ASM). Then wrote another progam that
would convert my intermediate lanugage into whatever code.
The big problem was that AmigaOS executable files are in some kind of weird
structure that I don't really know with strange headers etc... secondly the
code always assumes that it knows the hardware it's running on (with AROS
this is not true), thirdly the code generated was always poor (large and
slow executing).
My solution was to forget about using a translator. Instead I started on a
Heuristic Amiga simulator, which would run the code and try and figure out
what the code was trying to do then generate code (probably C) to do the same.
The Amiga hardware could be changed for OS calls, so there would be no
hardware problems. I am a chemist, not a computer scientist so I found the
whole thing too hard to do and by this time my A1200 was failing so I gave up.
> [...] how do you handle the case in wich the 68k program tells to the OS
> the address of a 68k routine to be executed by the OS later on?
> The only viable solution that I see is the translation on load.
My solution to such a problem was to use the MMU to trap access to the Amiga
hardware address range (about 2meg...) and then emulate the registers
elsewhere in memory (i.e. an interupt with a handler to read the psuedo
registers and then deal with the data in them correctly, maybe using code
from UAE)
Besides, this could be handled using the mmu or the sigsegv exception
under linux, just like the UAE JIT does.
Another possibility is to use the MMU. Emulated code pointers point to
wrong memory in the native environment. The exception when this code is
called will (re)start the interpretor/translator.
For now this would require the use of the mmap() function and so the
emulator should be part of the kernel, exec's perhaps. Later on we
could use the mmu.hid, also to gain OS independency.
I know that JIT engines (UAE-JIT and Kaffe come to mind) use so-called
"trampolines" to detect when a not-yet-translated subroutine is called.
I'm sorry I don't know the details, but I'm sure the problem has been
solved already.
Endianess is a concern but Georg made a proposal how to run m68k files on a
little-endian processor and switch between emulated and native execution.
Hm, maybe some analyzer (disassembler) for m68k files would also be
interesting and sufficient at the beginning to see whether this can be done.
Can one for example avoid translating (= disassembling) hardcoded structures,
i.e.
For example, if an 68k program would call OpenWindow. It gets back a pointer
to a struct Window. If you only convert the Window Pointer to big-endian then
the 68k emu has the correct pointer, but the stuff in the structure is still
all little-endian. So if the 68k program reads something from this structure:
winwidth = window->Width;
then it will get the wrong value, because normal 68k emus are big-endian.
The other problem (endianess) has been solved with C++ templates
but they are not complete. We need someone with some time or
a lot of C++ knowledge to fill the missing gaps:
int i;
ULONG a, b;
a = b;
i = a;
In the first case, there is no need for endianess conversion and in the
second, there is. A C++ compile can figure this out but C can't (the
developer has to and he better shouldn't make any mistakes). I think
with this technique, we have these advantages:
- Compiler adds conversions only when necessary
- Compiler warns about missing conversions (-> template defines no
conversion). It simply cannot happen anymore that a necessary
conversion is missing.
- Much more readable.
Hmm :-
68k program:
struct NewWindow nw = {10,20,30, ...} /* DATA hunk */
win = OpenWindow(&nw);
Does this mean that the AROS native OpenWindow() when accessing
the NewWindow structure must be prepaired that the newwindow might
be big-endian -> it must use some macro to access the fields?
NewWindow is a small problem, as it is only used by OpenWindow().
But what for example about struct Gadget. There is tons of AROS native
code which deals (peeks inside structure) with gadgets.
Same with taglists.
Another problem:
68k program:
WORD var;
case IDCMP_MENUPICK:
menuitem = ItemAddress();
var = menuitem->MutualExclude;
Assuming the menuitem is native (little-endian), because it was created with
gadtools.library/CreateMenusA(). The 68k program above saves (stupid example)
the menuitem's MutualExclude in a WORD (16 bit) variable, but the menuitem's
MutualExclude field is actually LONG (32 bit). If you are lucky the 68k code
looks like this:
move.l menuitem,a0
move.l MutualExclude(a0),d0
move d0, var
but if you are unlucky it could look like this:
move.l menuitem,a0
move MutualExclude+2(a0),var
Assuming menuitem->MutualExclude contains 0x00001234, then in little-endian
it is:
0x34 0x12 0x00 00
So your 68k emu in the unlucky case from above, will put "0" into var,
instead of 0x1234.
To switch between native and emulated execution is not a problem. It could for
example be done with an illegal m68k code in the jump tables where the
emulator realizes that it has to execute i386 (or whatever) code now and has
to pass the arguments in the registers to the function.
The problem is the endianness of the m68k program and the environment it is
supposed to be running in. A NewWindow structure that is hard coded into the
m68k program cannot be passed as is to the OpenWindow() function for example.
All the data in the structure are in big-endian whereas the function expects
them to be in little-endian. So you would have to swap all data before you
pass it to the function and swap it back when it returns from the
OpenWindow(). Then what is returned by the OpenWindow() function is a pointer
to a Window structure where all data are in little-endian but here you must
not swap the content since the system needs the content to be alright for its
own purposes.
The endianness really is the problem and I really also believe that AROS can
have an emulator on a big-endian (!) machine like PowerPC; that would work
just fine.
- Sometimes it works but often data and code is not seperated so it is not
easy to decide what is a 68k command and what is data.
- Some assumption about the compiler, compiler version and compiler options
could be helpful.
I just finished an "intelligent" disassembler specialized into KickStart
disassembly. The way it works is really simple : it follows the code and
when an opcode like JSR, BSR, (cp)Bcc or (cp)DBcc is found, the address is
saved in a stack. When a JMP or BRA is found, the disassembler's PC is
loaded with the new address. And, when an RTS or RTE is found, the PC is
loaded with the stack.
Digitial's solution for this in their x86 emulator for Alpha CPU-s was to run
the code through a normal (step-by-step emulation) and "learning" what the
code did (i.e. converting it to Alpha code). When the code jumped to a place
which already had been converted, the Alpha code was used directly. Maybe
something we can learn from?
Who owns the code generated?
The copyright would probably lie with the original owner. Any automatic
translation of a file should, I guess, be considered a mere a copy, if one
stored in a rather unusual way.
An easy solution would be a 68k emulation for big-endian machines only, for
100% system-friendly software which don't use any hardware features of AmigaOS
but could be sped up with a JIT plus kickstart/workbench in native format.
Then additional software could be supported with emulation of (parts) of the
Amiga hardware.
On little-endian machines an AROS version which internally always works with
big-endian could be a (slow?) solution.
|