7. The Misc group of Modules

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.

7.1. Functions in base/async/int.c

These are the functions defined in base/async/int.c.

7.1.1. int1a

int 0x1A call

This has (among other things) the calls that DOS makes to get/set its sense of time. On booting, DOS gets the RTC time and date with AH=2 and AH=4, after that it should use AH=0 calls to read the 'tick' counter from BIOS memory. Each time this crosses midnight, a flag is set that DOS uses to increment its date.

Here we can now change the 'view' of time so the calls either return BIOS tick (most DOS like), read the PIT counter (avoids INT-8 changes) or gets LINUX time (most accurate for long term NTP-adjusted time keeping).

7.1.2. ms_dos

int0x21 call

we trap this for two functions: simulating the EMMXXXX0 device and fudging the CONFIG.XXX and AUTOEXEC.XXX bootup files.

note that the emulation herein may cause problems with programs that like to take control of certain int 21h functions, or that change functions that the true int 21h functions use. An example of the latter is ANSI.SYS, which changes int 10h, and int 21h uses int 10h. for the moment, ANSI.SYS won't work anyway, so it's no problem.

7.1.3. run_caller_func(i, revect)

This function runs the specified caller function in response to an int instruction. Where i is the interrupt function to execute.

revect specifies whether we call a non-revectored leaf interrupt function or a "watcher" that sits in between: the leaf interrupt function is called if cs:ip is at f000:i*10 or if (the int vector points there and the int is labelled non-revectored) otherwise the non-leaf interrupt function is called, which may chain through to the real interrupt function (if it returns 0)

This function runs the instruction with the following model _CS:_IP is the address to start executing at after the caller function terminates, and _EFLAGS are the flags to use after termination. For the simple case of an int instruction this is easy. _CS:_IP = retCS:retIP and _FLAGS = retFLAGS as well equally the current values (retIP = curIP +2 technically).

However if the function is called (from dos) by simulating an int instruction (something that is common with chained interrupt vectors) _CS:_IP = BIOS_SEG:HLT_OFF(i) and _FLAGS = curFLAGS while retCS, retIP, and retFlags are on the stack. These I pop and place in the appropriate registers.

This functions actions certainly correct for functions executing an int/iret discipline. And almost certianly correct for functions executing an int/retf#2 discipline (with flag changes), as an iret is always possilbe. However functions like dos int 0x25 and 0x26 executing with a int/retf will not be handled correctlty by this function and if you need to handle them inside dosemu use a halt handler instead.

Finally there is a possible trouble spot lurking in this code. Interrupts are only implicitly disabled when it calls the caller function, so if for some reason the main loop would be entered before the caller function returns wrong code may execute if the retFLAGS have interrupts enabled!

This is only a real handicap for sequences of dosemu code execute for long periods of time as we try to improve timer response and prevent signal queue overflows! -- EB 10 March 1997

Grumble do to code that executes before interrupts, and the semantics of default_interupt, I can't implement this function as I would like. In the tricky case of being called from dos by simulating an int instruction, I must leave retCS, retIP, on the stack. But I can safely read retFlags so I do. I pop retCS, and retIP just before returning to dos, as well as dropping the stack slot that held retFlags.

This improves consistency of interrupt handling, but not quite as much as if I could handle it the way I would like. -- EB 30 Nov 1997

Trying to get it right now -- BO 25 Jan 2003

This function returns 1 if it's completely finished (no need to run real_run_int()), otherwise 0.

7.1.4. DO_INT

DO_INT is used to deal with interrupts returned to DOSEMU by the kernel.

7.1.5. setup_interrupts

SETUP_INTERRUPTS is used to initialize the interrupt_function array which directs handling of interrupts in protected mode and also initializes the base vector for interrupts in real mode.

7.1.6. int_vector_setup

Setup initial interrupts which can be revectored so that the kernel does not need to return to DOSEMU if such an interrupt occurs.

7.2. Remarks in base/async/int.c

Many video BIOSes use hi interrupt vector locations as scratchpad area - this is because they come before DOS and feel safe to do it. But we are initializing vectors before video, so this only causes trouble. I assume no video BIOS will ever: - change vectors < 0xe0 (0:380-0:3ff area) - change anything in the vector area _after_ installation - AV

7.3. Functions in arch/linux/async/sigsegv.c

These are the functions defined in arch/linux/async/sigsegv.c.

