@never-obsolete: I just complained about this in a PM with Sour this weekend, actually. Yes, it's a specific to RMW instructions. I don't want to include the PM conversation publicly without his consent, but I did mention that this behaviour is somewhat non-intuitive, debugger-wise, but the counterargument is that it's actually highly relevant for people doing cycle-timed code (and that's a legitimate purpose). I felt it was something that should be discussed community-wise. He stated that an option/checkbox in one of the menus to toggle break-once-per-instruction (vs. per actual T-state read or write) might be possible.

Due to conditional breakpoints, among other things, breakpoints are evaluated every single PPU cycle.Write breakpoints are triggered by the actual memory writes (e.g the last or before-last cycle of instructions) - there is nothing that "predicts" that the instruction will eventually write to an address and break ahead of it.

On operations with dummy reads or doubled up writes, this means that the breakpoint might be hit multiple times.Dummy read/writes can be the source of bugs (e.g reading some PPU registers with an instructions that contains a dummy read, some mapper registers might be affected by them as well, etc.), which is why the debugger does not attempt to "hide" them.

Additionally, it's possible for 2 separate breakpoints to trigger at different parts of the same instruction (e.g maybe an instruction with a regular execute breakpoint also ends up triggering a conditional breakpoint that breaks whenever the ppu reaches cycle 30 on scanline 100), in this case, the debugger will break up to once per PPU cycle, whenever a matching breakpoint exists.

Like I mentioned to koitsu, having a "only allow breakpoints to break once per instruction" option would solve most of these situations, but it will require a bit of extra logic beyond just ignoring all subsequent breakpoints (e.g because users would except the break to occur on the last write of an instruction rather than the "dummy" write that writes an incorrect value just before, etc.)

Like I mentioned to koitsu, having a "only allow breakpoints to break once per instruction" option would solve most of these situations, but it will require a bit of extra logic beyond just ignoring all subsequent breakpoints (e.g because users would except the break to occur on the last write of an instruction rather than the "dummy" write that writes an incorrect value just before, etc.)

I don't think it's necessary for this option to split that hair, for the same reason I would definitely turn it on. This is how I would normally expect any debugger to work, 6502 or otherwise:

1. The break should always be before the instruction begins, so you can inspect the state of the machine before it runs. Should not be looking at any instruction partially-executed. PC should still match the instruction's address.

2. One step should advance one instruction, all of its reads/writes at once and proceed to the next instruction. Can now inspect state of the machine after that instruction has finished.

