Welcome to the Lisa Emulator Project

[Why] [Top] [Lisa Docs] [Lisa Hardware Pics] [Lisa Emulator Status] [Lisa Emulator To do] [Lisa Links] [Downloads] Comments? Email me here
(just expunge the anti-$p4m text.)

Generator Meter Tool


Date: Mon, 1 Mar 2004 08:45:00 -0500

I've been running the tests for the last few days.  After a few weird runs
where some opcodes would crash, I believe I've fixed the issues involved
and got stable runs.

What I'm noticing so far is that there are a lot of differences between
Generator's execution and that of the 68040 - these are (so far) in the
condition register flags (or there are more bugs in the tester?)

I'm rerunning the tests again and trying to suppress some of the redundant
output (i.e. turning off most of the CCR tests after the first iteration)
because I fed the output of the tests to gzip -9 and even so managed to
fill up all the disk on the NeXT - all of it that I've seen being in the
CCR bits...  (It's a 400mb disk, with only 35mb free.)  I may run the tests
over ssh to another machine if it still goes over...

I'm sure that the test results will be too large to send by email, so I'll
put them on my web server and provide urls to them in my next email...

It looks like this:

The text after the broken opcode either spits out deltad0, deltad1, or
deltaflags.  This text is useful for searching (with grep, etc...)  I also
put asterisks after the affected fields on the GEN line for easy spotting
by eye when scrolling...  The number in parens is the numeric (decimal)
value of the condition code register, the d0/d1 registers are in hex.

The rows are the input registers to the opcode (d0,d1) and CCR before the
opcode.  The d0,d1,CCR registers after executing on the 040, and after
executing on Generator.  (The delta's are of course between the outputs of
the 040 and generator.)

Testing divs
broken opcode: divs     deltaflags
before d0=00000001    d1=00000000    CCR=..... (0)
M68K   d0=00000001    d1=00000000    CCR=..Z.. (4)
GEN    d0=00000001    d1=00000000    CCR=..... * (8192)

broken opcode: divs     deltaflags
before d0=00000001    d1=00000000    CCR=....C (1)
M68K   d0=00000001    d1=00000000    CCR=..Z.. (4)
GEN    d0=00000001    d1=00000000    CCR=..... * (8192)

broken opcode: divs     deltaflags
before d0=00000001    d1=00000000    CCR=...V. (2)
M68K   d0=00000001    d1=00000000    CCR=..Z.. (4)
GEN    d0=00000001    d1=00000000    CCR=..... * (8192)

broken opcode: divs     deltaflags
before d0=00000001    d1=00000000    CCR=...VC (3)
M68K   d0=00000001    d1=00000000    CCR=..Z.. (4)
GEN    d0=00000001    d1=00000000    CCR=..... * (8192)

broken opcode: divs     deltaflags
before d0=00000001    d1=00000000    CCR=..Z.. (4)
M68K   d0=00000001    d1=00000000    CCR=..Z.. (4)
GEN    d0=00000001    d1=00000000    CCR=..... * (8192)


Date: Tue, 24 Feb 2004 13:20:00 -0500

NeXTStep 3.3 won't allow me to directly modify data within text (code) pages, so I wouldn't normally be able to do self modifying code, but there is another way to do it:

I've added a few assembly functions to return pointers and offsets within the assembly code function. Instead of calling asmtest directly, I do this:

There's still the issue of cache coherency, but I can get around this by repeating the above steps, THEN freeing the previous chunk of RAM. (By allocating a new block before freeing the old one I can guarantee that malloc won't give me back the same space, which might still be cached by the instruction cache with the old opcode.)

Thus, it is possible to do self modifying code on the MC68040. ;-D

(I'm not crazy about doing a lot of malloc/free cycles - I've seen problems with this under some older stdlib's/OS's... if it becomes an issue, I'll initially allocate a circular block of N function chunks, then rotate the fn pointer through them...)

Subject: Re: [gen] Generator 68000 emu bugs... testing against a real motorola cpu (long)

From: sunder

Date: Mon, 23 Feb 2004 09:59:04 -0500

To: Generator development list

James Ponder wrote:

Ray (Lisa em) recently emailed me about this.  I'd forgotten you'd emailed
and I had not got around to checking your comments.  I now have, and umm.
Yes.  You appear to be correct on all counts.  Whoops.

Thanks :)

