For over a decade I have not used a debugger
Ouch.... That is painful.....
Seems like it is probably time to start using more of the full toolset.
gdb has been available for more than 25 years.....
The beauty of the gcc toolset is that just like unix and now linux the commands are very consistent and stable and have been so for decades.
(Although things can be tough if trying to use Windows for s/w development since it wasn't designed for that)
It amazes how many folks stop short of getting their s/w development tools in working order.
In my view part of that is getting s/w tools in order is to get the gdb source level debugger up and working.
For anything complex having the source level debugger up and running will more than pay for the time needed to get the tool working.
As far as using another environment for simulation. I've done this many times over the past 3 decades.
In fact at a company i started, we simulated an entire ASIC in s/w which allowed writing and debugging the unix drivers and full communications protocol stacks with actual end to end communications between multiple machines before the hardware existed.
I also created a simulated environment when I was bringing up the GLCDv3 library.
I was source level debugging the routines and drawing "pixels" on the screen of my linux machine long before I put the code into a library for the Arduino.
The key thing to keep in mind when doing simulations like this is to write very portable code and to properly layer the code to ensure that functionality is very isolated and interfaces are clean.
i.e. don't have code that does i/o and algorithms or multiple things in the same function.
Also, use wrapping macros whenever possible so that no native/proprietary functions/macros are ever called/used by the mainline code.
liberally use multiple source module files so you can keep the code clean so that that the truly portable code is not jumbled in the same source module file with code that starts to get closer to the i/o which will need simulation macros.
liberally use macros to map/wrap the missing functions/macros into something that simulates needed functionality or create stubs just to get things to link up if the functionality is not needed.
For the lower level code that starts to use proprietary macros/calls, start to create a simulation header that replicates those macros or maps them to something else rather than try to include the actual headers as using the original headers will often cascade and create additional issues.
--- bill