Yes, I get that the instruction takes several cycles, and even finer than that we can subdivide into pixels. That's really cool to be able to dive into if you want, but it's really far removed from being useful for most debugging purposes, and I also think it's unintuitive for most users to have to understand sub-cycle info, or understand that your debugger is handing them breakpoints in the middle of an instruction. I've never encountered any debugger that behaves this way (they may exist, but I haven't seen it), an instruction should be an indivisible unit by default. I'd been confused before about the PC after breakpoints in Mesen, for example, but I hadn't realized until just now that this is why. Hadn't yet understood that it wasn't just me misreading things, or that it was actually caused by this unusual behaviour.

It's very much important to trap breakpoints on the extra reads and writes, that part of it is critical to include, but at least for myself, the sub-instruction concept for breakpoints is way out of left field. I didn't expect it at all.

Instead of a "one breakpoint per instruction" option, I'd make a counter suggestion: by default, don't subdivide instructions: any breakpoint or halt of execution should rewind to the beginning of the current instruction, and any run/step should advance at least one instruction before it can stop/break again.

Instead have an "advanced" option for subdividing instructions which takes this limitation off. At that point allow the "run one PPU cycle" button, maybe allow a "run one CPU cycle" button as well. An instruction sub-cycle indicator somewhere in the UI would help a lot understanding where we are. (Maybe a dream beyond this could be a little diagram of the current instruction in another panel indicating which step in its sequence we are currently on.) If I could see this stuff indicated clearly, it would actually be really amazing, but definitely in an "advanced" category of features that I think having on by default detracts from normal use.

The main problem with the way it is currently is that I have no idea where in an instruction I am when a breakpoint hits. The PC is likely incremented to the next instruction already for most breakpoints, there's no indicator on which subcycle I'm on or when the current instruction started. Sort of what I was saying above, I noticed little things were not what I expected, but couldn't tell what was bothering me until koitsu pointed out this is an actual feature.

It's really neat to be able to go cycle by cycle in an emulator like this. I think this is an unprecedented feature, and am really impressed, but I also think that it gets in the user's way; much better to keep it off until they ask for it. Especially trying to step through code, if it starts requiring uneven number of presses per instruction to advance... that seems like a real problem unless your goal is specifically to look at very minute sub-cycle details.

Instead of a "one breakpoint per instruction" option, I'd make a counter suggestion: by default, don't subdivide instructions: any breakpoint or halt of execution should rewind to the beginning of the current instruction, and any run/step should advance at least one instruction before it can stop/break again.

Easier said than done, unfortunately. At best, I could take a savestate every frame and replay the emulation from the last state when trying to rollback the emulation after encountering a breakpoint (which would only result in maybe 5-10 milliseconds of delay even on slower computers). But there are a number of problems that come up when doing this (e.g the trace logger's log wouldn't be correct because of the rewind operation, other things like the profiler, call stack, etc. are also most likely affected), which are fine as "limitations" to the step back feature, but not acceptable if they are triggered every time a breakpoint is hit. It's not impossible to get done, but it involves a fair amount of work. Making it so stepping always runs the current instruction completely before breaking again should be pretty simple, though.

Other than the PC value being "odd" at times when breaking the execution on a read/write, I don't believe there is really anything else that's meaningfully affected by this, though? The execution does break before the reads/writes take place, it just doesn't break at the beginning of the instruction. The PPU does end up running for a few extra cycles before breaking, though.

The debugger window used to cheat and always display the PC as it was at the beginning of the instruction (regardless of the CPU's current state), but I ended up changing it when I added the functionality to edit the emulator's state from the UI, IIRC.

I was under the impression that you'd already implemented rewinding. How does the current "step back" function work?

Anyway, not trying to tell you what's easy to implement or not; just was going from that assumption.

rainwarrior wrote:

Other than the PC value being "odd" at times when breaking the execution on a read/write, I don't believe there is really anything else that's meaningfully affected by this, though? The execution does break before the reads/writes take place, it just doesn't break at the beginning of the instruction. The PPU does end up running for a few extra cycles before breaking, though.

If you're on the second write of an instruction with a dummy write, you would see the dummy value stored there, which would be different than from the beginning of the instruction?

Conditional breakpoints based on the value read and written might have very confusing consequences if you don't think about the dummy write in between. e.g. if you wanted to break when something changes from one value to another, an intermediate value might prevent the breakpoint entirely? I'm actually wondering if I might have run into this earlier... may do some tests later.

When debugging I'm often doing it because I'm unsure about my own code, so if the debugger is doing something I didn't forsee, it's more likely that I'd expect there is a problem with my code than the debugger. Having to think about an intermediary value could be a really hard problem. Synthesizing sub-instruction values in my head is something I will never be able to do, and that's sorta what I mean when I suggest that the debugger should present these things in a simpler looking way by default. Having to think about it as a prerequisite for being able to understand how the debugger is acting is a layer of complexity that feels like an encumbrance. It'd be great to be able to crack open an instruction in the debugger, cycle by cycle, but the need for it in my view is exceedingly rare.

I've needed to know about sub-cycle things a few times when thinking about how mappers work, but I've really never needed to know it when debugging, even when doing finely timed stuff like raster effects and PCM sound playback. In the latter case FCEUX's cycle counter in its debugger is actually extremely good for timing instruction to instruction, and I know exactly where it goes from/to because it always starts at the beginning of an instruction, which is the same way I have to write and think about the code. In the case of raster timing, I don't normally have the luxury of landing an instruction on a specific cycle anyway; what I need to understand is the range of timings where the instruction can happen. Sub-instruction steps have never seemed that important for it. They're important for the emulator to get in the right place, of course, but for writing NES software it's an internal detail I don't need to think about as long as it's consistent.

Even something more mundane like knowing the PC is a different value later in the instruction seems more like a vector for confusion to me than useful knowledge about the instruction's operation. (Maybe even critical to get this correct as an emulator author, but when writing software, I don't see the utility.)

Again, really impressed that Mesen even has this level of detail in its debugger, and I think it's fantastic as an extra feature, but only if it doesn't get in the way of more fundamental things. ...and probably I'm making suggestions that would be hard to implement, but I'm only trying to offer my opinion of how I expect a debugger to operate. (At the very least, I strongly recommend putting some sort of "sub instruction" indicator somewhere so I can at least see how many cycles into the instruction we have reached.)

So, that's kinda what I meant in my more succinct post above, but probably needed elaborating. I'm sure others might have more thoughts on this, or differing thoughts, would be glad to hear them.

(Apologies for the repeated comparisons to FCEUX, but I've been using it for 15 years, so it's what I know!)

I definitely have a preference for FCEUX's approach of breaking at the start of the instruction, but I totally understand why Mesen does it the way it does and that it'd be hard to change it. Some of this overlaps with what rainwarrior said, but as a user, I'd say the pain points of the current breaking behavior (assuming I'm not overlooking things that address these) are the following:

- When I break on something, I can't then modify register state to reliably affect future execution. I break on a STA, change A, and the old value is the one that gets stored (but A was changed and will affect the next instruction). There's internal state I'm not allowed to see or modify.

- There's no visibility into where I am within an instruction. Am I at the start or several cycles in? Instructions have multiple different steps and those are normally entirely hidden, but then they suddenly matter when breaking happens without the information being surfaced. Just knowing the current cycle of the instruction would be a big help, even more if something (instruction tooltips?) also laid out what happens on what cycle.

- It's not always clear why we've snapped on an instruction, such as with a RMW instruction potentially triggering 3 (or more, if also using execute) times. This strikes me as part of a larger issue where I often find myself wondering 'why am I here?' when the debugger snaps. Maybe it's a breakpoint (which one?), or an invalid instruction, or...? Highlighting the triggering breakpoint would be helpful, and/or maybe having a status indicating why the snap happened.

- Forbidding breaks on specific instructions is cumbersome. In FCEUX, I can create a breakpoint on an address and check Forbid so that that instruction won't trigger breakpoints anymore. In Mesen, I can modify the condition on a specific breakpoint to add a PC exemption (much less convenient, but OK), but this doesn't work because PC has been incremented by the time the instruction does the action we break on, so it's actually some unaligned PC that I need to exempt. I need to know what that intermediate PC is, or exempt a range of PCs per instruction (per breakpoint).

I agree with rainwarrior that sub-instruction debugging is really cool, but I'm unsure when I'd want to use it. I have a lot of experience doing carefully timed, cycle-precise code for things like screen splits or input reading and while this debugger behavior could maybe provide some value for such work, I've generally found that cycle-counting and the event viewer tend to be enough. I definitely like that it's there, but it seems to be overkill for the vast majority of use cases and can currently get in the way of clarity and some functionality I expect from other debuggers I've used. Things like these make me still reach for FCEUX a lot of the time when I don't need Mesen's more powerful features just because FCEUX is often easier, though I really love the feature set Mesen provides and its high level of accuracy (and I have been using it more and more as I get more used to it).

I was under the impression that you'd already implemented rewinding. How does the current "step back" function work?

It's implemented but like they say, the devil is in the detail :) When you use the step back function, it will affect a number of debugger-related state (the trace logger's log, profiler, access counters, and potentially more that I'm forgetting right now?) This is "ok" for a relatively minor feature (and not used excessively often) like step back, but if I were to apply this as-is to breakpoints, it wouldn't be ideal.

That being said, thanks for the feedback, I appreciate it. I agree that it's better/simpler to break at the start of an instruction at all times (at least by default). I could spend some time trying to argue some points or to explain why it might be relatively hard to pull off, but it's probably better to spend that time actually trying to implement it :p

On top of this, I think the idea of displaying "where" inside a specific instruction the debugger is currently at (e.g in the case where you re-enable sub-instruction breaks) is a pretty neat idea, too. Displaying which breakpoint triggered the break is also a good idea (it's been on my todo list for a while, too).

Fiskbit wrote:

I can modify the condition on a specific breakpoint to add a PC exemption (much less convenient, but OK), but this doesn't work because PC has been incremented by the time the instruction does the action we break on

IIRC, there is a "OpPC" value that can be used specifically to address this issue (it's documented inside the help tooltip for conditions)

Haven't quite gotten around to making breakpoints break at the start of the instructions yet. I actually just took a look at how FCEUX did this and it seems like it just uses some code to calculate the final read/write address of an instruction in the debugger ahead of time. This ends up copying a tiny portion of the CPU's logic in the debugger, but will actually be far easier than attempting what I was considering so far - turns out predicting the future is a lot easier than going back to the past :)

That being said, in the meantime I've been working on making the current behavior easier to understand. I've added UI elements to let the user know what caused the execution to break (e.g a breakpoint or an option, etc.) and also display the current instruction's "progress" (e.g what cycle are we on, is it an execute (X), read (R), write (W), dummy read/write (DR/DW) cycle, etc.). These can be turned off via the options menu ("Show break notifications" and "Show instruction progression")

Attachment:

CodeWindow1.png [ 20.82 KiB | Viewed 4896 times ]

Attachment:

CodeWindow2.png [ 4.91 KiB | Viewed 4896 times ]

Attachment:

CodeWindow3.png [ 15.72 KiB | Viewed 4896 times ]

One thing to note is that the "progress" shows an "estimated" total cycle count for the instruction. Crossing pages or taking branches will increment the total cycle count as needed, during executing (but the original value when the operation starts will not be correct 100% of the time). e.g a branch will start off as "1/2", but if the branch is taken and crosses a page, it will end up going 1/2 -> 2/2 -> 3/3 -> 4/4.

And also added an option in breakpoints to determine whether dummy read/writes should trigger the breakpoint (off by default):

Attachment:

Breakpoints.png [ 11.61 KiB | Viewed 4896 times ]

These changes should hopefully improve the current debugging experience - any feedback on these is more than welcome (the latest appveyor build [Windows][Linux] contains these changes).

How does Mesen determine SRAM size for MMC5 games that have an unknown CRC and no NES 2.0 header?

There's a standing recommendation to default to 64K. When w7n (a member of the FamiTracker users' Discord server) complained about an emulator not following this, I recommended that ROM authors could specify the correct RAM size in an NES 2.0 header. But w7n claimed that it would be just as easy for each emulator author to default to 64K for pre-NES 2.0 headers, as specified on the wiki, as it would for each ROM author to switch from the old header to NES 2.0.

