dirkwhoffmann / moira Goto Github PK
View Code? Open in Web Editor NEWA Motorola 68000 emulator written in C++
Home Page: https://dirkwhoffmann.github.io/Moira
License: Other
A Motorola 68000 emulator written in C++
Home Page: https://dirkwhoffmann.github.io/Moira
License: Other
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).
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
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).
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):
Milestone to reach: Make Enforcer work in vAmiga.
Instructions for generating the output:
run enforcer STDIO
lawbreaker
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.
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
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):
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);
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.
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.
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
If somebody knows a good candidate, please let me know.
Edit: An excellent article about how the MMU works can be found here.
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.
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.
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.
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
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:
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:
On the real machine, however, the test fails. The background color remains yellow and an illegal instruction is executed shortly after.
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.
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.)
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:
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.
Suggestion to change line 104:
str << "dc.w " << UInt16{op} << "; ILLEGAL";
Into:
str << "dc.w " << tab << UInt16{op} << "; ILLEGAL";
This makes disassembler output more aligned.
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
Hi Dirk,
I have the impression that Moira allows access to words/long words on uneven addresses. I.e. not raising an exception at that point. Can you confirm this and is there currently an option to turn it on?
Best regards, Elmer
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.