mtask.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. //////////////////////////////////////////////////////////////////////
  19. //
  20. // MTask.cpp
  21. //
  22. // Multitasking module for game run routines.
  23. //
  24. // This multitasking module allows you to write character
  25. // logic in a more linear fashion. Traditionally run routines
  26. // were written as a series of switch statements based on
  27. // some saved state for that character. They would run
  28. // through the logic, going through the switch statement
  29. // based on their state, and would exit at the end via
  30. // return statement. Then the next time they were called
  31. // to run, they started at the beginning of the function and
  32. // examined the state again etc.
  33. //
  34. // Using this multitasking module, you can write logic
  35. // in such a way that it seems as if the logic code is the
  36. // only code running. Rather than having an exit point
  37. // at the end of the logic, you can write the code that
  38. // doesn't seem to exit. At various points in the logic,
  39. // you must call MTaskWait which allows your task to be
  40. // switched out and allows other tasks to run. The MTaskWait
  41. // call can be put anywhere in your logic code, and when it
  42. // is time for your task to run again, it will resume with
  43. // its state restored, right after the MTaskWait call.
  44. //
  45. // Created On: 10/18/96 BRH
  46. // Implemented:10/17/96 BRH
  47. //
  48. // History:
  49. //
  50. // 10/17/96 BRH Started a test version in another file to
  51. // try the stack swapping and task switching.
  52. //
  53. // 10/18/96 BRH Started this file and imported the test
  54. // functions dealing with the Multitasking.
  55. // Renamed all of the functions for the
  56. // Multitasking module MTask____.
  57. //
  58. // 11/08/96 JMI Changed CList to RList in one location.
  59. //
  60. //////////////////////////////////////////////////////////////////////
  61. //////////////////////////////////////////////////////////////////////
  62. // Includes
  63. //////////////////////////////////////////////////////////////////////
  64. #include <stdlib.h>
  65. #include <string.h>
  66. #ifdef PATHS_IN_INCLUDES
  67. #include "ORANGE/MTask/mtask.h"
  68. #else
  69. #include "MTASK.H"
  70. #endif //PATHS_IN_INCLUDES
  71. //////////////////////////////////////////////////////////////////////
  72. // "Member" variables (Globals)
  73. //////////////////////////////////////////////////////////////////////
  74. static long m_lMainProgramStack; // save Main program stack here
  75. static long m_lTaskStack; // save Current task stack here
  76. static PTASKINFO ptiCurrentTask; // Pointer to current task for
  77. // use in error reporting
  78. static RList<TASKINFO> MTaskList;
  79. //////////////////////////////////////////////////////////////////////
  80. // Static Functions
  81. //////////////////////////////////////////////////////////////////////
  82. // This function is to be called from within the task's
  83. // process. It is used to transfer control to other tasks.
  84. // Your task will resume immediately following this call,
  85. // when its turn comes up again.
  86. static long* MTaskRun(void);
  87. // This function is an error trap in case a task
  88. // returns. Tasks are not supposed to return, they
  89. // call MTaskKill when they are done.
  90. static void MTaskReturnCatcher(void);
  91. //////////////////////////////////////////////////////////////////////
  92. //
  93. // MTaskManager
  94. //
  95. // Description:
  96. // This is the routine that is called in the main loop of
  97. // the application. It will run through all allocated
  98. // tasks and switch the stack for the task and let it
  99. // continue running where it left off. Once all of the
  100. // tasks have been processed once, the manager will return
  101. // back to the main loop of the application.
  102. //
  103. // Parameters:
  104. // none
  105. //
  106. // Returns:
  107. // none
  108. //
  109. //////////////////////////////////////////////////////////////////////
  110. void MTaskManager(void)
  111. {
  112. PTASKINFO ptiTask = MTaskList.GetHead();
  113. while (ptiTask)
  114. {
  115. ASSERT(ptiTask->plSP != NULL);
  116. // Set current task for error reporting & killing
  117. ptiCurrentTask = ptiTask;
  118. m_lTaskStack = (long) ptiTask->plSP;
  119. ptiTask->plSP = MTaskRun();
  120. if (ptiTask->plSP == 0)
  121. {
  122. ASSERT(ptiTask->plStackAddress != NULL);
  123. free(ptiTask->plStackAddress);
  124. ASSERT(ptiTask->pszFunctionName != NULL);
  125. free(ptiTask->pszFunctionName);
  126. MTaskList.Remove(ptiTask);
  127. delete ptiTask;
  128. }
  129. ptiTask = MTaskList.GetNext();
  130. }
  131. }
  132. //////////////////////////////////////////////////////////////////////
  133. //
  134. // MTaskAdd
  135. //
  136. // Description:
  137. // Adds a new task to the list of tasks to be processed. Note
  138. // that functions passed to this routine need to be specifically
  139. // designed to work with that task manager. That means that they
  140. // must periodically call MTaskWait to give up time to other
  141. // tasks, otherwise they will end up being the only task ever to
  142. // run. Also, they must never return. Once they are added
  143. // with MTaskAdd, they should run until they are no longer
  144. // needed at which time they should call MTaskKill to take them
  145. // off of the task list. If a task ever issues a return,
  146. // this module will catch it and ASSERT the failure.
  147. //
  148. // Parameters:
  149. // void* pFunction = pointer to the task you wish to add.
  150. //
  151. // Returns:
  152. // SUCCESS if a task was allocated and added
  153. // FAILURE if memory could not be allocated for the task
  154. //
  155. //////////////////////////////////////////////////////////////////////
  156. short MTaskAddFunc(void* pFunction, char* pszFuncName, short sStackSize)
  157. {
  158. PTASKINFO ptiNewTask = NULL;
  159. long* plNewStack = NULL;
  160. short sLongElements = sStackSize/4;
  161. short sReturn = SUCCESS;
  162. ptiNewTask = new TASKINFO;
  163. if (ptiNewTask != NULL)
  164. {
  165. plNewStack = (long*) calloc(sLongElements, 4);
  166. if (plNewStack)
  167. {
  168. plNewStack[sLongElements-1] = (long) MTaskReturnCatcher;
  169. plNewStack[sLongElements-2] = (long) pFunction;
  170. plNewStack[sLongElements-3] = 0; //bp
  171. ptiNewTask->plStackAddress = plNewStack;
  172. ptiNewTask->plSP = (plNewStack + (sLongElements-3));
  173. ptiNewTask->pszFunctionName = (char*) calloc(sizeof(pszFuncName), 1);
  174. strcpy(ptiNewTask->pszFunctionName, pszFuncName);
  175. }
  176. else
  177. {
  178. TRACE("MTaskAdd - Error allocating stack for new task\n");
  179. sReturn = FAILURE;
  180. }
  181. MTaskList.Add(ptiNewTask);
  182. }
  183. else
  184. {
  185. TRACE("MTaskAdd - Error allocating a new task info structure\n");
  186. sReturn = FAILURE;
  187. }
  188. return sReturn;
  189. }
  190. //////////////////////////////////////////////////////////////////////
  191. //
  192. // MTaskKill
  193. //
  194. // Description:
  195. // Removes the task from the list. This should only be called
  196. // from within the currently running task. It calls this when
  197. // it is done running, for example, if the task is a run
  198. // routine for character logic and the character dies, then
  199. // it would call this funtion to take it off of the task list.
  200. // This funcition kills the ptiCurrentTask.
  201. //
  202. // This function is called in place of a normal MTaskWait,
  203. // and therefore returns the current tasks's stack pointer.
  204. // This routine always returns 0 to indicate that the task
  205. // should be killed which is actually cleaned up in the
  206. // MTaskManager routine when it gets a return SP valuie of 0.
  207. //
  208. // Parameters:
  209. // none
  210. //
  211. // Returns:
  212. // 0 to indicate that the task should be deleted
  213. //
  214. //////////////////////////////////////////////////////////////////////
  215. __declspec (naked) long* TaskKill(void)
  216. {
  217. __asm
  218. {
  219. mov esp,m_lMainProgramStack ;restore the program's stack
  220. pop ebp ;restore programs's stack frame
  221. mov eax,0h ;return NULL to flag as
  222. mov dx,0h ;ready to delete
  223. ret
  224. }
  225. }
  226. //////////////////////////////////////////////////////////////////////
  227. //
  228. // MTaskWait
  229. //
  230. // Description:
  231. // Switches back to the program's main stack and returns
  232. // to MTaskManager where it left off in the loop after it
  233. // called MTaskRun. The MTaskManager calls the MTaskRun
  234. // which switches stacks and resumes the task. Then the
  235. // task calls this function which switches back to the main
  236. // program stack and returns the task's stack pointer to
  237. // MTaskManager which then can switch to the next task.
  238. //
  239. // Parameters:
  240. // none
  241. //
  242. // Returns:
  243. // SP pointer location for the task just run
  244. //
  245. //////////////////////////////////////////////////////////////////////
  246. __declspec (naked) long* MTaskWait(void)
  247. {
  248. __asm
  249. {
  250. push ebp ;save task's frame pointer
  251. mov m_lTaskStack,esp ;Save task's stack pointer
  252. mov esp,m_lMainProgramStack ;restore the program's main stack
  253. pop ebp ;restore Real Stack's frame pointer
  254. mov eax,m_lTaskStack ;put current stack in ax
  255. mov edx,m_lTaskStack ;put current stack in dx
  256. shr edx,16 ;move high word to low word of register
  257. ret
  258. }
  259. }
  260. //////////////////////////////////////////////////////////////////////
  261. //
  262. // MTaskRun
  263. //
  264. // Description:
  265. // Switches from the main stack to the task's stack and
  266. // then returns to where the task was last running.
  267. //
  268. // Parameters:
  269. // none
  270. //
  271. // Returns:
  272. // Note: The function seems to return a long of the
  273. // task's stack pointer, but in fact, the return
  274. // value does not come from this function, but from
  275. // MTaskWait or MTaskKill.
  276. //
  277. //////////////////////////////////////////////////////////////////////
  278. __declspec (naked) long* MTaskRun(void)
  279. {
  280. __asm
  281. {
  282. push ebp ;Save Real Stack's frame pointer
  283. mov m_lMainProgramStack,esp ;Save current stack
  284. mov esp,m_lTaskStack ;Set task's stack
  285. pop ebp ;restore task's frame pointer
  286. ret
  287. }
  288. }
  289. //////////////////////////////////////////////////////////////////////
  290. //
  291. // MTaskReturnCatcher
  292. //
  293. // Description:
  294. // Catches tasks that have returned when they were not
  295. // supposed to. Because we are switching tasks and stacks,
  296. // if a task returns, it would not clean up its stack and
  297. // free its task. Therefore tasks should never return. They
  298. // should call MTaskKill when they are done to clean up
  299. // their task and stack. If they do accidently return, they
  300. // will come here, where the program will hang and report
  301. // an error.
  302. //
  303. // Parameters:
  304. // none
  305. //
  306. // Returns:
  307. // never returns;
  308. //
  309. //////////////////////////////////////////////////////////////////////
  310. void MTaskReturnCatcher(void)
  311. {
  312. TRACE("MTask - The task %s has returned rather than calling MTaskKill\n", ptiCurrentTask->pszFunctionName);
  313. ASSERT(FALSE);
  314. }