7.3.1. dosemu_fault(int, struct sigcontext_struct);

All CPU exceptions (except 13=general_protection from V86 mode, which is directly scanned by the kernel) are handled here.

7.3.2. print_exception_info

Prints information about an exception: exception number, error code, address, reason, etc.

7.4. Functions in arch/linux/async/signal.c

These are the functions defined in arch/linux/async/signal.c.

7.4.1. NEWSETQSIG

Arguments are:

  • sig - the signal to have a handler installed to.

  • fun - the signal handler function to install

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 ADDSET_SIGNALS_THAT_QUEUE define and use SETQSIG(). To that end they will also need to be set up in an order such as SIGIO.

7.4.2. SIG_init

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

7.4.3. signal_init

Initialize the signals to have NONE being blocked. Currently this is NOT of much use to DOSEMU.

7.4.4. handle_signals

Due to signals happening at any time, the actual work to be done because a signal occurs is done here in a serial fashion.

The concept, should this eventualy work, is that a signal should only flag that it has occurred and let DOSEMU deal with it in an orderly fashion as it executes the rest of it's code.

7.4.5. SIGNAL_save

Arguments are:

  • context - signal context to save.

  • signal_call - signal handling routine to be called.

Save into an array structure queue the signal context of the current signal as well as the function to call for dealing with this signal. This is a queue because any signal may occur multiple times before DOSEMU deals with it down the road.

7.4.6. SIGIO_call

Whenever I/O occurs on devices allowing SIGIO to occur, DOSEMU will be flagged to run this call which inturn checks which fd(s) was set and execute the proper routine to get the I/O from that device.

7.5. Remarks in arch/linux/async/signal.c

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

-----

Check for keyboard coming from client For now, first byte is interrupt requests from Client

7.6. Functions in base/misc/disks.c

These are the functions defined in base/misc/disks.c.

7.6.1. disk_init

Test by opening all floppies/hardrives configured.

7.7. Functions in base/dev/misc/timers.c

These are the functions defined in base/dev/misc/timers.c.

7.7.1. initialize_timers

ensure the 0x40 port timer is initially set correctly

7.7.2. timer_tick

Every time we get a TIMER signal from Linux, this procedure is called. It checks to see if we should queue a timer interrupt based on the current values.

7.7.3. do_sound

do_sound handles the _emulated_ mode pc-speaker emulation.

As far as I can determine all cases of the pc-speaker are now emulated. But I am not sure where Rainer Zimmerman got his (pit[2].mode == 2) || (pit[2].mode == 3) test in the original implementation, it doesn't seem to cause problems though.

The implementation of speaker_on & speaker_off can be found in src/base/speaker.c

Major Changes from version written by Rainter Zimmerman.

o Added support for programs that control the directly through bit 1 of port61.

o Added a generic interface to allow multiple speaker backends.

o Implemented X speaker code so the emulated speaker now works in X.

--EB 21 September 1997

7.7.4. timer_int_engine

This is experimental TIMER-IRQ CHAIN code! This is a function to determine whether it is time to invoke a new timer irq 0 event. Normally it is 18 times a second, but many video games set it to 100 times per second or more. Since the kernel cannot keep an accurate timer interrupt, the job of this routine is to perform a chained timer irq 0 right after the previous timer irq 0. This routine should, ideally, be called right after the end of a timer irq, if possible.

This would speed up high frequency timer interrupts if this code can be converted into an assembly macro equivalent!

PLEASE NOTE

This code has been replaced by interrupt scheduling code in pic. The result is that we simply call pic_sched and run the dos interrupt. If the new code causes no problems, I'll revise this section permanently.

7.8. Functions in base/misc/dos2linux.c

These are the functions defined in base/misc/dos2linux.c.

7.8.1. run_unix_command

Runs a command and prints the (stdout and stderr) output on the dosemu screen.

Return values mean:

Arguments are:

  • buffer - string with command to execute

7.9. Functions in base/misc/ioctl.c

These are the functions defined in base/misc/ioctl.c.

7.9.1. io_select_init

Initialize fd_sets to NULL for both SIGIO and NON-SIGIO.

7.9.2. add_to_io_select

Arguments are:

  • fd - File handle to add to select statment

  • want_sigio - want SIGIO (1) if it's available, or not (0).

Add file handle to one of 2 select FDS_SET's depending on whether the kernel can handle SIGIO.

7.9.3. remove_from_io_select

Arguments are:

  • fd - File handle to remove from select statment.

  • used_sigio - used SIGIO (1) if it's available, or not (0).

Remove a file handle from one of 2 select FDS_SET's depending on whether the kernel can handle SIGIO.

7.10. Functions in base/dev/misc/lpt.c

These are the functions defined in base/dev/misc/lpt.c.

7.10.1. printer_init

Initialize printer control structures

7.11. Functions in base/dev/misc/pci.c

These are the functions defined in base/dev/misc/pci.c.

7.11.1. pci_read_header

Use standard 32-bit (type 1) access method to read PCI configuration space data

7.11.2. pci_setup

Register standard PCI ports 0xcf8-0xcff

7.12. Functions in base/dev/misc/joystick.c

These are the functions defined in base/dev/misc/joystick.c.

7.12.1. joy_latency_over

Tells DOSEMU whether or not it is time to update its internal status of the joystick (for nonblocking reads only).

DOS programs read/poll from the joystick port hundreds of thousands of times per second so the idea is that we really don't need to read from Linux for every such query (increasing performance by about 40%) because:

1. humans are incapable of changing the status of the joystick (moving, pressing buttons) more than about 10 times per second

2. no one will not notice a delay in DOS registering the joystick status (if it is in the order of a few milliseconds)

Of course, this means that you should not set joy_latency in dosemu.conf to more than 1000/(#times I can press a button/move joy per second * 2), unless you want DOSEMU to miss quick axis/button changes and want to wait a ridiculous amount of time before DOSEMU registers any changes at all.

7.12.2. joy_emu_button_set

Update the button status for each joystick.

We must perform "button mapping" if only the first joystick is enabled i.e. we are required to map the "excessive" buttons (>2) from the first joystick onto the second:

a) 3rd button of 1st joy --> 1st button of 2nd joy

b) 4th button of 1st joy --> 2nd button of 2nd joy

7.12.3. joy_emu_axis_set

Update the axis status for each joystick.

We must perform "axis mapping" if only the first joystick is enabled i.e. we are required to map the "excessive" axes (>2) from the first joystick onto the second:

a) 3rd axis of 1st joy --> 2st axis of 2nd joy