Yup. :)  To clarify, I'm in the process of building an opcode tester by using a real Motorola processor.

So if you've got any reservations about certain opcodes, please let me know so that I can test them.  With this framework, I can't do certain things - like test branching or memory accesses, but I can test thing like rotates, shifts, and other bit/arithmetic operations.

The Idea:

I've got a ton of bugs in my Lisa emulator (otherwise I would have released it), so I want to verify that the Generator core is doing the right thing for all opcodes so as to eliminate it out of the pool of possible bugs. Otherwise, I'm not playing with a full deck. :)

The idea is to get the CPU core compiled on a m68k, then execute a single opcode with the three inputs (two register values + the CCR) on both the native 68k chip, and the Generator function, then compare the results in the output register and the CCR.  If there's any difference, complain, then change the input values and try again, etc...  :)

[I suppose, if I have enough energy/free time, I might do a NeXTStep port of Generator, but I'm not promising anything!]

A long time ago, I suggested that James test the Generator core against the UAE core.  (I believe v0.3 or some another ancient ver. had both cores.)

The testing was done by running against Genesis game ROMs on both cores and checking for differences.  I'm sure this located a lot of core bugs.  But, this test didn't account for two important cases:

1. it can only test the cpu core against another emulator core - what if there are CPU bugs in UAE?  So testing against a real 68k processor is important.

2. Genesis games are not going to use every possible 68000 opcode - their authors would optimize them for speed and size - and the games are not operating systems.  With the Lisa, this is a bigger issue as this machine needs to run several OS's (LisaOS, Xenix, MacWorks), which will use opcodes that games are almost guaranteed not to.  [i.e. backing out of opcodes when MMU exceptions occur, supervisor vs user mode opcodes, etc.  A lot of this is of course covered by Generator and now works, I'm just presenting it as an example for this argument.]

History of this sub-project (incase you're curious about the setup):
(You can skip this part if your eyes have already glazed over.)

In order to do these tests, I needed a 68k machine that could run Generator and let me write assembly code.  That implied that I needed something that was (relatively) fast, had enough disk, RAM, and could compile Generator. This also implied requiring GCC and therefore, to make my life easier, some form of Unix. :)

I originally tried to install OpenBSD on an old Mac IIsi - because it is a real Motorola 68k chip, but it lacks an FPU, so OBSD won't boot.  (SoftFPU let me run the installer - which took 13+ hours, but the OS wouldn't boot without a real FPU.)

I looked around for 68882 sources/FPU cards for the IIsi, but gave up. There are plenty of dirt cheap 040 Macs on ebay, but I'm not about to spend $30 to ship a $5 computer - which I wouldn't have too much use for afterward.

Rather than continue with NetBSD (which also needs an FPU) or Linux, I remembered that my NeXT slab has a very nice 68040, and a lot more RAM than the IIsi, plus it's already got the NeXTStep OS on it. It was also a good use of the slab - so far, it's been just a museum piece for me. :-D

Dumbass me, I hadn't turned on the NeXT in years and had forgotten its password...  Had to do the single user Command-~ trick to get in, then found, that oh, yeah, changing the passwd file isn't enough since once NetInfo runs, it's not going to use /etc/passwd...  So I had to launch NetInfo service by service until I could run the nu command.  After, I raided the peanuts archive to get a recent GCC.  I then had to struggle to get ld, as, etc. to work: it turns out, they're on the NeXT Developer CD...

After a few minutes of beating up on as (GNU assembler) and trying to figure out it's syntax (it's neither what's in the Motorola 68k programmer's ref, nor what Generator's disassembler displays) I cheated and used gcc -S on a small C program to figure it out.  :)

