DOSEMU plug-ins =============== The term 'plug-in' may be wrong here, because in fact the software in the tree dosemu/src/plugin can't be plugged in at runtime but must be configured in at compiletime. The reason to have such mechanisme is to ease inclusion of code, that normally will not be part of the official DOSEMU; maybe because of copyright problems (GNU is very tricky in some cases) or because those parts are distributed separately for pratical reasons. Nevertheless, we now have it and here is how it works: ----------------------------------------------------- - all plug-ins within src/plugin can be removed or added without modification to the main DOSEMU source. You just have to make sure you do a make pristine before adding/deleting a plug-in and a ./default-configure or make after changes to the contents of src/plugin. The ./default-configure script will take care of all what is needed to insert/remove your plug-in. - you should always add/delete _complete_ plugin trees, that is, each directory (and its subdirectories) belongs to exactly one plug-in. - Configuration of each discrete plug-in is done with files under src/plugin//config, which may be plugin_enable plugin_override plugin_dirs plugin_incdirs plugin_parser plugin_lexer plugin_config.h plugin_init.h plugin_close.h plugin_inte6.h plugin_poll.h (I'll describe their meaning below). If some (or all) doesn't exist there are defaults taken. Of course, a missing plugin_enable will default to 'disable' and the plug-in is not compiled in ;-) - The *.h header files in src/plugin/*/config are accumulated into files of the same name under src/include. These are then included directly at the proper places in the dosemu code. If no plug-in exists at all, then all src/include/plugin_*.h will be empty and in a pristine DOSEMU distribution tree they won't exist at all. - The script ./mkpluginhooks in the DOSEMU root directory is responsible for actually do plug-in insertion. It is used by ./default-configure and should not be called explicitely. When called with 'clean' as the only argument, it will remove the hooks it established in a run without arguments. Within this script also is defined, which header files from the src/plugin/*/config directories are accumulated into their counterparts in src/include. To add new ones, look here. The plugin_enable file ---------------------- This file is just for convenience, if it contains 'yes' the plug-in will be compiled in, if not or not existing, the whole plug-in and its subdirectories are ignored. So, you may have the tree installed even if you don't want to compile it currently. If plugin_enable contains 'yes' _and_ it is overridden (see plugin_override below), then it will be temporary set to 'replaced' (which means disabling it) and reset to 'yes' on cleanup ('mkpluginhooks clean'). The plugin_override file ------------------------ If this file exists _and_ the plugin is enabled (see plugin_enable), it should contain a blank separated list of base names of other plugins, that this plugin is intended to replace. This feature is used to have multiple plugins in the tree wich are mutual exclusive to each other. Example: Given you wrote a new keyboard code src/plugin/my_keyboard, which completely should replace the default one in src/plugin/keyboard, than you simply need to have a src/plugin/my_keyboard/config/plugin_override with one line containing 'keyboard'. As a result the file src/plugin/keyboard/config/plugin_enable will be set to 'no' before the keyboard plugin has a chance to be included. The plugin_dirs file -------------------- In order to compile correctly within DOSEMU, Makefile.main needs to know which trees do contain a valid DOSEMU-type Makefile to produce the needed lib*.a and in order to link the stuff finaly. An example of such a Makfile can be found in src/plugin/demo/Makefile. The plugin_dirs file now defines _which_ directories under src/plugin// are considered to have such a Makefile (note: a plug-in may contain as much as subdirectories it wants), such as tools otherstuff ./ The './' means the root directory of the plug-in, and omitting it is allowed in which case all compiles will happen in the subdirectories only. All entries in plugin_dirs will produce subdir entries for Makefile.main such as plugin/myplugin/tools plugin/myplugin/otherstuff plugin/myplugin and will create libraries under src/lib such as libplugin_myplugin_tools.a libplugin_myplugin_otherstuff.a libplugin_myplugin.a If plugin_dirs is empty or doesn't exist, the content './' is assumed, which in most cases will be sufficient. The plugin_incdirs file ----------------------- If you have a plugin, which exports include files to the rest of DOSEMU (such as src/base/keyboard does), you need to list the include directory here (same format as in plugin_dirs file). The files in these directories are then symbolically linked from src/plugin/include. The plugin_parser and plugin_lexer files ---------------------------------------- It may be necessary to have some runtime configuration for your plugin, which you want to integrate in DOSEMU's configuration parser. For this you can use the merge files plugin_parser and plugin_lexer for merging into src/base/init/parser.y and lexer.l respectively. The format of plugin_parser is: |---------- colum 1 of plugin_parser ------------------------- /* Any comment going top of parser.y */ %{ /* C DECLARATIONS, such as */ #include "mystuff.h" static void mystuff(int); %} /* BISON DECLARATIONS, such as */ %token MYTOKEN %% new_rule: existing_rule | new_rule existing_rule ; existing_rule: MYTOKEN bool { mystuff($2); } | MYTOKEN '(' bool ')' { mystuff($2); } ; %% /* ADDITIONAL C CODE, such as */ static void mystuff(int foo) { whatever(); } --------------------------end of plugin_parser ------------- The above 'new_rule' will be inserted behind all existing rules in parser.y while 'existing_rule' will be merged into an existing rule. If one of the parts (between %%, %{ or %}) is empty, it needs to have one empty line at least. To check wether you did it right your stuff you may do $ cd where_I_have_dosemu_source $ ./mkpluginhooks $ diff -u src/base/init/parser.y.in src/base/init/parser.y $ ./mkpluginhooks clean The format of plugin_parser is: |---------- colum 1 of plugin_lexer ------------------------- /* new comment */ newtoken RETURN(MYTOKEN); /* existing comment */ somenewtoken RETURN(...); ------------ end of plugin_lexer Merging into lexer.l is done by searching for the comment lines in front of the lexer regular expressions. The comment lines are expected to have exactly one (\t) at colum 1 before the /* ... */. This is DOSEMU special and won't work on other lexer files (while it will for parser.y;-). If a comment already exists in lexer.l, the lines behind the comment in plugin_lexer until the next fitting comment line will be merged with the existing ones, else they are inserted (including the comment line) in front of the comment line /* strings */ of lexer.l. Again you can check this the same way you checked plugin_parser with mkpluginhooks. The plugin_*.h files -------------------- In order to hook into the DOSEMU code we include the contents of the plugin_*.h file directly at those place in DOSEMU were we want the hook. The advantage is, that it doesn't neither generate any unused code if not needed nor do we have tons of #ifdef to cope with. The script ./mkpluginhooks collects all plugin_*.h at configuration time and puts #include references into _one_ includefile with the appropriate name under src/include. e.g. the files plugin/myplugin/config/plugin_init.h plugin/yourplugin/config/plugin_init.h will be included from the file src/plugin/include/plugin_init.h which always is included in src/emu.c at startup of DOSEMU. In case no plug-in is configured, the file src/include/plugin_init.h will be empty, hence does not produce any code. The contents of any plugin_*.h file (except plugin_config.h, see below) should look like my_plugin_init(); Currently there are the following hooks for plug-ins implemented, those are: - plugin_init.h, which hooks before DOS gets booted. - plugin_close.h, which hooks at time of leavedos(). If you decided within plugin_init.h to disable the service of your plug-in, you should check that in plugin_close, DOSEMU won't do. - plugin_inte6.h, which hooks within the DOSEMU_HELPER. This hook should look such as case (DOS_HELPER_PLUGIN + XXX): { extern int my_plugin_inte6(void); if ( ! my_plugin_inte6() ) return 0; break; } (where XXX is a offset unique to your plug-in (value 0..15). It hooks before the 'default' case of dos_helper() of async/int.c such as switch (LO(ax)) { case .... break; #include "plugin_inte6.h" default: error("bad dos ....\n"); return 0; } return 1; so you see how to do it right;-) The AL values reserved for plug-ins and the DOSEMU_HELPER interrupt are 0x60..0x6f and are defined in src/include/doshelpers.h as #define DOS_HELPER_PLUGIN 0x60 #define DOS_HELPER_PLUGIN_LAST 0x6f You are responsible for assigments within this scope yourself, there is no mechanisme for allocation of unused AL values. The AH can be used for subfunctioning, so you don't need more then _one_ AL value. - plugin_poll.h, which hooks within run_vm86() of do_vm86.c and run_dpmi() of dpmi.c. This hook should look such as if (my_plugin_need_poll) my_plugin_poll(VM86_RETURN_VALUE); The my_plugin_poll() functions is then called on each return from vm86() after handle_signals() and uhook_poll(), but before mhp_debug(DBG_POLL...). VM86_RETURN_VALUE is the full value returned by the last call to vm86(), use the macro VM86_ARG() and VM86_TYPE() to extract the info you need. Main purpose of this hook is to handle background stuff that otherwise can't be monitored (such as time consuming output to files, that would block dosemu otherwise) or to do additional hacking for application special events. Keep in mind that handle_signals() may lead to calling your my_plugin_ioselect() (see above), hence you may get a call to my_plugin_poll() directly after (without having returned to vm86 mode). - plugin_config.h. Its purpose is to declare the prototypes and extern declarations of all the functions and variables explained above, and to include configuration switches, which are exported to DOSEMU via src/include/config.h. Each plugin_config.h will be included at the _buttom_ of config.h, such that you may redefine/interprete already set global configuration variables. This also is a way to let other plugins and/or DOSEMU code know that a given plugin is available/activated or to check for consistency, such as in plugin_config.h: #define HAVE_MY_PLUGIN 2 #if (IS_DEVEL_RELEASE && (DOSEMU_VERSION_CODE < VERSION_OF(1,1,2,0))) \ || (!IS_DEVEL_RELEASE && (DOSEMU_VERSION_CODE < VERSION_OF(1,0,3,0))) #error "Sorry, wrong DOSEMU version for my_plugin, please upgrade" #endif and then in the other plugin and/or DOSEMU code: #include "config.h" #if HAVE_MY_PLUGIN #if HAVE_MY_PLUGIN < 2 #error "Sorry, wrong my_plugin version for this DOSEMU version" #endif /* plugin special code */ #endif For what can plug-ins be used? ------------------------------ The main reason I implemented 'plugin' was the need to write a DOS driver for some (very) special selfmade Hardware, which nobody else would profit from except my employer ... and because I was too lazy to patch in the changes to every new released dosemu version;-) This way DOSEMU atleast profits from the interface. Having it now, I see some more usage such as - DOS drivers using 32-bit written by contributors and distributed separately. - special debugging code to reverse engineer (DOS) drivers for special hardware, if the manufacturer does not cooperate in writing a Linux driver. Also with this, distributing separately can avoid copyright conflicts. - Replacements for DOS system commands, which (via inte6) then run faster in 32-bit or can do things which aren't possible otherwise. - Drivers for video chips we currently do not support. We even may consider to move more of our current code into plugin (as we just did with the keyboard code), this would make it possible to distribute them separately. It may be necessary to add some more hooks (plugin_*.h), but for this we wait what happens;-) Hans Lermen , Nov 30 2000