EDIT: I'm asking this with respect to multiple emulators' MMC5 implementations, as each may or may not be affected by this design decision.

Haven't quite gotten around to making breakpoints break at the start of the instructions yet.

Alright, so after a bit more fumbling, I finally have something that works and is reasonably efficient. (Appveyor dev builds: [Windows][Linux])

Essentially the debugger executes the upcoming instruction on a "dummy" CPU that can't affect the emulator's state, keeps track of the reads/writes done by the instruction and then breakpoints are evaluated based on that information, before ever executing the instruction. Since the debugger is only allowed to read APU & PPU registers during this process, it's not perfect (e.g if you try to use a conditional breakpoint based on the value read from the MMC5's multiply register, it won't work as expected, but the old breakpoint system will). For the vast majority of cases, it should work as expected, though.

This is slightly slower than the "old" breakpoint system (because it essentially requires executing every CPU instruction twice to predict the read/write patterns properly), but in practice the difference in performance is less than 10% (and the typical cases where no read/write breakpoints exist run as fast as they did before).

The option to control this behavior is Options->Break options->"Enable sub-instruction breakpoints" (if anyone has a better idea for the name, let me know..). When enabled, it will revert to the 0.9.7 breakpoint system of breaking in the middle of instructions for read/write breakpoints, etc. The option is disabled by default, so the default is now similar to FCEUX's behavior. In this new mode, the debugger will also only break a single time per instruction, no matter how many different breakpoints match.

One thing to keep in mind is that the "Run PPU cycle/scanline/frame" options can still break in the middle of instructions, but other than this, the execution should now always break at the start of the instruction. It's also now possible to edit the value that is about to be written (e.g having a write breakpoint on a STA instruction and changing the value of A before stepping will write the updated value - this was one of the issues described by Fiskbit in his post).

Hopefully this makes working with Mesen's debugger more intuitive from a homebrew/romhacking point of view.Let me know if you find any bugs with this (it's a fairly large change, so bugs are somewhat likely) or if there are any other concerns with regards to usability vs FCEUX, etc.

As suggested by yaros in another thread, I added a "Go To All" feature to the code window in Mesen. This behaves more or less like the same feature in Visual Studio - you type some text, and it finds whatever it can that matches it (labels, symbols, constants, registers, files). The default shortcut is Ctrl+comma (mostly because that is one of the default shortcuts for it in VS), but it can be customized like all the other shortcut keys.

This is what it looks like on a project using CC65 integration w/ .dbg files:

Attachment:

GoToAll.png [ 21.41 KiB | Viewed 3996 times ]

You can navigate the result list with the up/down/page up/page down keys, or just by scrolling/clicking with the mouse. Pressing enter or double-clicking on an entry will close the window & navigate to that location in the code window.

When a specific label isn't available to the debugger (e.g because it's been switched out of memory), the debugger will automatically switch to source view to display the original code.

Each entry lists some basic information: the offset in PRG ROM (or work ram, etc.), the current memory location in CPU memory, the current value stored at that address, and the source file & line number (with CC65 integration).

The icons indicate the type of data:-Functions-Jump/Branch targets (this isn't perfect quite yet since the debugger loses this information every time it is restarted)-Labels that are defined as registers-File-Constant-And another "hex code" icon for anything else

When used on a project with no CC65 integration, the debugger can't display code/data that's been switched out of memory, so the entries will be shown last in the list, with a warning icon (and they will be disabled):

Attachment:

disabled.png [ 22.65 KiB | Viewed 3996 times ]

There's still a bit of work to do - e.g I'd like to make the search logic a bit better than it is now and I want to add the same feature to the hex editor window, too. As usual, any feedback is welcome! (The latest appveyor build has the feature)

Who is online

Users browsing this forum: No registered users and 1 guest

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum