DANG The DOSEMU Alterer Novices Guide <author>Alistair MacDonald, <tt/alistair@slitesys.demon.co.uk/ <date>For DOSEMU v0.63.1 <abstract> This Document is the DOSEMU Alterer Novices Guide. It is known as the DANG. </abstract> <toc> <sect>Introduction <p> This document is the preliminary draft of a manual to help people understand the inner workings of dosemu. It is the goal of this document to create new dosemu hackers. This concept was inspired by the linux kernel hackers guide. This Guide was concieved and originally written by &dquot;Corey Sweeney&dquot; <corey@>. It has been completely revised. It is now generated automatically directly from the source code. Special thanks to &dquot;James B. MacLean&dquot; <macleajb@ednet.ns.ca> for supplying the original information. (It was mostly ripped out of a mail message.) &dquot;Jochen Hein&dquot; has made many useful comments & suggestions. At the end if this document is a section detailing how this guide is put together. This may help you when trying to locate the relevant pieces of code. If you add new code, it would be useful if the relevant markers are added where appropriate. This file is a collective effort. If you don't like one of the explanations, or want to add anything, please send me something! <sect>The Main group of Modules <p> These files are used to start DOSEMU as well as hold globally called functions and global vars. <sect1>dos.c Information <p> <sect2>Functions in dos.c <p> These are the functions defined in dos.c. <sect3>dosemu <p> <p>Arguments are:&nl; <itemize> <ITEM> argc - Count of argumnents. <ITEM> argc - Actual arguments. </itemize> Function created by entry point into libdosemu. Called to jump into the emulate function of DOSEMU. <sect1>emu.c Information <p> <sect2>Functions in emu.c <p> These are the functions defined in emu.c. <sect3>jmp_emulate <p> call the emulate function by way of the dll headers. Always make sure that this line is the first of emu.c and link emu.o as the first object file to the lib <sect3>SIG_int <p> Requires the sig/sillyint.o driver loaded (using NEW modules package), or a kernel patch (implementing sig/int.c driver). The IRQ numbers to monitor are taken from config.sillyint, each bit corresponding to one IRQ. The higher 16 bit are defining the use of SIGIO <sect3>emulate <p> <p>Arguments are:&nl; <itemize> <ITEM> argc - Argument count. <ITEM> argv - Arguments. </itemize> Emulate gets called from dos.c. It initializes DOSEMU to prepare it for running in vm86 mode. This involves catching signals, preparing memory, calling all the initialization functions for the I/O subsystems (video/serial/etc...), getting the boot sector instructions and calling vm86(). <sect2>Remarks in emu.c <p> DOSEMU must not work within the 1 meg DOS limit, so start of code is loaded at a higher address, at some time this could conflict with other shared libs. If DOSEMU is compiled statically (without shared libs), and org instruction is used to provide the jump above 1 meg. ----- At this time we have to use SIGALRM in addition to SIGIO I don't (yet) know why the SIGIO signal gets lost sometimes (once per minute or longer). But if it happens, we can retrigger this way over SIGALRM. Normally SIGIO happens before SIGALARM, so nothing hurts. (Hans) <sect1>include/emu.h Information <p> <sect2>Functions in include/emu.h <p> These are the functions defined in include/emu.h. <sect3>NEWSETQSIG <p> <p>Arguments are:&nl; <itemize> <ITEM> sig - the signal to have a handler installed to. <ITEM> fun - the signal handler function to install </itemize> All signals that wish to be handled properly in context with the execution of vm86() mode, and signals that wish to use non-reentrant functions should add themselves to the SIGNALS_THAT_QUEUE define and use SETQSIG(). To that end they will also need to be set up in an order such as SIGIO. <sect2>Remarks in include/emu.h <p> The `vm86_struct` is used to pass all the necessary status/registers to DOSEMU when running in vm86 mode. ----- We assume system call restarting... under linux 0.99pl8 and earlier, this was the default. SA_RESTART was defined in 0.99pl8 to explicitly request restarting (and thus does nothing). However, if this ever changes, I want to be safe ----- DOSEMU keeps system wide configuration status in a structure called config. ----- The var `fatalerr` can be given a true value at any time to have DOSEMU exit on the next return from vm86 mode. <sect>The DPMI group of Modules <p> DPMI is Lutz's Baby. It's a really important part of the Emulator as far as we are concerned, since it will allow us to run so many more programs and, most importantly, bcc. This is the one thing that the WINE developers want that we haven't been able to give them. If you think you can help .... &dquot;Away you Go!&dquot; (Sorry to those non-UK folks ... Thats a reference to a UK kids sports programme from my youth ... anyway ... enough of this banter. You'll be wanting to know that this is all about DPMI ...) <sect1>dosext/dpmi/dpmi.c Information <p> <sect2>Functions in dosext/dpmi/dpmi.c <p> These are the functions defined in dosext/dpmi/dpmi.c. <sect3>dpmi_control <p> This function is similar to the vm86() syscall in the kernel and switches to dpmi code. <sect3>run_pm_int <p> This routine is used for running protected mode hardware interrupts and software interrupts 0x1c, 0x23 and 0x24. run_pm_int() switches to the locked protected mode stack and calls the handler. If no handler is installed the real mode interrupt routine is called. <sect3>do_default_cpu_exception <p> This is the default CPU exception handler. Exceptions 0, 1, 2, 3, 4, 5 and 7 are reflected to real mode. All other exceptions are terminating the client (and may be dosemu too :-)). <sect3>do_cpu_exception <p> This routine switches to the locked protected mode stack, disables interrupts and calls the DPMI client exception handler. If no handler is installed the default handler is called. <sect3>dpmi_fault <p> This is the brain of DPMI. All CPU exceptions are first reflected (from the signal handlers) to this code. Exception from nonpriveleged instructions INT XX, STI, CLI, HLT and from WINDOWS 3.1 are handled here. All here unhandled exceptions are reflected to do_cpu_exception() <sect2>Remarks in dosext/dpmi/dpmi.c <p> We are caching ldt here for speed reasons and for Windows 3.1. I would love to have an readonly ldt-alias (located in the first 16MByte for use with 16-Bit descriptors (WIN-LDT)). This is on my wish list for the kernel hackers (Linus mainly) :-))))))). ----- DPMI is designed such that the stack change needs a task switch. We are doing it via an SIGSEGV - instead of one task switch we have now four :-(. Arrgh this is the point where I should start to include DPMI stuff in the kernel, but then we could include the rest of dosemu too. Would Linus love this? I don't :-((((. Anyway I would love to see first a working DPMI port, maybe we will later (with version 0.9 or similar :-)) start with it to get a really fast dos emulator............... ----- Handling of the virtuell interruptflag is still not correct and there are many open questions since DPMI specifications are unclear in this point. An example: If IF=1 in protected mode and real mode code is called which is disabling interrupts via cli and returning to protected mode, is IF then still one or zero? I guess I have to think a lot about this and to write a small dpmi client running under a commercial dpmi server :-). <sect2>Items for Fixing in dosext/dpmi/dpmi.c <p> We shouldn't return to dosemu code if IF=0, but it helps - WHY? ----- we should not change registers for hardware interrupts <sect2>New Ideas for dosext/dpmi/dpmi.c <p> Simulate Local Descriptor Table for MS-Windows 3.1 must be read only, so if krnl386.exe/krnl286.exe try to write to this table, we will bomb into sigsegv() and and emulate direct ldt access <sect>The Video group of Modules <p> All of the Video handling code is in the &dquot;video&dquot; subdirectory. There is one file for each video card or chipset and the master file. To Add a new card, it needs a set of save & restore routines putting in a file here. <sect1>env/video/vc.c Information <p> <sect1>env/video/video.c Information <p> <sect2>Functions in env/video/video.c <p> These are the functions defined in env/video/video.c. <sect3>video_init <p> Set pointer to correct structure of functions to initialize, close, etc... video routines. <sect2>Remarks in env/video/video.c <p> Here the sleeping lion will be awoken and eat much of CPU time !!! The result of setting VM86_SCREEN_BITMAP (at state of Linux 1.1.56): Each vm86 call will set 32 pages of video mem RD-only (there may be 1000000 per second) Write access to RD-only page results in page-fault (mm/memory.c), which will set a bit in current->screen_bitmap and calls do_wp_page() which does __get_free_page(GFP_KERNEL) but frees it immediatly, because copy-on-write is not neccessary and sets RD/WR for the page. (this could happen 32000000 per second, if the CPU were fast enough) It would be better to get the DIRTY-bit directly from the page table, isn't it? A special syscall in emumodule could do this. ----- reserve_video_memory() This procedure is trying to eke out all the UMB blocks possible to maximize your memory under DOSEMU. If you know about dual monitor setups, you can contribute by putting in the correct graphics page address values. <sect1>env/video/X.c Information <p> <sect2>Functions in env/video/X.c <p> These are the functions defined in env/video/X.c. <sect3>get_vga256_colors <p> Allocates a colormap for 256 color modes and initializes it. <sect3>X_close <p> Destroys the window, unloads font, pixmap and colormap. <sect3>X_setmode <p> Resizes the window, also the graphical sizes/video modes. remember the dos videomodi <sect3>X_change_mouse_cursor <p> This function seems to be called each screen_update :( It is called in base/mouse/mouse.c:mouse_cursor(int) a lot for show and hide. <sect3>X_redraw_screen <p> Redraws the entire screen, also in graphics mode Used for expose events etc. returns: nothing <p>Arguments are:&nl; <itemize> <ITEM> none </itemize> <sect3>X_update_screen <p> Updates, also in graphics mode Graphics in X has to be smarter and improved returns: 0 - nothing updated 2 - partly updated 1 - whole update <p>Arguments are:&nl; <itemize> <ITEM> none </itemize> <sect3>set_mouse_position <p> places the mouse on the right position Not tested in X with graphics returns: nothing <p>Arguments are:&nl; <itemize> <ITEM> x,y - coordinates </itemize> <sect1>env/video/console.c Information <p> <sect1>env/video/dualmon.c Information <p> <sect2>Functions in env/video/dualmon.c <p> These are the functions defined in env/video/dualmon.c. <sect3>MDA_init <p> Initializes the monochrome card. First detects which monochrome card is used, because the Hercules RamFont and the Hercules InColor need one more register to be initialized. If there is no monochrome card at all, we just think there is one and poke an peek in the void. After the detection the card is initialized. returns: nothing <p>Arguments are:&nl; <itemize> <ITEM> none </itemize> <sect2>Remarks in env/video/dualmon.c <p> After MDA_init() the VGA is configured, something in video.c or console.c &dquot;reprograms&dquot; the monochrome card again in such a way that I always have to run hgc.com before I can use any program that uses the monochrome card. I've spent a day trying to find it, but I can't figure out. Something is writing to one of the following ports: 0x3b4, 0x3b5, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bf. The problem occurs at (at least) the following 2 systems: - AMD 386DX40, Trident 9000/512Kb ISA, Hercules Graphics Card Plus - Intel 486DX2/66, Cirrus Logic 5426/1Mb VLB, Hercules clone The problem doesn't occur when I start dosemu from a telnet connection or from a VT100 terminal. (Erik Mouw, jakmouw@et.tudelft.nl) <sect1>env/video/et4000.c Information <p> <sect1>env/video/hgc.c Information <p> <sect1>base/bios/int10.c Information <p> <sect1>env/video/s3.c Information <p> <sect1>env/video/terminal.c Information <p> <sect1>env/video/trident.c Information <p> <sect1>env/video/vga.c Information <p> <sect>The Keyboard group of Modules <p> All of the Keyboard handling code is in the &dquot;keyboard&dquot; subdirectory. Latest addition is SLANG. <sect1>base/keyboard/Xkeyb.c Information <p> <sect1>base/keyboard/keymaps.c Information <p> <sect2>Remarks in base/keyboard/keymaps.c <p> The DEAD codes must refer to keys that don't exist on any language keyboard. I hope nobody has a smily face key :-) dead_key_table is a list of the dead keys supported. They must be placed on the correct key in the keymaps above. See key_map_es_latin1. ----- dos850_dead_map consists of the triple, {deadkey, letter, result}. It should be correct for all the code page 850 users (Western Europe). If you uses a different code page, please create a map! Jon Tombs jon@gtex02.us.es <sect1>base/keyboard/slang-termio.c Information <p> <sect>The Misc group of Modules <p> These are the remaining important files, that do not really fit into another group. These should not be dismissed as unimportant - rather, they are often amongst the most important. <sect1>dosext/misc/emm.c Information <p> <sect1>dosext/misc/xms.c Information <p> <sect1>arch/linux/async/sigsegv.c Information <p> <sect2>Functions in arch/linux/async/sigsegv.c <p> These are the functions defined in arch/linux/async/sigsegv.c. <sect3>dosemu_fault <p> All CPU exceptions (except 13=general_protection from V86 mode, which is directly scaned by the kernel) are handled here. <sect1>include/int.h Information <p> <sect1>include/ports.h Information <p> <sect1>base/misc/dosio.c Information <p> <sect1>base/misc/disks.c Information <p> <sect2>Functions in base/misc/disks.c <p> These are the functions defined in base/misc/disks.c. <sect3>disk_init <p> Test by opening all floppies/hardrives configured. <sect1>emi-i386/cpu.c Information <p> <sect1>dev/misc/lpt.c Information <p> <sect>The Serial group of Modules <p> This is the code that works our serial emulation. This needs to be very fast if we are to convince DOS that we have a very fast serial port. <sect1>base/serial/ser_defs.h Information <p> <sect2>Remarks in base/serial/ser_defs.h <p> Extensions to serial debugging. SER_DEBUG_MAIN (0 or 1) - extra debug output on the most critical information. SER_DEBUG_HEAVY (0 or 1) - super-heavy extra debug output, including all ports reads and writes, and every character received and transmitted! SER_DEBUG_INTERRUPT (0 or 1) - additional debug output related to serial interrupt code, including flagging serial interrupts, or PIC-driven code. SER_DEBUG_FOSSIL_RW (0 or 1) - heavy FOSSIL debug output, including all reads and writes. SER_DEBUG_FOSSIL_STATUS (0 or 1) - super-heavy FOSSIL debug output, including all status checks. You must recompile dosemu everytime one of these constants are modified. Just type 'make' in the dosemu dir and it will recompile the changes only. ----- IMPORTANT INFO about com[] variable array structure used in serial.c Most of the serial variables are stored in the com[] array. The com[] array is a structure in itself. Take a look at the 'serial_t' struct declaration in the serial.h module for more info about this. Only the most commonly referenced global variables are listed here: config.num_ser Number of serial ports active. com[x].base_port The base port address of emulated serial port. com[x].real_comport The COM port number. com[x].interrupt The PIC interrupt level (based on IRQ number) com[x].mouse Flag mouse (to enable extended features) com[x].fd File descriptor for port device com[x].dev[] Filename of port port device com[x].dev_locked Flag whether device has been locked The arbritary example variable 'x' in com[x] can have a minimum value of 0 and a maximum value of (config.numser - 1). There can be no gaps for the value 'x', even though gaps between actual COM ports are permitted. It is strongly noted that the 'x' does not equal the COM port number. This example code illustrates the fact, and how the com[] array works: for (i = 0; i < config.numser; i++) s_printf(&dquot;COM port number %d has a base address of %x&dquot;, com[i].real_comport, com[i].base_port); <sect1>base/serial/ser_init.c Information <p> <sect2>Maintainers <p> Mark Rejhon <htmlurl url="mailto:marky@ottawa.com" name="<marky@ottawa.com>">&nl; <sect2>Functions in base/serial/ser_init.c <p> These are the functions defined in base/serial/ser_init.c. <sect3>serial_init <p> This is the master serial initialization function that is called upon startup of DOSEMU to initialize ALL the emulated UARTs for all configured serial ports. The UART is initialized via the initialize_uart function, which opens the serial ports and defines variables for the specific UART. If the port is a mouse, the port is only initialized when i <sect2>Items for Fixing in base/serial/ser_init.c <p> This needs more work before it is implemented into /etc/dosemu.conf as an 'rtscts' option. <sect1>base/serial/ser_ports.c Information <p> <sect2>Functions in base/serial/ser_ports.c <p> These are the functions defined in base/serial/ser_ports.c. <sect3>do_serial_in <p> The following function returns a value from an I/O port. The port is an I/O address such as 0x3F8 (the base port address of COM1). There are 8 I/O addresses for each serial port which ranges from the base port (ie 0x3F8) to the base port plus seven (ie 0x3FF). [num = abritary port number for serial line, address = I/O port address] <sect3>do_serial_out <p> The following function writes a value to an I/O port. The port is an I/O address such as 0x3F8 (the base port address of COM1). [num = abritary port number for serial line, address = I/O port address, val = value to write to I/O port address] <sect2>Items for Fixing in base/serial/ser_ports.c <p> Should clearing UART cause THRE int if it's enabled? ----- Fix the calculation assumption ----- Is this safe to put this here? ----- Is this safe to put this here? <sect1>base/serial/ser_irq.c Information <p> <sect2>Functions in base/serial/ser_irq.c <p> These are the functions defined in base/serial/ser_irq.c. <sect3>serial_int_engine <p> This function is the serial interrupts scheduler. Its purpose is to update interrupt status and/or invoke a requested serial interrupt. If interrupts are not enabled, the Interrupt Identification Register is still updated and the function returns. See pic_serial_run() below it is executed right at the instant the interrupt is actually invoked. Since it is not possible to run the interrupt on the spot, it triggers the interrupt via the pic_request() function (which is in pic.c) and sets a flag that an interrupt is going to be occur soon. Please read pic_serial_run() for more information about interrupts. [num = port, int_requested = the requested serial interrupt] <sect3>pic_serial_run <p> This function is called by the priority iunterrupt controller when a serial interrupt occurs. It executes the highest priority serial interrupt for that port. (Priority order is: RLSI, RDI, THRI, MSI) Because it is theoretically possible for things to change between the interrupt trigger and the actual interrupt, some checks must be repeated. <sect3>serial_run <p> This is the main housekeeping function, which should be called about 20 to 100 times per second. The more frequent, the better, up to a certain point. However, it should be self-compensating if it executes 10 times or even 1000 times per second. Serial performance increases with frequency of execution of serial_run. Serial mouse performance becomes more smooth if the time between calls to serial_run are smaller. <sect2>Remarks in base/serial/ser_irq.c <p> Linux code hackers: How do I detect a break signal without having to rely on Linux signals? Can I peek a 'break state bit'? Also, how do I 'turn on' and 'turn off' the break state, via an ioctl() or tcsetattr(), rather than using POSIX tcsendbrk()? <sect2>Items for Fixing in base/serial/ser_irq.c <p> how do we cancel a PIC interrupt, when we have come this far? ----- Perhaps this can be modified to limit max chain length? <sect>The Mouse group of Modules <p> All of the Mouse handling code is in the &dquot;mouse&dquot; subdirectory. There are only 2 main files, mouse.c and mouseint.c. <sect1>base/mouse/mouse.c Information <p> <sect2>Functions in base/mouse/mouse.c <p> These are the functions defined in base/mouse/mouse.c. <sect3>mouse_init <p> Initialize internal mouse. <sect>The Bios group of Modules <p> All of the Bios code is in the &dquot;bios&dquot; subdirectory. DOSEMU requires certain code to be coded in assembler and also code to be located in the F000 segment. This is where all such code should be put. <sect1>base/bios/bios.S Information <p> <sect>The PIC group of Modules <p> All of the PIC handling code is in the &dquot;PIC&dquot; subdirectory. <sect1>dev/pic/pic.c Information <p> <sect1>devpic/pic.h Information <p> <sect>And Finally ... <p> The Following items are used to delimit the text used to create this file. Whilst it is not necessary to know this, they are included because they may be useful for searching, as they are (at least at the moment) reasonably unique. DANG_BEGIN_MODULE / DANG_END_MODULE This will bracket a description of the file (normally at the start). DANG_BEGIN_FUNCTION / DANG_END_FUNCTION This brackets a description of functions (good this, isn't it!) Not every function needs to be described in this way - just the major ones. DANG_BEGIN_REMARK / DANG_END_REMARK This brackets descriptions of obscure items, like data structures and architecture. DANG_FIXTHIS This is a one line item, indicating a an area requiring a fix, or redesign. DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA New Ideas Start Here! As Ideas are proposed, that get added with their description, so that future generations can laugh at or code the ideas ..... These bracket the idea description. DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG Changelogs - very useful for bug fixing, and avvailable for use with DPR (or that's the theory) </article>