custom illegal opcode execution? #26
-
what's the 'correct' process for this? i inject an illegal opcode, handle it with willExecute, but then moira continues to process the exception. i can edit the moira source code to not do this, but i'm wondering if i'm missing something? i'd like to keep moira untouched of possible. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 6 replies
-
Moira has a feature called software traps, but I am not sure if it fits your needs. A software trap pauses emulation when a certain instruction is executed. When a software trap is set at some address, the instruction is remembered and replaced by a specific LINE-A instruction. When this LINE-A instruction is executed, Moira pauses emulation, calls the delegate Software traps are handles in this function: template <Core C, Instr I, Mode M, Size S> void
Moira::execLineA(u16 opcode)
{
AVAILABILITY(C68000)
// Check if a software trap is set for this instruction
if (debugger.swTraps.traps.contains(opcode)) {
auto &trap = debugger.swTraps.traps[opcode];
// Restore the original instruction
reg.pc = reg.pc0;
queue.irc = trap.instruction;
prefetch<C>();
// Inform the delegate
softwareTrapReached(reg.pc0);
return;
}
...
} In vAmiga, this feature is used to let us pause emulation when an application starts to execute. For doing so, it patches an ADF file with a software trap at the proper location of an executable. It is best explained by showing some code: void
FloppyDrive::catchFile(const std::filesystem::path &path)
{
{ SUSPENDED
// Extract the file system
auto fs = MutableFileSystem(*this);
// Seek file
auto file = fs.seekFile(path.string());
if (file == nullptr) throw Error(VAERROR_FILE_NOT_FOUND);
// Extract file
Buffer<u8> buffer;
file->writeData(buffer);
// Parse hunks
auto descr = ProgramUnitDescriptor(buffer);
// Seek the code section and read the first instruction word
auto offset = descr.seek(HUNK_CODE);
if (!offset) throw Error(VAERROR_HUNK_CORRUPTED);
u16 instr = HI_LO(buffer[*offset + 8], buffer[*offset + 9]);
// Replace the first instruction word by a software trap
auto trap = cpu.debugger.swTraps.create(instr);
buffer[*offset + 8] = HI_BYTE(trap);
buffer[*offset + 9] = LO_BYTE(trap);
// Write the modification back to the file system
file->overwriteData(buffer);
// Convert the modified file system back to a disk
auto adf = ADFFile(fs);
// Replace the old disk
swapDisk(std::make_unique<FloppyDisk>(adf));
}
} |
Beta Was this translation helpful? Give feedback.
-
I see. You're referring to this option: /* If ON, CPU will call the callback when it encounters an illegal instruction
* passing the opcode as argument. If the callback returns 1, then it's considered
* as a normal instruction, and the illegal exception in canceled. If it returns 0,
* the exception occurs normally.
* The callback looks like int callback(int opcode)
* You should put OPT_SPECIFY_HANDLER here if you cant to use it, otherwise it will
* use a dummy default handler and you'll have to call m68k_set_illg_instr_callback explicitely
*/
#define M68K_ILLG_HAS_CALLBACK OPT_OFF
#define M68K_ILLG_CALLBACK(opcode) op_illg(opcode) Seems like a usable feature to me. I could add something like a Then, changing the current implementation ... template <Core C, Instr I, Mode M, Size S> void
Moira::execLineF(u16 opcode)
{
AVAILABILITY(C68000)
execException<C>(EXC_LINEF);
CYCLES_68000(34)
CYCLES_68010(38)
CYCLES_68020(34)
FINALIZE
} to something like that should do the trick: template <Core C, Instr I, Mode M, Size S> void
Moira::execLineF(u16 opcode)
{
AVAILABILITY(C68000)
if (lineInstrHandler(opcode) == false) {
prefetch<C>();
return;
}
execException<C>(EXC_LINEF);
CYCLES_68000(34)
CYCLES_68010(38)
CYCLES_68020(34)
FINALIZE
} Would it fit your needs? Side note: I don't want to intercept at a lower level (i.e., in execException), because this function is sometimes called in the middle of an instruction, e.g., when an invalid argument is encountered. Simply calling |
Beta Was this translation helpful? Give feedback.
-
Thanks for the explanations!
I guess you mean in comparison to the earlier version, which presumably used Musashi? The reason is, indeed, that Moira is big. I've deliberately exchanged memory consumption for speed. Moira gains its speed by heavily using templates, which causes the compiler to replicate functions multiple times. By doing so, I managed to make Moira even slightly faster than Musashi, which is already fast due to the generated exception handler. Also, Musashi has the speed advantage of pure C code, compared to Moira, which comes with a small runtime overhead due to its C++ nature. |
Beta Was this translation helpful? Give feedback.
Since I am curious: Can you elaborate a little more about the purpose of catching illegal instructions? Are you using this to debug or implement some ST-specific features? In vAmiga, I never felt the need to customize the treatment of illegal instructions (except for the software trap case, which was used in a particular scenario).