Cycle cost diagnostics (GBS0400–GBS0499)
Estimated cycle costs measured against the 70,224-cycle frame budget. Estimates from the IR - read them as ceilings for comparing changes, not as measurements.
GBS0401: Frame loop is close to a frame
Severity: Performance · Suppressible: yes
This frame loop costs an estimated {0} cycles an iteration, about {1}% of a frame.{2}
The hardware gives 70,224 cycles between frames, at 59.7 frames a second. Everything else comes out of the same budget: the VBlank handler, any audio driver, and whatever the loaders copy, none of which GB# can see from the source. So 100% is well past too late, and this fires early on purpose. The build report ranks the functions this loop reaches, which is usually where the time has gone.
GBS0410: Loop cost
Severity: Performance · Suppressible: yes
This loop runs up to {0} times at an estimated {1} cycles each, about {2} in total.{3}
The trip count comes from the loop's own bounds, or from the capacity of the collection it walks, which its count can never exceed. A 'break' makes the count an upper bound rather than an exact one, which is what a worst-case estimate wants. The per-iteration figure does not account for what SDCC keeps in registers, so read the total as a ceiling and as a way of comparing two versions of the same loop.
GBS0420: Call depth
Severity: Resource · Suppressible: yes
The deepest call path is {0} calls: {1}.
Every call costs two bytes of stack for its return address before any argument or local, and a banked call costs more again for the trampoline. The stack grows down from the top of work RAM into the same 8 KB the static fields grow up through, and nothing checks. This depth is exact rather than estimated: GB# has no delegates and no function pointers, so the call graph is the whole account of what can reach what.
GBS0440: Banked call every frame
Severity: Performance · Suppressible: yes
'{0}' is reached from the frame loop and switches to ROM bank {1}, which costs an estimated {2} cycles more than a local call every time.
GBS0301 says this call is banked. This says it is banked on the path that runs sixty times a second, which is the version worth doing something about. Mark the callee [Bank(0)] to keep it resident, or hoist the call out of the loop and keep what it returned. Bank 0 is only 16 KB and the build report shows how much of it is left.
GBS0441: Callee could share its caller's bank
Severity: Info · Suppressible: yes
'{0}' is in bank {1} and every call to it comes from bank {2}; moving it would remove {3} bank switches.
A bank switch is paid per call rather than per function, so putting a callee in the same bank as its only caller removes all of them at once. This is a consequence, not a recommendation: the callee may well be where it is because the other bank is full, and the build report is what says whether it would fit.