News:

11 March 2016 - Forum Rules

Main Menu

[PC] Non-linear code flow

Started by Gemini, May 07, 2014, 01:02:36 PM

Previous topic - Next topic

Gemini

These days I'm working on my own engine, again. For the moment the whole environment runs on a PlayStation setup with non-linear code flow, which is basically a main loop with a scheduler of sorts that can jump among tasks, something that is referred by Sony as threading (but it's really not). So my question would be: is there any way on PC to preserve the machine state, jump to a section of code and keep that in sync between critical sections? Here's a sample of what I mean:
int main()
{
Init_scheduler();
Init_system();
Task_execute(0,Init_main);

while(1)
{
Rnd();
// reset pool
GfxResetPacket();
// cache pad reads
Read_pad();
// set sort lists
Clear_sort_lists();
// progress with threads
Scheduler();
// carry fade effects
System_trans();
// display primitives and flip buffers
Sort_display();
}

return 0;
}

This is my game loop, which keeps executing between frame VSync. Now, what Scheduler() does there is fetching tasks and returning to the main loop as soon as a "sleep" signal is hit. Here's an example of tasks altering code flow:
void Init_main()
{
Trans_init();
Task_chain(Game_loop);
}

void Game_loop()
{
while(1)
{
if(!(G.System_flg & SYSFLG_STOP))
{
Pl_move();
Sca_ck_em((EM_WORK*)&Pl_work,0x8000);
Enemy_move();
}

Sp_to_scratch();
Update_entity((EM_WORK*)&G.Player);
for(i=0; i<MAX_ENEMIES; i++)
if(G.pEnemy[i])
Update_entity(G.pEnemy[i]);
Sp_restore();
Task_sleep(1);
}

Load_overlay(OVR_TITLE,CD_READ_SYNC);
}

Task_sleep() is a function that takes a timer as a parameter to tell the scheduler how many vsync cycles it has to wait before code can be executed again from the point where it left, keeping the CPU state almost exactly like it was. The moment Task_sleep is called, my code returns to the body of Scheduler() and carries on with the following tasks if they are active. Usually the first task must always be active in order to have anything moving on screen, which is what Task_execute(0,Init_main) does. Task_execute() sets a function to the task list (first parameter is the priority of it, ranging from 0 to 2, where 0 means it's the first to be executed and 2 means the last), while Task_chain reroutes the program counter to another function by effectively taking the current task and changing code flow in a critical section, almost like an interrupt.

I tried searching for many approaches and apparently semaphores and mutex seem the closest thing, but I'm not entirely sure if they are even remotely similar, given the structure of my code so far.

Nightcrawler

I have some experience with FreeRTOS, which is a glorified task scheduler in C that runs on many, many architectures and is open source. You'll be able to find many good ideas as far as task scheduling, semaphores, and mutexes go after reading about it's architecture and delving into some of the source code. I can't recall now for sure, but I want to say they had to use inline assembly to handle some of the critical details such as the state control you are mentioning.

You will probably end up needing semaphores type mechanisms to control direct hardware access from your various tasks.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

Gemini

Extremely interesting piece of code, thanks! I'll let you know if I can find a way to integrate it into the engine, though for now the task source does indeed look like what I'm looking for. :o

theloon

I'm a PEEK'er. I'm a POKE'er. And I'm a midnight coder. I type my programs and hit RUN.


Check out itch.io for my Atari 2600 game ROMs! http://theloon.itch.io/

Gemini

I actually found a better solution: custom assembly implementations based on the principle of setjmp and longjmp. They do exactly what I needed and are extremely simple to use with no code edits whatsoever. :)

henke37

This reminds me of bsnes and the cooperative threading.