Skip to content

Declare tic80_tick's callbacks as they are defined and called - #2981

Open
annejan wants to merge 1 commit into
nesbox:mainfrom
annejan:fix-tic80-tick-callbacks
Open

Declare tic80_tick's callbacks as they are defined and called#2981
annejan wants to merge 1 commit into
nesbox:mainfrom
annejan:fix-tic80-tick-callbacks

Conversation

@annejan

@annejan annejan commented Aug 21, 2026

Copy link
Copy Markdown

include/tic80.h declares the timing callbacks as u64 (*)(), while tic.c defines tic80_tick taking CounterCallback and FreqCallback, which are u64 (*)(void*), and core.c calls them with an argument:

core->data->counter(core->data->data)

Under C17 and earlier an empty parameter list means "unspecified arguments", so the mismatch is hidden. C23 gives () the same meaning as (void), and the declaration and definition become incompatible types. Building with a C23 compiler fails:

src/tic.c:104:16: error: conflicting types for 'tic80_tick'; have
'void(tic80 *, tic80_input, u64 (*)(void *), u64 (*)(void *))'

GCC 15 defaults to -std=gnu23, so this is reached simply by building with a current toolchain. I hit it porting TIC-80 to the ESP32-P4, where the SDK compiles at C23; the workaround there is pinning -std=gnu17, which seemed worth replacing with a real fix.

The change

run.c already implements these callbacks correctly, taking void* and receiving .data. Only the two standalone callers did not, and they worked because the callee ignored the extra argument:

  • include/tic80.h — declare the parameters the definition actually has
  • src/system/sdl/player.c — its two static callbacks take void*
  • src/system/libretro/tic80_libretro.c — likewise

Five lines. No behaviour change; the argument was already being passed at every call.

Tested by building the SDL target with GCC 15.

Happy to take the opposite approach instead if you would rather the callbacks genuinely take no argument — that would mean changing the typedefs in api.h, the four call sites in core.c, and run.c, and dropping the .data plumbing. I went with the smaller change that keeps the existing design.

include/tic80.h declares the counter and freq callbacks as u64 (*)(),
while tic.c defines tic80_tick taking CounterCallback and FreqCallback,
which are u64 (*)(void*), and core.c calls them with an argument:

    core->data->counter(core->data->data)

Under C17 and earlier an empty parameter list means "unspecified", so the
mismatch is hidden. C23 gives () the same meaning as (void), and the
declaration and definition become incompatible types, so building with a
C23 compiler fails:

    error: conflicting types for 'tic80_tick'

The studio already implements these correctly, in run.c, taking void* and
receiving .data. The two standalone callers did not, and worked only
because the callee ignored the extra argument.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@joshgoebel

Copy link
Copy Markdown
Collaborator

Looks good at a glance, though I'm not sure I understand technically which approach might be best. If this is fixing the minority case that seems reasonable to go with the majority unless anyone else kows better.

@annejan

annejan commented Aug 23, 2026

Copy link
Copy Markdown
Author

I bumped into the issue porting TIC-80 to our own fantasy console of sorts https://nicolaielectronics.nl/tanmatsu/

In my opinion it's a matter of future proofing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants