You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The client should be able to read settings from the command line, a settings file, or settings menu in the interface, save the settings file, and provide the settings in a convenient way to the other parts of the program that may need it.
Exact format of the settings file TBD, but should be something simple enough that parsing it in C won't be too much of a headache.
Being able to set options over CLI that override the settings file would be great too. Those should probably be passed through to the client when initializing it.
Because settings and the formats for storing them are so common, it may make sense to introduce a small dependency for a .json or .toml or whatever format parser and just work with that, since it's far less interesting (imo) than the primary work on the emulator or the client. But maybe someone is really motivated to implement it themselves?
If we have to add a dependency on some parser (for the client) I wouldn't be opposed, as you said that sort of thing just isn't very interesting. Would there be a loss though on using a simple format where parsing is more trivial akin to INI?
I like the idea of command line args but I'm a little unsure of how to go about it. We theoretically want the core to be able to compile without an operating system (it's not a priority yet, but we've discussed running the emulator in an embedded setting as a secondary client once the core is further along), so it's not necessarily safe to assume that we have those.
I think it'd be fine to just have an INI file, I don't think the exact format matters exactly as long as it's fairly easy to read and edit by hand. Like just having a hashmap (actually it doesn't have to be a hashmap even, could just be a regular binary tree) mapping key=value for each [heading] in the file seems like something we could write ourselves easily enough.
I'm not sure how the entrypoint would look when compiling to bare-metal. One way I can think of keeping the concerns seperate, is putting the would-be main() code in a new function like emu_main(), and then having the normal OS-hosted main pass along its argc and argv, while the freestanding version can call emu_main() with whatever fixed arguments, including (0, NULL). Then the different versions of main (normal vs freestanding) can be behind defines, like
intemu_main(intargc, char*argv[]);
#ifdefFREESTANDING_IMPLvoidfree_main() { emu_main(0, NULL); /*or whatever set of fixed args*/ }
#elseintmain(intargc, char*argv[]) { emu_main(argc, argv); }
#endifintemu_main(intargc, char*argv[]) { /*the stuff we normally have in main*/ }
You should probably keep the clients completely separate from each other; freestanding is going to have extremely different requirements from pc (global variables, initialization, etc.). I would recommend having a src/client_pc/entry.c and src/client_freestanding/entry.c which both contain their own main and do all their own initialization and everything.
The only thing that they ""share"" is both have a global NesEmulator variable. The "includes/nes.h" should have all functions that the client needs to run. I think someone mentioned something about calling client functions from the emulator, and you should not do this. Polling in the client is completely fine!
I think this was initially what someone's idea was, but I shot it down at first. Sorry about that! I wasn't aware of all the details of what you needed to do at the time.
You should probably keep the clients completely separate from each other; freestanding is going to have extremely different requirements from pc (global variables, initialization, etc.). I would recommend having a src/client_pc/entry.c and src/client_freestanding/entry.c which both contain their own main and do all their own initialization and everything.
The only thing that they ""share"" is both have a global NesEmulator variable. The "includes/nes.h" should have all functions that the client needs to run.
I agree that they are going to be wildly different in their implementation, but it sounds like what you're suggesting is essentially what we already have with client_init() in the client API. Every client has its own init function that the entrypoint should call after it does its most basic setup. If they all have their own main, this to me suggests that the emulator code should be a library and the clients should be executables who are linking to it (static or dynamic lib) (The project was originally structured this way before switching it around).
I think someone mentioned something about calling client functions from the emulator, and you should not do this. Polling in the client is completely fine!
I think this was initially what someone's idea was, but I shot it down at first. Sorry about that! I wasn't aware of all the details of what you needed to do at the time.
Can you explain a bit more about why we should definitely not use callbacks vs. polling?
If they all have their own main, this to me suggests that the emulator code should be a library and the clients should be executables who are linking to it (static or dynamic lib).
Yea i think statically linking here is best.
The project was originally structured this way before switching it around
yea thats my bad! I didn't know all the details at the time when i made that suggestion, apologies!
Can you explain a bit more about why we should definitely not use callbacks vs. polling?
iirc i saw something in the discord where you want a callback to signal that a new frame can be drawn. In sdl, I don't think you would draw the frame from inside the callback (ive never used sdl but from working briefly with other graphics libs it seems unlikely), instead you'll probably have some loop in main() that renders a frame every 30ms. In this particular case, polling would work just fine because you can just check nes.has_new_frame == true to render a new frame.
There may be some situations where you might need a callback, but i would recommend trying to first think if it can be done via polling instead. Polling is gonna be much easier to write, debug, and test. If you have any particular things that you think require a callback lmk in the discord.
Activity
TheEyesightDim commentedon Oct 13, 2026
Because settings and the formats for storing them are so common, it may make sense to introduce a small dependency for a .json or .toml or whatever format parser and just work with that, since it's far less interesting (imo) than the primary work on the emulator or the client. But maybe someone is really motivated to implement it themselves?
jbcarras commentedon Oct 14, 2026
If we have to add a dependency on some parser (for the client) I wouldn't be opposed, as you said that sort of thing just isn't very interesting. Would there be a loss though on using a simple format where parsing is more trivial akin to INI?
I like the idea of command line args but I'm a little unsure of how to go about it. We theoretically want the core to be able to compile without an operating system (it's not a priority yet, but we've discussed running the emulator in an embedded setting as a secondary client once the core is further along), so it's not necessarily safe to assume that we have those.
TheEyesightDim commentedon Oct 15, 2026
I think it'd be fine to just have an INI file, I don't think the exact format matters exactly as long as it's fairly easy to read and edit by hand. Like just having a hashmap (actually it doesn't have to be a hashmap even, could just be a regular binary tree) mapping
key=valuefor each[heading]in the file seems like something we could write ourselves easily enough.I'm not sure how the entrypoint would look when compiling to bare-metal. One way I can think of keeping the concerns seperate, is putting the would-be
main()code in a new function likeemu_main(), and then having the normal OS-hosted main pass along itsargcandargv, while the freestanding version can callemu_main()with whatever fixed arguments, including(0, NULL). Then the different versions of main (normal vs freestanding) can be behind defines, likeFejiberglibstein commentedon Oct 15, 2026
You should probably keep the clients completely separate from each other; freestanding is going to have extremely different requirements from pc (global variables, initialization, etc.). I would recommend having a
src/client_pc/entry.candsrc/client_freestanding/entry.cwhich both contain their own main and do all their own initialization and everything.The only thing that they ""share"" is both have a global NesEmulator variable. The "includes/nes.h" should have all functions that the client needs to run. I think someone mentioned something about calling client functions from the emulator, and you should not do this. Polling in the client is completely fine!
I think this was initially what someone's idea was, but I shot it down at first. Sorry about that! I wasn't aware of all the details of what you needed to do at the time.
TheEyesightDim commentedon Oct 15, 2026
I agree that they are going to be wildly different in their implementation, but it sounds like what you're suggesting is essentially what we already have with
client_init()in the client API. Every client has its own init function that the entrypoint should call after it does its most basic setup. If they all have their own main, this to me suggests that the emulator code should be a library and the clients should be executables who are linking to it (static or dynamic lib) (The project was originally structured this way before switching it around).Can you explain a bit more about why we should definitely not use callbacks vs. polling?
Fejiberglibstein commentedon Oct 15, 2026
Yea i think statically linking here is best.
yea thats my bad! I didn't know all the details at the time when i made that suggestion, apologies!
iirc i saw something in the discord where you want a callback to signal that a new frame can be drawn. In sdl, I don't think you would draw the frame from inside the callback (ive never used sdl but from working briefly with other graphics libs it seems unlikely), instead you'll probably have some loop in main() that renders a frame every 30ms. In this particular case, polling would work just fine because you can just check
nes.has_new_frame == trueto render a new frame.There may be some situations where you might need a callback, but i would recommend trying to first think if it can be done via polling instead. Polling is gonna be much easier to write, debug, and test. If you have any particular things that you think require a callback lmk in the discord.
TheEyesightDim commentedon Oct 15, 2026
Sure we can talk about it more in the discord (peeps can still chime in here though)