Giter Site home page Giter Site logo

dirkwhoffmann / moira Goto Github PK

View Code? Open in Web Editor NEW
98.0 10.0 11.0 320.02 MB

A Motorola 68000 emulator written in C++

Home Page: https://dirkwhoffmann.github.io/Moira

License: Other

C++ 29.56% C 61.54% CMake 0.12% TypeScript 8.74% Shell 0.04%
motorola-68000 cpu-emulator emulation

moira's People

Contributors

dirkwhoffmann avatar mithrendal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moira's Issues

68030 bus error exception frame format

Now I'm at the point where I need to create the bus error exception frame. Unfortunately, the user manual is very unspecific about what is really saved on the stack:

Bildschirmfoto 2022-09-22 um 20 53 40

The most important entry is obviously the PC, because this entry is used by the RTE instruction to jump back into the program code. It has to point at the offending instruction. But what about the others? Does anybody know what's behind all those "internal registers"? I haven't been able to find more detailed information about the exception frame format.

Low level bus signals

Hello,

Although this emulator claims to be cycle-accurate, it isn't possible to emulate a single clock cycle, let alone half a clock cycle.

For accurately representing bus transactions, it would be needed to emulate the rise and falling edges of the clock. For instance, the /DTACK bus signal may take many clock cycles to come down. Maybe you have a way around that at the moment by setting the clock cycle count via a method. The important thing is that data is not in the bus when the read16 method is called, and that's why /DTACK halts the CPU, but the read16 method cannot emulate that because it has to reply with a value.

I understand that this probably falls out of your scope, but I just wanted to share it; in case you want to aim for a more truly cycle-accurate emulation.

Thank you

Design decision: Use C++ exceptions to emulate address errors and bus errors

Background: In 68030+ CPUs every access to memory will require a call to translate to convert a logical address to a physical one. The problem is that any call to translate may result in a bus error which terminates the execution of the current instruction immediately. The situation is comparable with address errors. When I implemented address error support a while ago I had to blow up the code by passing around a lot of boolean flags and adding many If-statements to check those flags. Adding bus error support in a similar way would make the code much more ugly.

Therefore, I am thinking of using C++ exceptions to emulate address errors and bus errors. But before doing this, I’d like to sum up pros and cons. Here is my point of view (which might be wrong):

  1. Speed: Throwing and catching exceptions is costly. I think we can live with that because address errors and bus errors are very rare events. On the standard execution path, speed could even increase slightly, because functions such as readOp are no longer required to pass back a success flag. Code readability would also increase. I.e., statements such as

     if (!readOp<C, M, Byte>(ax, &ea1, &data1)) return;
    

    would simply look like this:

     readOp<C, M, Byte>(ax, &ea1, &data1);
    
  2. Memory: The C++ compiler has to add some amount of exception processing code to each function that might throw an exception. Since we have lots of instruction handlers due to heavy template usage, the resulting overhead might become an issue.

  3. Style: In theory, exceptions should only be used to handle error conditions. They should not be used as part of the normal program control flow. Address errors and bus errors are „virtual error conditions“ and as such part of the functionality of the program. Hence, one could argue that exceptions would be misused in this context. However, we have a special situation here, because address and bus errors are nothing else than exceptions built in hardware which makes them a perfect fit for being emulated by software exceptions.

Item 2 has the potential to be a show stopper. Hence, I think it’s best to postpone MMU stuff at the moment and to evaluate the cost of implementing address error handling via C++ exceptions first. If it doesn’t blow up the code too much, bus errors could be handled the same way.

MMU test programs needed

In this thread it was discussed to add MMU support to Moira.

I'd like to start by porting Musashi's MMU, but I am unsure about the best way to test the result. What is needed is an Amiga program which

  • utilizes the MMU (obviously),
  • is as simple as possible,
  • is likely to break in the presence of bugs.

If somebody knows a good candidate, please let me know.

Edit: An excellent article about how the MMU works can be found here.

Best m68k disassembler

I've started to add disassembler support for MMU instructions.

A little background: Moiras disassembler is verified by the testrunner app. For each opcode, testrunner disassembles the instruction with Moira, Musashi, and vdam68k. After that, it matches the outputs. For some opcodes, I've observed that the output of Musashi snd vdam68k are not only syntactically different (which is OK), but also semantically.

What I'd like to have (but may not exist) is a really reliable disassembler that can be treated as a golden reference where I can compare to. Originally, I thought vdam68k could serve this purpose, but it can't (at least not in the LINE-F area).

If somebody has suggestions for such a golden reference disassembler, please let me know.

P.S.: I've also looked at the Online Disassembler (which is really nice), but it doesn't seem to be perfect either. E.g., it doesn't know of any MMU command at all.

EDIT: Here is an example of such a difference:

DISASSEMBLER MISMATCH FOUND
Instruction: [10] 0trapeq   #$10101; (extension = $1) (2-3) (Musashi)
             [10] 0trapeq   #$10101; (extension = $1) (2-3)  (Moira)

             [8] ptrapbc.l #0x10001                       (Vda68k, Motorola)
             [8] ptrapbc.l #0x10001                       (Moira)

             [8] ptrapbc.l #0x10001                       (Vda68k, MIT)
             [8] ptrapbc.l #0x10001                       (Moira)

Setup:   PC: 1000  Opcode: f07b  Ext1: 0001  Ext2: 0001  Ext3: 0001 (SUPERVISOR MODE)
         CCR: f5  VBR: 01  SFC: 01  DFC: 01  CACR: 01 CAAR: 01

Musashi and Vda68k differ in both the operand ($10101 vs. $10001) and the instruction size (10 bytes vs. 8 bytes).

Moira Dasm: word size interpreted as negative

Hello Dirk,
I noticed the disassembler considers a word of the signed type, see first line from the screenshot. Is there a possibility to force the output all in unsigned type? See the second line of the screenshot what I mean.

Thanks for maybe considering this :-)

Screenshot 2022-10-11 at 23 21 48

MMU: Address mapping if table index is zero

I've began to implement some experimental MMU address mapping code and got stuck here (68030 user manual):

Table Index (TIA, TIB, TIC, and TID)
These 4-bit fields specify the numbers of logical address bits used as the indexes for the four possible levels of the translation tables (not including the optional level indexed by the function codes). The index into the highest level table (following the function code, when used) is specified by TIA, and the lowest level, by TID. The fields contain integers, 0-15. When a zero value in a TIx field is encountered during a table search operation, the search is over unless the indexed descriptor is a table (indirect) descriptor.

What I need to know is what "the search is over" exactly means in this context (over aka an exception is thrown, direct translation is used, etc.).

My favorite MMU article is also not clear on this point:

When setting up the TC register, it is worth remember that

IS + TIA + TIB + TIC + TID + PS = 32

These values must always sum to 32, as there are 32 bits in a logical address.
TIA must always be greater than zero, and if TIB is zero it must be a minimum of two.
If any of the TIn values contain zeroes, the tables at a lower level is ignored.
So if you set TIC to zero, you must also set TID to zero.

Support caches

Ref: dirkwhoffmann/vAmiga#730 #8

Long term it might be an idea to support caches properly as it's observable to programs. Attached is such an evil Amiga program (verified on a 68EC020). On a real 020 (Note: NOT later version) it should show a black screen. A blue screen is shown otherwise. (The test could be improved in many ways and assumes instruction cache is enabled).

cache.zip

Source:

start:
        move.w  #$7fff,$dff09a
        move.w  #$7fff,$dff096
        lea     $dff180,a0
        moveq   #0,d0
        move.b  #$0f,d1
        lea     .sup(pc),a1
        move.w  #$f00,(a0)
        lea     .inst+1(pc),a2
        move.l  a1,32.w
        moveq   #0,d2
        move.w  #$2000,sr
.sup
        tst.l   d2
        bne.b   .loop
        addq.l  #1,d2
        cnop    0,4
.loop:
        move.w  d0,(a0)
.inst   moveq   #0,d0
        move.b  d1,(a2)
        bra     .loop

32-bit data bus support

From dirkwhoffmann/vAmiga#730 some thoughts:

With 020+ and MMU support coming along/planned it might be a good idea to add support for proper 32-bit bus operations sooner rather than later. If I'm reading the source code correctly things are nicely prepared to support it, but all 32-bit accesses are split up into two 16-bit ones.

At the very latest, I think you'll have issues with (long)word accesses straddling protection boundaries, but I guess already with a (non-EC) 020 word access to $00ffffff you might notice a difference. Without having a complete overview, I'm guessing the "Memory" abstraction needs to be the one making the decision of whether to split up the access. Maybe it also needs to provide feedback on the access time for (more) exact emulation (at a much later point).

If/when making such changes it might be worthwhile to also consider how caches can/will be implemented. Not that I see any particular problems with the current implementation, just something to keep in mind (i.e. if 16-bit accessible chip mem is cached, you want a longword read to be read from the cache in one go, etc.)

Enforcer test

I've run Enforcer in combination with LawBreaker on my modded A500 (Terrible Fire), mainly to verify that the MMU is OK (which seems to be the case).

Here is the output (5 violations have been reported):

Bildschirmfoto 2022-10-04 um 15 22 05

Bildschirmfoto 2022-10-04 um 15 18 20

Milestone to reach: Make Enforcer work in vAmiga.

Instructions for generating the output:

  1. Install Kick 2.04
  2. Boot WB 2.0
  3. Open CLI
  4. Insert Enforcer disk
  5. Type run enforcer STDIO
  6. Type lawbreaker