Implementation Details:
(Skip this if you don't care about 68k code.)

I'm passing two pointers to uint32 which get loaded into the d0,d1 registers as longs, and pointers to two uint16's which act as the CCR before the opcode and after. (The opcodes can be tested against word and byte operands of course - this is so that I don't have to build 3 different functions for the same test.)

I'll also probably limit the inputs to a few special cases to save some time - I don't intend to run the tests for months at a time. :) For bytes, the values could be: 0,1,2,4,8,16,32,64,128, 0x55, 0xaa, 0xff (and expansions thereof to words/longs, of course.)

For things like rotate/shifts, I could test all the values to shift by, especially those exceeding the size I'm shifting/rotating (0,16,32), to make sure they're handled properly... i.e. 0-35 (as long as they're legal as opcodes.)

Another set of permutations is the Condition Code Register input. (i.e. N,Z,V,X,C flags) which adds another 32 more permutations.

This method is currently also limited to testing one opcode per assemble/link cycle...

Making a more efficient tester:

I suppose that I could pad the opcode area I'm testing with say, 4 NOP's to provide enough padding for all opcodes, then get a pointer to the NOP area so that I could fill it with a new opcode to test.  But, this causes cache coherency issues... Not an problem on the original 68000's, but anything above an 030 (maybe even 020?) will, according to the Motorola manuals, break.

That implementation might be this:  I could put a label right before the NOP's, then add another pointer parameter which returns a pointer to the label.   Then, the higher level C code could just overwrite the tested opcode and pad the remaining bytes with NOP's.

If you've got any ideas as how to do this on an '040 safely, i.e. force flush the instruction cache when there's an opcode change from user mode - (I'm not in the microkernel's context after all!), let me know.

It might not even be at all possible as it depends on whether NeXTStep allows text pages to be modified.  i.e. the OS might mark them as read only. (This is more likely of modern security conscious OS's, but possible for simplifying page/vm management.)

Another problem might be this: if the page containing asmtest() gets swapped out to disk without the OS noticing the write and marking it as dirty - it would discard the change and load back the original.

Here's the actual asm test function:

[blackhole:ray:~/lisaem/68000-tester:29]$ more asmtest.s
*                                  Generator Meter                                     *
*                                                                                      *
*                         Copyright (C) 2004 Ray A. Arachelian                         *
*                                 All Rights Reserved                                  *
*                                                                                      *
*                                                                                      *
*   MC68000 Assembly Opcode Tester Routines.  These MC68000 instructions are to        *
*   be executed on a real Motorola 68040 machine (NeXTStep 3.3) at the same time       *
*   time that Generator CPU core code is executed with the same parameters.  The       *
*   condition code register and output registers are then compared in order to         *
*   detect emulation errors.                                                           *
*                                                                                      *


 C Prototype for this function call is:

   extern void asmtest(uint16 *ccrin, uint32 *reg1,
                       uint32 *reg2, uint16 *ccrout);

   word: &ccrin =a6@(8)  - condition code register before (in)
   long: ®1  =a6@(12) - d0 register (in/out)
   long: ®2  =a6@(16) - d1 register (in/out)
   long: &ccrout=a6@(20) - condition code register after (out)


        .align 1
.globl _asmtest

        pea a6@                      /*  Setup stack frame   */
        movel sp,a6
        movel a2,sp@-

        movel a6@(8),a2              /* Get pointer to ccrin */
        movel a6@(12),a0             /* Get pointer to reg1  */
        movel a6@(16),a1             /* Get pointer to reg2  */
        movel a6@(20),a3             /* Get pointer to ccrout*/

        movel a0@,d0                 /* Get reg1 into d0     */
        movel a1@,d1                 /* Get reg2 into d1     */
        movew a2@,d2                 /* Get CCRin            */

        movew d2,ccr                 /* copy it to CCR       */

        nop                          /* Will overwrite NOP's */
        nop                          /* with opcode to test  */

        movew ccr,d2                 /* Get new CCR value    */

        movel d0,a0@                 /* Save d0 into reg1    */
        movel d1,a1@                 /* Save d1 into reg2    */
        movew d2,a3@                 /* Save CCRout          */

        jra L2                       /* Return to C land     */
        .align 1
        movel sp@+,a2
        unlk a6

gen mailing list