b) 4th axis of 1st joy --> 1st axis of 2nd joy (yes, these are reversed deliberately because it's what happens in DOS)

7.12.4. joy_emu_axis_conv

Convert a Linux joystick axis reading to a DOS one by making use of the differences in the allowable range of axis values.

NOTE: I don't know whether or not Linux returns exponential values for the joystick but (apparently) DOS programs expect the values to be exponential and so if this is to be fixed, it should probably be done in this function.

7.12.5. joy_linux_process_event

Update global joystick status variables given a Linux joystick event.

7.12.6. joy_linux_read_events

Process the event queue for _both_ linux joysticks using nonblocking reads with the new joystick API (joy_driver_new).

This should be done before (well, actually, not before _every_ single read -- see joy_latency_over()) the joystick status is returned to DOS as all Linux joystick events are queued until they are processed and we want to return a reasonably current state of the joystick -- not what it was a long time ago. _Both_ joysticks are processed here because of axis/button mapping affecting the status of both emulated joysticks (what DOS sees).

7.12.7. joy_linux_read_status

Read both the current position and current button status of the joystick from Linux (joy_driver_old).

7.12.8. joy_linux_read_buttons_(family)

Eventually called from DOS to get the button status of the joysticks. The threaded version will simply get the status from global variables. The unthreaded versions will perform non-blocking reads.

7.12.9. joy_linux_read_axis_(family)

Eventually called from DOS to get the axis status of the joysticks. The threaded version will simply get the values from global variables. The unthreaded versions will perform non-blocking reads.

Arguments are:

  • invalid_val - value to return to signify a non-existent axis

  • update - whether DOSEMU should update its internal axis values from Linux

  • (for each read of the joystick position, set this flag _only_

  • on the first of the 4 calls to this function unless you want

  • the axis positions to be from different points in time :)

7.12.10. joy_bios_read

This is the int15 function 0x84 handler (i.e. BIOS joystick emulation), called from src/base/async/int.c.

The real BIOS actually reads its values straight from the joystick port (0x201) but we don't bother because we can do it faster :)

Because of this, it returns the joystick axis values with the same range as port 0x201 BUT the range for a real BIOS varies between computers as it is dependant on how it reads from the port (hopefully this won't cause any problems).

7.12.11. joy_port_inb

This function emulates reads from the joystick port (0x201) -- this is the most common way of detecting and reading the joystick.

The real implementation of this port sets a bit for each axis for a certain period of time, corresponding to analog measurements of the position of each axis (so "if you count the analog values in software, a faster machine yields different values from a slow machine [unless you use a timer]" - DOS 6: A Developer's Guide).

In contrast, this implementation sets the bits high for a certain number of port reads, corresponding to the position of each axis (independent of time). This means that, for most programs, the axis range will be that specified in dosemu.conf (which is rather convenient) and avoids the issue of super-fast computers causing DOS program axis counters to overflow (e.g. in a real system, if the program used an 8-bit variable for storing the position of an axis and the system was fast enough to read from the port more than 127 or 255 times, there would be trouble).

7.13. Remarks in base/dev/misc/joystick.c

We make a runtime decision based on the detected joystick API version and #ifdef USE_PTHREADS, on the way in which we obtain the joystick status from Linux (a "driver"):

1. joy_driver_nojoy: simply tells DOS programs that you have no joystick(s)

2. joy_driver_old: uses old, non-blocking joystick API (<1.0.0); limited to 2 axes; supported because DOSEMU supports old kernels

3. joy_driver_new: uses new, non-blocking joystick API (>=1.0.0); a (little) slower than joy_driver_new_threaded

4. joy_driver_new_threaded: uses new, BLOCKING joystick API (>=1.0.0); efficient but requires pthreads (which is known to make DOSEMU unstable!)

The same driver is used for both joysticks.

-----

if the 2nd joystick is enabled, we ignore any button >= 2 regardless of which joystick it is (if it's the 1st, the 2nd joystick would overwrite its buttons; if it's the 2nd, it would be out of range)

-----

if the 2nd joystick is enabled, we ignore any axis >= 2 regardless of which joystick it is (if it's the 1st, the 2nd joystick would overwrite its axes; if it's the 2nd, it would be out of range)

-----

Apparently, the Carry Flag is cleared if int15 function 0x84 exists and it is set if it doesn't exist.

But what does this mean? Does the existence of such a BIOS function depend on the existence of a Game Card/SoundBlaster, or does it just mean that there is such an implemented BIOS function, regardless of whether or not you have a joystick?

I have never seen a real BIOS set the Carry Flag on this call, even on a computer without a joystick -- so to mimick what happens in the real world, I just clear the Carry Flag regardless of whether the user has a joystick or not. This could be incorrect behaviour so it may have to be changed in the future.

-----

Here we set bits based on joystick axis counters. The code here is particularly tricky and if you try to change it, you will probably break it :)

-----

Here we read the button status from Linux (programs can read the button status from the port, _without_ making a dummy write to the port first so the Linux read must be done _here_) and return it.

7.14. Items for Fixing in base/dev/misc/joystick.c

does this code work for ports other than 0x201?

-----

joy_reset() is called immediately after joy_init(), which is rather inconvenient (anyone heard of a port_unregister_handler()?) so we don't bother resetting at all but in the future this could cause problems

-----

perhaps we should not have been called to start with?

7.15. Remarks in include/doshelpers.h

The Helper Interrupt uses the following groups:

0x00 - Check for DOSEMU 0x01-0x11 - Initialisation functions & Debugging 0x12 - Set hogthreshold (aka garrot?) 0x20 - MFS functions 0x21-0x22 - EMS functions 0x28 - Garrot Functions for use with the mouse 0x29 - Serial functions 0x30 - Whether to use the BOOTDISK predicate 0x33 - Mouse Functions 0x40 - CD-ROM functions 0x50-0x5f - DOSEMU/Linux communications 50 -- run unix command in ES:DX 51,52? 53 -- do system(ES:DX) 54 -- get CPU speed 55 -- get terminal type 0x60-0x6f - reserved for plug-ins 0x7a - IPX functions 0x8x -- utility functions 0x80 -- getcwd(ES:DX, size AX) 0x81 -- chdir(ES:DX) 0xdc - helper for DosC kernel 0xfe - called from our MBR, emulate MBR-code. 0xff - Terminate DOSEMU

There are (as yet) no guidelines on choosing areas for new functions.