testRunner issue

I describe the output of the testRunner and its backtrace, the executable is not compiled in debug, I cannot trace the assert line:

Moira CPU tester. (C) Dirk W. Hoffmann, 2019 - 2023

The test program runs Moira agains Musashi with randomly generated data.

Test rounds : 1

Random seed : 222

Exec range : (opcode >= 0x0000 && opcode <= 0xEFFF)
Dasm range : (opcode >= 0x0000 && opcode <= 0xFFFF)

Round 1:

68000 CPU ................................ PASSED (Moira: 1.28s Musashi: 1.11s)

68010 CPU ................................ PASSED (Moira: 2.62s Musashi: 2.32s)

EC020 CPU ................................ PASSED (Moira: 4.01s Musashi: 3.53s)

68020 CPU .......................

Program received signal SIGFPE, Arithmetic exception.

0x0000555555a11a9c in m68k_op_divl_32_d ()

(gdb) bt

#0 0x0000555555a11a9c in m68k_op_divl_32_d ()

#1 0x00005555559e2292 in m68k_execute ()

#2 0x00005555555830e0 in runMusashi(Setup&, Result&) ()

#3 0x0000555555582e08 in runSingleTest(Setup&, unsigned short) ()

#4 0x00005555555829dc in runCPU(long) ()

#5 0x0000555555582858 in run() ()

#6 0x0000555555581a00 in main ()

runned on Windows and Ubuntu, same result

Breakpoints at the beginning of exception handlers are ignored

Posted by Elmer:

I actually run into one issue concerning breakpoints at the start of exception handlers:
It is possible (of course) to add a breakpoint at the specific address, but the system is unable to detect it
The reason seems to be that the execute() function is atomic:
First it checks for changed ipl levels, if necessary initiates the exception (during which an address is loaded from the auto-vector table).
Subsequently, in one go, it executes the first instruction of the exception code, after which the address of the next instruction will be checked for a breakpoint.

MMU test translate1 fails on real machine

Test case: https://github.com/dirkwhoffmann/vAmigaTS/tree/master/CPU/MMU30/30translate1

This test installs a very simple MMU table which maps the $Axxxxx range to $Dxxxxx. After enabling the MMU, it changes the background color to green by writing into $AFF180.

In FSUAE and vAmiga, it works just fine:

Bildschirmfoto 2022-10-03 um 13 57 32

On the real machine, however, the test fails. The background color remains yellow and an illegal instruction is executed shortly after.

realMachine

A misaligned MMU table was my first guess, but I think that's not the case (I am aligning the MMU table with align 4 which should align it to an address which is a multiple of 16). Unfortunately, I am running out of ideas of what could cause the crash.

Jumps to odd exception vectors not handled correctly

If an exception vector points to an odd address, Moira reads a word from this address without checking for address violations.

I am unsure what the CPU is supposed to do in this case. If the address error exception vector is odd, does the CPU enter an infinite loop? 🤔

If somebody knows how the real CPU deals with odd exception vectors, please let me know.

Breakpoints / Watchpoints are not detected after reset

To speed up emulation, breakpoint checking is only performed if a specific flag is set:

    if (flags & CPU_CHECK_BP) {
        if (debugger.breakpointMatches(reg.pc)) {
            breakpointReached(reg.pc);
        } 
    }

During reset(), variable flags is set to a default value which has CPU_CHECK_BP and CPU_CHECK_WP set to 0. These flags must not be cleared if breakpoints / watchpoints are present.

Link instruction apparently broken?

Hi! I'm currently trying to build a fantasy console based on 68k processors and I quickly ran into this strange issue:
I have code generated by gcc that uses the link instruction to generate a frame pointer. The code looks something like this:

80000074:	4e56 fff8      	linkw %fp,#-8
80000078:	2f0d           	movel %a5,%sp@-

The issue is that when using Moira, A6 (FP) is initialized to 0 and is still 0 after running linkw, when it should be 8 bytes under the SP register (0x800030cc and 0x800030c4 in my case). In contrast, running the same instruction under Musashi gives the correct result of 0x800030cc in the FP register. I've attached the ELf that generates this issue, I'm not sure how to convert it to a flat image so it works without parsing it.
console-test.elf.gz

Add support for FC pins

The purpose of the function code pins (FC0 - FC2) is to make the 68000 connectable to a MMU. They are not implemented at the moment, simply because the Amiga has no MMU.

TODO:

  • Compute the proper FC value before a memory access happens. To avoid performance decrease, add config option EMULATE_FC.
  • Add an API function for querying the current value of FC2|FC1|FC0.
  • Add FC pin checking to the test runner (i.e., compare Moira’s FC value with the FC value computed by Musashi).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.