home *** CD-ROM | disk | FTP | other *** search
/ ftp.whtech.com / ftp.whtech.com.7z / ftp.whtech.com / emulators / v9t9 / linux / sources / V9t9 / source / v9t9_module.c < prev    next >
Encoding:
C/C++ Source or Header  |  2006-10-19  |  22.2 KB  |  960 lines

  1.  
  2. /*
  3.  *    Handle v9t9 modules.
  4.  *
  5.  *    The module concept is used here to separate the guts of the emulator
  6.  *    from the particularities of the host executing environment.  Without
  7.  *    modules, the emulator is a silent, invisible, noniteractive state machine.
  8.  *    Modules define the interface between the 99/4A state machine and the 
  9.  *    display, sound, keyboard, and I/O devices of the host.
  10.  *
  11.  */
  12.  
  13. #include <assert.h>
  14.  
  15. #include "v9t9_common.h"
  16.  
  17. #include "command.h"
  18. #include "v9t9_module.h"
  19.  
  20. /*    These are lists of modules of the given type.  Except (currently) for
  21.     vmCPU and vmVideo, any of the types may have more than one active
  22.     module at one time.  For instance, there can be two separate sound
  23.     devices [maybe one for MIDI output of the sound generator, and another
  24.     for digitized data output]. */
  25.  
  26. vmModule   *vmCPU, *vmVideo, *vmKeyboard, *vmSound, *vmDSR;
  27.  
  28. /*    Currently active main module */
  29. vmModule   *vmCPUMain, *vmVideoMain, *vmKeyboardMain, *vmSoundMain,
  30.  
  31.     *vmDSRMain;
  32.  
  33. static char *vmErrors[] = {
  34.     "no error",
  35.     "not available",
  36.     "configuration error",
  37.     "module disabled",
  38.     "internal error"
  39. };
  40.  
  41. static char *vmTypeNames[] = {
  42.     "CPU",
  43.     "video",
  44.     "sound",
  45.     "keyboard",
  46.     "DSR"
  47. };
  48.  
  49. #define MAX_MODULES    64
  50.  
  51. static vmModule *modules[MAX_MODULES];
  52. static int  nmodules;
  53.  
  54. #define _L     LOG_MODULES | LOG_INFO
  55. #define _LL     LOG_MODULES | LOG_ERROR
  56.  
  57. static int  
  58. AddModule(vmModule * module)
  59. {
  60.     if (nmodules >= MAX_MODULES) {
  61.         logger(_LL | 0, "AddModule:  too many modules defined");
  62.         return 0;
  63.     }
  64.  
  65.     if (module->name == NULL) {
  66.         logger(_LL | 0, "AddModule:  module has no name\n");
  67.         return 0;
  68.     }
  69.  
  70.     if (module->version != VM_CURRENT_VERSION) {
  71.         logger(_LL | 0,
  72.                 "AddModule:  module '%s' has wrong module version (%d != %d)\n\n",
  73.                 module->name, module->version, VM_CURRENT_VERSION);
  74.         return 0;
  75.     }
  76.  
  77.     if (module->m.generic == NULL) {
  78.         logger(_LL | 0, "AddModule: module '%s' has no specific module pointer\n",
  79.                 module->name);
  80.         return 0;
  81.     }
  82.  
  83.     switch (module->type) {
  84.     case vmTypeCPU:
  85.         if (module->m.cpu->version != VMCPU_CURRENT_VERSION) {
  86.             logger(_LL | 0, "AddModule: CPU module '%s' has wrong version (%d)\n",
  87.                     module->name, module->m.cpu->version);
  88.             return 0;
  89.         }
  90.         if (!module->m.cpu->execute || !module->m.cpu->stop) {
  91.             logger(_LL | 0, "AddModule: CPU module '%s' has NULL callback(s)\n",
  92.                     module->name);
  93.             return 0;
  94.         }
  95.         break;
  96.  
  97.     case vmTypeVideo:
  98.         if (module->m.video->version != VMVIDEO_CURRENT_VERSION) {
  99.             logger(_LL | 0, "AddModule: video module '%s' has wrong version (%d)\n",
  100.                     module->name, module->m.video->version);
  101.             return 0;
  102.         }
  103.         if (!module->m.video->updatelist || !module->m.video->resize ||
  104.             !module->m.video->setfgbg || !module->m.video->setblank ||
  105.             !module->m.video->resetfromblank) {
  106.             logger(_LL | 0, "AddModule: Video module '%s' has NULL callback(s)\n",
  107.                     module->name);
  108.             return 0;
  109.         }
  110.         break;
  111.  
  112.     case vmTypeSound:
  113.         if (module->m.sound->version != VMSOUND_CURRENT_VERSION) {
  114.             logger(_LL | 0, "AddModule: sound module '%s' has wrong version (%d)\n",
  115.                     module->name, module->m.sound->version);
  116.             return 0;
  117.         }
  118.         if (!(module->flags & vmFlagsExclusive) &&
  119.             (module->m.sound->update == NULL &&
  120.              module->m.sound->flush == NULL && 
  121.              module->m.sound->play == NULL && 
  122.              module->m.sound->read == NULL)) {
  123.             logger(_LL | 0,
  124.                     "AddModule: '%s' is an invalid sound module (has no callbacks "
  125.                     "and is not exclusive), ignoring\n", module->name);
  126.             return 0;
  127.         }
  128.         break;
  129.  
  130.     case vmTypeKeyboard:
  131.         if (module->m.kbd->version != VMKEYBOARD_CURRENT_VERSION) {
  132.             logger(_LL | 0,
  133.                     "AddModule: keyboard module '%s' has wrong version (%d)\n",
  134.                     module->name, module->m.kbd->version);
  135.             return 0;
  136.         }
  137.         if (!module->m.kbd->scan || !module->m.kbd->getspecialkeys) {
  138.             logger(_LL | 0, "AddModule: Keyboard module '%s' has NULL callback(s)\n",
  139.                     module->name);
  140.             return 0;
  141.         }
  142.         break;
  143.  
  144.     case vmTypeDSR:
  145.         if (module->m.dsr->version != VMDSR_CURRENT_VERSION) {
  146.             logger(_LL | 0, "AddModule: DSR module '%s' has wrong version (%d)\n",
  147.                     module->name, module->m.dsr->version);
  148.             return 0;
  149.         }
  150.         if (!module->m.dsr->getcrubase || !module->m.dsr->filehandler) {
  151.             logger(_LL | 0, "AddModule: DSR module '%s' has NULL callback(s)\n",
  152.                     module->name);
  153.             return 0;
  154.         }
  155.         break;
  156.  
  157.         logger(_LL | 0, "AddModule:  module '%s' has unknown type (%d)\n",
  158.                 module->name, module->type);
  159.         return 0;
  160.     }
  161.  
  162.     if (module->detect == NULL ||
  163.         module->init == NULL ||
  164.         module->term == NULL ||
  165.         module->enable == NULL ||
  166.         module->disable == NULL ||
  167.         module->restart == NULL || module->restop == NULL) {
  168.         logger(_LL | 0, "AddModule:  callback(s) missing in '%s'\n", module->name);
  169.         return 0;
  170.     }
  171.  
  172.     modules[nmodules++] = module;
  173.     return 1;
  174. }
  175.  
  176. /*********************************/
  177.  
  178. int
  179. vmModulesInit(void)
  180. {
  181.     nmodules = 0;
  182.     return 1;
  183. }
  184.  
  185. int
  186. vmModulesTerm(void)
  187. {
  188.     vmTermModules();
  189.     nmodules = 0;
  190.     return 1;
  191. }
  192.  
  193. int
  194. vmRegisterModules(vmModule ** module)
  195. {
  196.     vmModule  **mptr;
  197.  
  198.     for (mptr = module; *mptr; mptr++)
  199.         if (!AddModule(*mptr))
  200.             return 0;
  201.     return 1;
  202. }
  203.  
  204. /*********************************/
  205.  
  206. vmResult
  207. vmDetectModule(vmModule * module)
  208. {
  209.     vmResult    res;
  210.  
  211.     assert(module != NULL);
  212.     logger(_L | L_1, "vmDetectModule:  '%s'\n", module->name);
  213.     res = module->detect();
  214.     if (res != vmOk) {
  215.         // not _LL since a detection needn't succeed
  216.         logger(_L | 0, "vmDetectModule:  '%s':  %s\n", module->name, vmErrors[res]);
  217.         module->runtimeflags |= vmRTUnavailable;
  218.     }
  219.     return res;
  220. }
  221.  
  222. vmResult
  223. vmInitModule(vmModule * module)
  224. {
  225.     vmResult    res;
  226.  
  227.     assert(module != NULL);
  228.  
  229.     if (module->runtimeflags & vmRTUnavailable)
  230.         return vmOk;
  231.  
  232.     if (module->runtimeflags & vmRTUndoInit)
  233.         return vmOk;
  234.  
  235.     logger(_L | L_1, "vmInitModule:  '%s'\n", module->name);
  236.     res = module->init();
  237.     if (res != vmOk) {
  238.         logger(_LL | 0, "vmInitModule:  '%s':  %s\n", module->name, vmErrors[res]);
  239.         module->runtimeflags |= vmRTUnavailable;
  240.     } else {
  241.         module->runtimeflags |= vmRTUndoInit;
  242.     }
  243.     return res;
  244. }
  245.  
  246. vmResult
  247. vmTermModule(vmModule * module)
  248. {
  249.     vmResult    res;
  250.  
  251.     assert(module != NULL);
  252.  
  253.     if (!(module->flags & vmRTUndoInit))
  254.         return vmOk;
  255.  
  256.     module->runtimeflags &= ~vmRTUndoInit;
  257.     logger(_L | L_1, "vmTermModule:  '%s'\n", module->name);
  258.     res = module->term();
  259.     if (res != vmOk)
  260.         logger(_LL | 0, "vmTermModule:  '%s':  %s\n", module->name, vmErrors[res]);
  261.     return res;
  262. }
  263.  
  264. vmResult
  265. vmEnableModule(vmModule * module)
  266. {
  267.     vmResult    res;
  268.  
  269.     assert(module != NULL);
  270.  
  271.     if (module->runtimeflags & vmRTUnavailable)
  272.         return vmOk;
  273.  
  274.     if ((module->flags & vmFlagsOneShotEnable) &&
  275.         (module->runtimeflags & vmRTEnabledOnce)) return vmOk;
  276.  
  277.     if (module->runtimeflags & vmRTUndoEnable)
  278.         return vmOk;
  279.  
  280.     logger(_L | L_1, "vmEnableModule:  '%s'\n", module->name);
  281.     res = module->enable();
  282.     if (res != vmOk)
  283.         logger(_LL | 0, "vmEnableModule:  '%s':  %s\n", module->name, vmErrors[res]);
  284.     else {
  285.         module->runtimeflags |= vmRTEnabledOnce;
  286.         module->runtimeflags |= vmRTUndoEnable;
  287.     }
  288.  
  289.     return res;
  290. }
  291.  
  292. vmResult
  293. vmDisableModule(vmModule * module)
  294. {
  295.     vmResult    res;
  296.  
  297.     assert(module != NULL);
  298.  
  299.     if (!(module->runtimeflags & vmRTUndoEnable))
  300.         return vmOk;
  301.  
  302.     logger(_L | L_1, "vmDisableModule:  '%s'\n", module->name);
  303.     module->runtimeflags &= ~vmRTUndoEnable;
  304.     res = module->disable();
  305.     if (res != vmOk)
  306.         logger(_LL | 0, "vmDisableModule:  '%s':  %s\n",
  307.                 module->name, vmErrors[res]);
  308.  
  309.     return res;
  310. }
  311.  
  312. vmResult
  313. vmRestartModule(vmModule * module)
  314. {
  315.     vmResult    res;
  316.  
  317.     assert(module != NULL);
  318.  
  319.     if (!(module->runtimeflags & vmRTInUse))
  320.         return vmOk;
  321.  
  322.     if (!(module->runtimeflags & vmRTEnabledOnce))
  323.         return vmOk;
  324.  
  325.     if (module->runtimeflags & vmRTUndoRestart)
  326.         return vmOk;
  327.  
  328.     logger(_L | L_1, "vmRestartModule:  '%s'\n", module->name);
  329.     res = module->restart();
  330.     if (res != vmOk)
  331.         logger(_LL | 0, "vmRestartModule:  '%s':  %s\n",
  332.                 module->name, vmErrors[res]);
  333.     else
  334.         module->runtimeflags |= vmRTUndoRestart;
  335.  
  336.     return res;
  337. }
  338.  
  339. vmResult
  340. vmRestopModule(vmModule * module)
  341. {
  342.     vmResult    res;
  343.  
  344.     assert(module != NULL);
  345.  
  346.     if (!(module->runtimeflags & vmRTInUse))
  347.         return vmOk;
  348.  
  349.     if (!(module->runtimeflags & vmRTEnabledOnce))
  350.         return vmOk;
  351.  
  352.     if (!(module->runtimeflags & vmRTUndoRestart))
  353.         return vmOk;
  354.  
  355.     logger(_L | L_1, "vmRestopModule:  '%s'\n", module->name);
  356.     res = module->restop();
  357.     if (res != vmOk)
  358.         logger(_LL | 0, "vmRestopModule:  '%s':  %s\n", module->name, vmErrors[res]);
  359.     else
  360.         module->runtimeflags &= ~vmRTUndoRestart;
  361.  
  362.     return res;
  363. }
  364.  
  365. /********************/
  366.  
  367. static int
  368. CollectModules(vmModule ** list, vmType type, bool required)
  369. {
  370.     int         found = 0;
  371.     int         x;
  372.  
  373.     *list = NULL;
  374.     for (x = 0; x < nmodules; x++)
  375.         if (modules[x]->type == type && vmDetectModule(modules[x]) == vmOk) {
  376.             *list = modules[x];
  377.             list = &(*list)->next;
  378.             found++;
  379.         }
  380.     *list = NULL;                /* terminate list */
  381.  
  382.     if (!found && required)
  383.         logger(_L | LOG_FATAL, "CollectModules:  Could not find a %s module\n",
  384.              vmTypeNames[type]);
  385.  
  386.     return found;
  387. }
  388.  
  389. /*    Detect all applicable modules */
  390. int
  391. vmDetectModules(void)
  392. {
  393.     logger(_L | L_1, "vmDetectModules");
  394.  
  395.     /*  CPU modules  */
  396.     if (!CollectModules(&vmCPU, vmTypeCPU, true))
  397.         return 0;
  398.  
  399.     /*  Video modules */
  400.     if (!CollectModules(&vmVideo, vmTypeVideo, true))
  401.         return 0;
  402.  
  403.     /*  Sound modules */
  404.     if (!CollectModules(&vmSound, vmTypeSound, true))
  405.         return 0;
  406.  
  407.     /*  Keyboard modules */
  408.     if (!CollectModules(&vmKeyboard, vmTypeKeyboard, true))
  409.         return 0;
  410.  
  411.     /*  DSR modules */
  412.     CollectModules(&vmDSR, vmTypeDSR, false);
  413.  
  414.     return 1;
  415. }
  416.  
  417. /*    Select startup (or re-startup) modules.  
  418.     -- We are allowed to use more than one module of one type
  419.     at once if none has the vmFlagsExclusive flag set.
  420.     -- For sound modules, we also make the restriction that
  421.     we only use as many modules as will satisfy both callbacks;
  422.     if the first module selected defines one, that one will
  423.     be ignored even if defined in a second selected one.
  424.     -- For simplicity, we assume that the list of modules
  425.     is ordered by priority and stop with the first workable
  426.     combination.
  427. */
  428.  
  429. static void
  430. SelectModules(vmModule * list, vmModule ** main, vmType type, 
  431.               bool startup, bool exclusive)
  432. {
  433.     vmModule   *ptr, *prev;
  434.     int         chained = 0;    /* already using a module? */
  435.     int         termed = 0;        /* not looking for new modules */
  436.  
  437.     for (ptr = list, prev = NULL; ptr; ptr = ptr->next) {
  438.         /* clear flag */
  439.         ptr->runtimeflags &= ~vmRTInUse;
  440.  
  441.         if (termed) {
  442.             continue;
  443.         }
  444.  
  445.         if (ptr->runtimeflags & vmRTUnavailable) {
  446.             logger(_L | L_1, "SelectModules: '%s' is unavailable, skipping\n",
  447.                  ptr->name);
  448.             continue;
  449.         }
  450.  
  451.         if (ptr->runtimeflags & vmRTUnselected) {
  452.             logger(_L | L_1, "SelectModules: '%s' is unselected, skipping\n",
  453.                  ptr->name);
  454.             continue;
  455.         }
  456.  
  457.         if (ptr->flags & vmFlagsExclusive) {
  458.             if (chained) {
  459.                 /* already using non-exclusive one */
  460.                 logger(_L | L_1,
  461.                      "SelectModules: not using '%s' since non-exclusive module already in use\n",
  462.                      ptr->name);
  463.                 continue;
  464.             }
  465.             /*  else, select this one,
  466.                and one-by-one ignore each of the rest */
  467.         } else
  468.             /* sound has special semantics */
  469.         if (type == vmTypeSound && prev != NULL) {
  470.             /* if we're here, then the 'prev' module defines
  471.                one callback; only select a module that defines 
  472.                the other one */
  473.             if ((prev->m.sound->update == NULL &&
  474.                  ptr->m.sound->update == NULL)
  475.                 ||
  476.                 ((prev->m.sound->play == NULL) &&
  477.                  (ptr->m.sound->play == NULL))
  478.                 ||
  479.                 ((prev->m.sound->read == NULL) &&
  480.                  (ptr->m.sound->read == NULL))) {
  481.                 /* neither defines the missing callback */
  482.                 logger(_L | L_1,
  483.                      "SelectModules: not using '%s' since it doesn't satisfy missing "
  484.                      "callbacks from module '%s'\n", ptr->name, prev->name);
  485.                 continue;
  486.             }
  487.         }
  488.         /* just use it */
  489.  
  490.         logger(_L|L_0, "SelectModules: using '%s'\n", ptr->name);
  491.  
  492.         if (!chained && main)
  493.             *main = ptr;
  494.  
  495.         ptr->runtimeflags |= vmRTInUse;
  496.         if (!startup) vmEnableModule(ptr);
  497.         chained++;
  498.         prev = ptr;
  499.  
  500.         /* did we get the only one we could use? */
  501.         if (exclusive) {
  502.             logger(_L | L_1,
  503.                  "SelectModules: terminating search, only one %s module may be active\n",
  504.                  vmTypeNames[type]);
  505.             termed = 1;
  506.             continue;
  507.         }
  508.  
  509.         if (type == vmTypeSound &&
  510.             ptr->m.sound->update != NULL && ptr->m.sound->play != NULL &&
  511.             ptr->m.sound->read != NULL) {
  512.             logger(_L | L_1, "SelectModule: terminating search since '%s' defines "
  513.                  "all sound callbacks\n", ptr->name);
  514.             termed = 1;
  515.             continue;
  516.         }
  517.     }
  518.  
  519.     if (!chained && prev != NULL)
  520.         logger(_L | LOG_FATAL,
  521.              "SelectModules:  did not find a single usable %s module?\n",
  522.              vmTypeNames[type]);
  523. }
  524.  
  525.  
  526. /*    The user has opted to select a custom
  527.     list of modules.  Validate each combination.
  528.     If the combination fails, simply call SelectModules()
  529.     again to return to the default state. 
  530.     
  531.     Valid list:  -- no two are vmTypeExclusive
  532.     -- none has vmRTUnselected set
  533.     -- for sound, no conflicting callbacks 
  534.     
  535.     AND, an empty list is valid if "incremental" is set.
  536. */
  537.  
  538. static int
  539. ValidateModules(vmModule * list, vmType type, bool required,
  540.                 bool exclusive, bool incremental)
  541. {
  542.     vmModule   *ptr, *prev;
  543.     int         chained = 0;    /* already using a module? */
  544.  
  545.     ptr = list;
  546.     prev = NULL;
  547.     while (ptr) {
  548.         if (!(ptr->runtimeflags & (vmRTUnselected | vmRTUnavailable))) {
  549.             /* did we get the only one we could use? */
  550.             if (chained && exclusive) {
  551.                 logger(_LL | 0, "ValidateModules: '%s' must not be selected since "
  552.                         "only one %s module may be active at once\n", ptr->name,
  553.                         vmTypeNames[type]);
  554.                 return 0;
  555.             }
  556.  
  557.             if (ptr->flags & vmFlagsExclusive) {
  558.                 if (chained) {
  559.                     /* already using non-exclusive one, fail */
  560.                     logger(_LL | 0,
  561.                             "ValidateModules: '%s' must not be selected since "
  562.                             "other non-exclusive modules are already selected\n",
  563.                             ptr->name);
  564.                     return 0;
  565.                 }
  566.             } else
  567.                 /* sound has special semantics */
  568.             if (type == vmTypeSound && prev != NULL) {
  569.                 if (((prev->m.sound->update != NULL) &&
  570.                      (ptr->m.sound->update != NULL))
  571.                     ||
  572.                     ((prev->m.sound->play != NULL) &&
  573.                      (ptr->m.sound->play != NULL))
  574.                     ||
  575.                     ((prev->m.sound->read != NULL) &&
  576.                      (ptr->m.sound->read != NULL))) {
  577.                     /* both have the same callback defined */
  578.                     logger(_LL | 0,
  579.                             "ValidateModules: '%s' must not be selected since module "
  580.                             "'%s' defines the same callbacks\n", ptr->name,
  581.                             prev->name);
  582.                     return 0;
  583.                 }
  584.             }
  585.             // success, so far
  586.  
  587. //          // allow selection of disabled module
  588. //          ptr->runtimeflags &= ~vmRTUnselected;
  589.  
  590.             chained++;
  591.             prev = ptr;
  592.         }
  593.  
  594.         ptr = ptr->next;
  595.  
  596.     }
  597.  
  598.     if (!chained && !incremental && required) {
  599.         logger(_LL | 0, "ValidateModules: no %s modules are selected; "
  600.                 "at least one is required\n", vmTypeNames[type]);
  601.         return 0;
  602.     }
  603.  
  604.     return 1;
  605. }
  606.  
  607. static vmResult
  608. IterModuleList(vmModule * ptr, vmResult(*vmFunc) (vmModule *))
  609. {
  610.     vmResult    res = vmOk, ret = vmOk;
  611.  
  612.     while (ptr) {
  613.         if (!(ptr->runtimeflags & (vmRTUnavailable | vmRTUnselected))) {
  614.             if ((res = vmFunc(ptr)) != vmOk)
  615.             {
  616.                 if (res == vmNotAvailable)
  617.                     ptr->runtimeflags |= vmRTUnavailable;
  618.                 else
  619.                     ret = res;
  620.             }
  621.         }
  622.         ptr = ptr->next;
  623.     }
  624.     return ret;
  625. }
  626.  
  627. static vmResult
  628. IterModuleLists(vmModule ** list[], vmResult(*vmFunc) (vmModule *))
  629. {
  630.     vmResult    res = vmOk, ret = vmOk;
  631.     int         step;
  632.  
  633.     step = 0;
  634.     while (list && list[step]) {
  635.         vmModule   *ptr = *(list[step]);
  636.  
  637.         if ((res = IterModuleList(ptr, vmFunc)) != vmOk)
  638.             ret = res;
  639.         step++;
  640.     }
  641.  
  642.     return ret;
  643. }
  644.  
  645.  
  646. int
  647. vmValidateModuleSelection(bool startup, bool incremental)
  648. {
  649.     logger(_L | L_1, "vmValidateModuleSelection\n");
  650.  
  651.     /*  CPU modules  */
  652.     SelectModules(vmCPU, &vmCPUMain, vmTypeCPU, startup, true /*exclusive*/);
  653.  
  654.     /*  Video modules */
  655.     SelectModules(vmVideo, &vmVideoMain, vmTypeVideo, startup, false /*exclusive*/);
  656.  
  657.     /*  Sound modules */
  658.     IterModuleList(vmSound, vmRestopModule);
  659.     SelectModules(vmSound, &vmSoundMain, vmTypeSound, startup, false /*exclusive*/);
  660.     IterModuleList(vmSound, vmRestartModule);
  661.  
  662.     /*  Keyboard modules */
  663.     SelectModules(vmKeyboard, &vmKeyboardMain, vmTypeKeyboard, startup, false /*exclusive*/);
  664.  
  665.     /*  DSR modules */
  666.     SelectModules(vmDSR, NULL, vmTypeDSR, startup, false /*exclusive*/);
  667.  
  668.     /*  Reset our features mask. */
  669.     features &= ~(FE_VIDEO | FE_KEYBOARD | FE_SOUND | FE_SPEECH);
  670.  
  671.     /*  These are always available. */
  672.     features |= FE_VIDEO | FE_KEYBOARD;
  673.  
  674.     /*  These depend on the union of callbacks our sound modules define. */
  675.     if (vmSound->m.sound->update != NULL ||
  676.         (vmSound->next && vmSound->next->m.sound->update != NULL))
  677.         features |= FE_SOUND;
  678.     if (vmSound->m.sound->play != NULL ||
  679.         (vmSound->next && vmSound->next->m.sound->play != NULL))
  680.         features |= FE_SPEECH | FE_CSXWRITE;
  681.     if (vmSound->m.sound->read != NULL ||
  682.         (vmSound->next && vmSound->next->m.sound->read != NULL))
  683.         features |= FE_CSXREAD;
  684.  
  685.     return 1;
  686. }
  687.  
  688. int
  689. vmSelectStartupModules(void)
  690. {
  691.     return vmValidateModuleSelection(true, false);
  692. }
  693.  
  694. int
  695. vmInitModules(void)
  696. {
  697.     static vmModule **initList[] = {
  698.         &vmKeyboard, &vmSound, &vmVideo, &vmCPU, &vmDSR, NULL
  699.     };
  700.  
  701.     logger(_L | L_1, "vmInitModules\n");
  702.     return IterModuleLists(initList, vmInitModule) == vmOk;
  703. }
  704.  
  705. int
  706. vmTermModules(void)
  707. {
  708.     static vmModule **termList[] = {
  709.         &vmDSR, &vmVideo, &vmCPU, &vmKeyboard, &vmSound, NULL
  710.     };
  711.  
  712.     logger(_L | L_1, "vmTermModules\n");
  713.     return IterModuleLists(termList, vmTermModule) == vmOk;
  714. }
  715.  
  716. int
  717. vmEnableModules(void)
  718. {
  719.     static vmModule **enableList[] = {
  720.         &vmVideo, &vmKeyboard, &vmSound, &vmDSR, &vmCPU, NULL
  721.     };
  722.  
  723.     logger(_L | L_1, "vmEnableModules\n");
  724.     return IterModuleLists(enableList, vmEnableModule) == vmOk;
  725. }
  726.  
  727. int
  728. vmDisableModules(void)
  729. {
  730.     static vmModule **disableList[] = {
  731.         &vmCPU, &vmDSR, &vmSound, &vmKeyboard, &vmVideo, NULL
  732.     };
  733.  
  734.     logger(_L | L_1, "vmDisableModules\n");
  735.     return IterModuleLists(disableList, vmDisableModule) == vmOk;
  736. }
  737.  
  738. int
  739. vmRestartModules(void)
  740. {
  741.     static vmModule **restartList[] = {
  742.         &vmDSR, &vmCPU, NULL
  743.     };
  744.     int ret = 1;
  745.  
  746.     logger(_L | L_1, "vmRestartModules, features = %x\n", features);
  747.     if (features & FE_VIDEO)
  748.             ret &= (IterModuleList(vmVideo, vmRestartModule) == vmOk);
  749.     if (features & FE_KEYBOARD)
  750.             ret &= (IterModuleList(vmKeyboard, vmRestartModule) == vmOk);
  751.     if (features & FE_SOUND)
  752.             ret &= (IterModuleList(vmSound, vmRestartModule) == vmOk);
  753.  
  754.     return ret && IterModuleLists(restartList, vmRestartModule) == vmOk;
  755. }
  756.  
  757. int
  758. vmRestopModules(void)
  759. {
  760.     static vmModule **restopList[] = {
  761.         &vmCPU, &vmDSR, NULL
  762.     };
  763.     int ret = 1;
  764.  
  765.     logger(_L | L_1, "vmRestopModules, features = %x\n", features);
  766.     if (features & FE_SOUND)
  767.       ret &= (IterModuleList(vmSound, vmRestopModule) == vmOk);
  768.     if (features & FE_KEYBOARD)
  769.       ret &= (IterModuleList(vmKeyboard, vmRestopModule) == vmOk);
  770.     if (features & FE_VIDEO)
  771.       ret &= (IterModuleList(vmVideo, vmRestopModule) == vmOk);
  772.     return ret && IterModuleLists(restopList, vmRestopModule) == vmOk;
  773. }
  774.  
  775. int
  776. vmClearUserFlagsOnModules(void)
  777. {
  778.     static vmModule **allList[] = {
  779.         &vmCPU, &vmDSR, &vmSound, &vmVideo, &vmKeyboard, 0L
  780.     };
  781.     int idx = 0;
  782.     logger(_L | L_1, "vmClearUserFlagsOnModules\n");
  783.     while (allList[idx]) {
  784.         vmModule *list = *allList[idx];
  785.         while (list) {
  786.             list->runtimeflags &= ~vmRTUserModified;
  787.             list = list->next;
  788.         }
  789.         idx++;
  790.     }
  791.     return 1;
  792. }
  793.  
  794. /******************************/
  795.  
  796. vmModule   *
  797. vmLookupModule(char *tag)
  798. {
  799.     int         x;
  800.  
  801.     for (x = 0; modules[x] && x < MAX_MODULES; x++) {
  802.         if (strcasecmp(modules[x]->tag, tag) == 0)
  803.             return modules[x];
  804.     }
  805.     return NULL;
  806. }
  807.  
  808. static
  809. DECL_SYMBOL_ACTION(vm_list_v9t9_modules)
  810. {
  811.     int         x;
  812.  
  813.     for (x = 0; modules[x] && x < MAX_MODULES; x++) {
  814.         logger(LOG_USER, "Tag: '%s', name: '%s', type: '%s'\n",
  815.              modules[x]->tag, modules[x]->name, vmTypeNames[modules[x]->type]);
  816.         logger(LOG_USER, "\tFlags: ");
  817.         if (modules[x]->flags & vmFlagsExclusive)
  818.             logger(LOG_USER, "exclusive ");
  819.         if (modules[x]->flags & vmFlagsOneShotEnable)
  820.             if (modules[x]->runtimeflags & vmRTEnabledOnce)
  821.                 logger(LOG_USER, "[enable-once] ");
  822.             else
  823.                 logger(LOG_USER, "enable-once ");
  824.         if (modules[x]->flags & vmFlagsOneShotDisable)
  825.             if (modules[x]->runtimeflags & vmRTDisabledOnce)
  826.                 logger(LOG_USER, "[disable-once] ");
  827.             else
  828.                 logger(LOG_USER, "disable-once ");
  829.         if (modules[x]->runtimeflags & vmRTInUse)
  830.             logger( LOG_USER, "in-use ");
  831.         if (modules[x]->runtimeflags & vmRTUnselected)
  832.             logger( LOG_USER, "unselected ");
  833.         logger(LOG_USER, "\n");
  834.     }
  835.     return 1;
  836. }
  837.  
  838. static
  839. DECL_SYMBOL_ACTION(vm_toggle_v9t9_module)
  840. {
  841.     char       *tag;
  842.     int         onoff;
  843.     vmModule   *mod;
  844.  
  845.     if (task == csa_READ)
  846.     {
  847.         static int idx;
  848.  
  849.         if (!iter)
  850.             idx = 0;
  851.  
  852.         if (idx >= nmodules)
  853.             return 0;
  854.  
  855.         while (idx < nmodules) {
  856.             if (modules[idx]->runtimeflags & vmRTUserModified) {
  857.                 command_arg_set_string(sym->args, modules[idx]->tag);
  858.                 command_arg_set_num(sym->args->next, 
  859.                                     !(modules[idx]->runtimeflags & vmRTUnselected));
  860.                 idx++;
  861.                 return 1;
  862.             } else {
  863.                 idx++;
  864.             }
  865.         }
  866.         return 0;
  867.     }
  868.  
  869.     command_arg_get_string(sym->args, &tag);
  870.     command_arg_get_num(sym->args->next, &onoff);
  871.     mod = vmLookupModule(tag);
  872.     if (mod == NULL) {
  873.         logger(_LL | LOG_USER, "No such v9t9 module '%s'\n\n", tag);
  874.         return 0;
  875.     }
  876.  
  877.     if (!onoff) {
  878.         vmRestopModule(mod);
  879.         vmDisableModule(mod);
  880.     }
  881.  
  882.     if (onoff)
  883.         mod->runtimeflags &= ~vmRTUnselected;
  884.     else
  885.         mod->runtimeflags |= vmRTUnselected;
  886.  
  887.     mod->runtimeflags |= vmRTUserModified;
  888.  
  889.     if (!vmValidateModuleSelection(false, true))
  890.         return 0;
  891.  
  892.     if (onoff) {
  893.         if (vmInitModule(mod) != vmOk)
  894.             return 0;
  895.         if (vmEnableModule(mod) != vmOk)
  896.             return 0;
  897.         if (vmRestartModule(mod) != vmOk)
  898.             return 0;
  899.     }
  900.  
  901.     return 1;
  902. }
  903.  
  904. static
  905. DECL_SYMBOL_ACTION(vm_setup_v9t9_modules)
  906. {
  907.     return vmValidateModuleSelection(false, false);
  908. }
  909.  
  910. void
  911. vmAddModuleCommands(void)
  912. {
  913.     command_symbol_table *modules =
  914.         command_symbol_table_new("V9t9 Module Commands",
  915.                                  "These options affect the modules used to emulate V9t9",
  916.  
  917.          command_symbol_new("ListV9t9Modules",
  918.                             "List available modules and current status",
  919.                             c_DONT_SAVE,
  920.                             vm_list_v9t9_modules,
  921.                             NULL /* ret */ ,
  922.                             NULL    /* args */
  923.                             ,
  924.  
  925.         command_symbol_new("ToggleV9t9Module",
  926.                    "Turn use of a module on or off",
  927.                              c_DYNAMIC,
  928.                              vm_toggle_v9t9_module,
  929.                              NULL /* ret */ ,
  930.                              command_arg_new_string
  931.                              ("tag",
  932.                               "tag name for module",
  933.                               NULL,
  934.                               NEW_ARG_STR(64),
  935.                               command_arg_new_num
  936.                               ("on|off",
  937.                                "whether to use or not use module",
  938.                                NULL,
  939.                                NEW_ARG_NUM(bool),
  940.                                NULL /* next */ ))
  941.                              ,
  942.  
  943.        command_symbol_new("SetupV9t9Modules",
  944.                               "Setup module gestalt",
  945.                               c_DONT_SAVE,
  946.                               vm_setup_v9t9_modules,
  947.                               NULL /* ret */ ,
  948.                               NULL    /* args */
  949.                               ,
  950.  
  951.                               NULL /* next */ ))),
  952.  
  953.          NULL /* sub */ ,
  954.  
  955.          NULL    /* next */
  956.         );
  957.  
  958.     command_symbol_table_add_subtable(universe, modules);
  959. }
  960.