• PRG20 - Generic time slicing function for many multi-taskers

    From FAQServer@2:5020/181 to All on Sat Mar 2 07:37:25 2024
    [Q]: Generic time slicing function for many multi-taskers

    [A]: Serg Projzogin

    ;========================================================================
    ;
    ; SLICE.ASM
    ;
    ; Provides a generic time slicing function for all multi-taskers I know
    ; or care about.
    ;
    ; Note that this library is Turbo Assembler specific, since I have long
    ; since weaned myself from MASM's brain-dead memory addressing syntax.
    ;
    ; This library is designed to be easily extended; for each new
    ; multi-tasker supported, you need to write a detect routine and a
    ; time-slice routine.
    ;
    ; Your detection function will take no input, and should return with
    ; carry set if the associated multi-tasker is detected. This routine
    ; may safely alter register AX. No other registers should be altered.
    ;
    ; The time-slice routine will take no input and give up a "standard"
    ; timeslice for the associated multi-tasker. This routine may safely
    ; alter registers AX, BX and DS. No other registers should be altered.
    ;
    ; Once you have such routines written, add their addresses to the
    ; arrays detect_func and slice_func below. Increment the
    ; NumMultitaskers equate, and you're done.
    ;
    ; This library placed in the public domain by Kevin Vigor, 1/5/93.
    ; I would, however, appreciate it if you do the following two things:
    ;
    ; 1: If you distribute an altered version of this source, please add to
    ; this header a log of your changes;
    ;
    ; 2: If you discover any bugs or extend this library, please send a copy
    ; of your changes to me at one of the below addresses:
    ;
    ; Compuserve: 72500,3705
    ; Internet: kevin@wicat.com
    ; 72500.3705@compuserve.com ;========================================================================


    IDEAL ; Requires Turbo Assembler.

    MODEL SMALL ; This may be changed to any model safely. Note,
    ; however, that you will not be able to link
    ; this routine to a .COM, since it makes explicit
    ; segment refrences. This is just laziness; I
    ; haven't bothered to do all the ifdef'ing.

    LOCALS ; Allow local symbols starting with @@

    DATASEG

    ; Define known multitaskers.
    None equ 0
    DesqView equ 1
    Windows_3x equ 2
    OS2_2x equ 3

    NumMultitaskers EQU 3 ; Do not include 'None'

    current_tasker dw 0 ; Detected multi-tasker

    ; Table of detection routines.

    detect_func DW OFFSET @code:dummy_detect
    DW OFFSET @code:Desqview_detect
    DW OFFSET @code:Windows_3X_detect
    DW OFFSET @code:OS2_2x_detect

    ; Table of time-slicing functions.

    slice_func DW OFFSET @code:dummy_slice
    DW OFFSET @code:Desqview_slice
    DW OFFSET @code:Win_3x_or_OS2_2x_slice
    DW OFFSET @code:Win_3x_or_OS2_2x_slice

    CODESEG

    PUBLIC _detect_multitasker, _timeslice

    ;; Detection routines: return with carry set if the appropiate tasker is
    ;; detected and clear if not.

    PROC dummy_detect ; SHould never be called, but does no harm.
    clc ; Always fail.
    ret
    ENDP

    PROC Desqview_detect ; Return with carry set if Desqview detected.
    ;
    ; This routine is based on information in the Desqview version 2.x manual.
    push ax
    push bx
    push cx
    push dx

    mov cx, 'DE'
    mov dx, 'SQ'
    mov ax, 02B01h ; DOS set date function.
    int 021h
    cmp al, 0FFh ; Did DOS report the invalid date?
    jnz @@desqview ; If not, we've got Desqview.

    clc ; Report failure.

    @@clean_stack:
    pop dx
    pop cx
    pop bx
    pop ax
    ret

    @@desqview:

    ; BH = Desqview major version, BL = Desqview minor version. I have no idea
    ; at what version the timeslicing calls became available, so I just assume
    ; they are supported. If this is an invalid assumption, this would be the
    ; place to test.

    stc ; Report sucess.
    jmp short @@clean_stack ; and exit.

    ENDP ; Desqview_detect.

    PROC Windows_3X_detect
    ; Note: this function detects Windows 3.x in enhanced mode only.
    ; I am not a Windows guru (or even user), but I believe there is no
    ; capability for time-slicing in standard or real modes, therefore this
    ; function is sufficient for the purposes of this library.
    ; I am basing this function on the fine book PC Interrupts, which lists
    ; a number of magic values which mean WIndows 3.x enhanced mode is not running.

    push ax

    mov ax, 01600h
    int 02Fh

    cmp al, 00h
    jz @@no_Windows

    cmp al, 080h
    jz @@no_Windows

    cmp al, 01h ; Windows/386 2.x; not supported.
    jz @@no_windows

    cmp al, 0FFh ; Windows/386 2.x; not supported.

    ; If AL is none of the above values, it is the Windows major version number.

    cmp al, 03h ; At least Win 3.0?
    jb @@no_windows

    stc ; Yes, report sucess.
    pop ax
    ret

    @@no_windows:
    clc ; Report failure.
    pop ax
    ret
    ENDP

    PROC OS2_2x_detect
    ; I do not know of an 'official' way of testing for OS/2 presence; the
    ; method used here is to test the DOS version. If the major version
    ; is 20 or above, we assume we're in an OS/2 2.x DOS box.

    push ax
    push cx

    mov ah, 030h ; DOS get version fn.
    int 021h

    cmp al, 014h ; 20 decimal.
    jb @@no_OS2

    stc ; Report sucess.

    @@clean_stack:
    pop cx
    pop ax
    ret

    @@no_OS2:
    clc ; Report failure.
    jmp short @@clean_stack

    ENDP

    ;; Time slicing routines for each tasker.

    PROC dummy_slice ; Should never be called, but does no harm. ret
    ENDP

    PROC Desqview_slice ; Give up a slice under Desqview.

    ASSUME cs:@code, ds:nothing, es:nothing
    mov ax, 0101Ah ; Switch to DV's stack.
    int 015h
    mov ax, 01000h ; Give up time-slice.
    int 015h
    mov ax, 01025h ; Restore local stack.
    int 015h
    ret
    ENDP

    PROC Win_3x_or_OS2_2x_slice

    ; This call works under either Windows 3.x in Enhanced mode, or OS/2 2.x

    ASSUME ds:@code, ds:nothing, es:nothing
    mov ax, 01680h ; Win 3.x / OS/2 2.x timeslice call. int 02Fh
    ret
    ENDP


    PROC _detect_multitasker
    ; Tries to find a multi-tasker.
    ; Returns the ID in AX, and sets up the internal data to call _timeslice.
    ;
    ; Note that this function can be safely called from Turbo/Borland C. I have
    ; no idea about other compilers.

    push ds
    push bx
    push cx

    ASSUME cs:@code, ds:nothing, es:nothing
    mov ax, @data
    mov ds, ax
    ASSUME ds:@data

    mov cx, NumMultitaskers ; Number of routines to try.
    xor ax, ax

    @@detect_loop:
    inc ax

    ; AX holds the number of the detection routine to try.
    push ax
    shl ax, 1
    mov bx, ax ; BX = AX * 2

    call [detect_func + bx] ; Call this function.
    pop ax ; Restore AX.
    jc @@found_one ; quit now if we hit one.

    loop @@detect_loop ; Go through all known detection routines.

    xor ax, ax ; Signal failure.
    jmp short @@clean_stack ; and exit.

    @@found_one:
    mov [current_tasker], ax

    @@clean_stack:
    pop cx
    pop bx
    pop ds

    ASSUME ds:nothing

    ret
    ENDP

    PROC _timeslice
    ; Give up a timeslice. Depends on having the current_tasker global set by
    ; a call to detect_multitasker. However, will call dummy_slice and do no
    ; harm if detect_multitasker has not been called.
    ;
    ; Note that this function can be safely called from Turbo/Borland C. I have
    ; no idea about other compilers.

    push ds
    push ax
    push bx

    ASSUME cs:@code, ds:nothing, es:nothing
    mov ax, @data
    mov ds, ax
    ASSUME ds:@data

    mov ax, [current_tasker]
    shl ax, 1 ; BX = AX * 2
    mov bx, ax

    call [slice_func + bx] ; Call appropiate time-slice function.

    pop bx
    pop ax
    pop ds
    ret
    ENDP

    END
    === Cut ===

    === Cut ===
    /* SLICE.H
    *
    * Turbo/Borland C prototypes for the functions provided by SLICE.ASM
    *
    */
    #ifndef SLICE_H_
    #define SLICE_H_

    /* Returns zero if no known multi-tasker found, or an ID if one is. */
    int detect_multitasker(void);

    /* Give up a timeslice. detect_multitasker should be called first. */
    void timeslice(void);

    #endif
    === Cut ===

    === Cut ===
    /*
    * TEST.C
    *
    * Stupid test-bed for the time-slicing functions in SLICE.ASM;
    * simply detects a multi-tasker and then waits for a keystroke
    * twice, once with time-slicing and once without.
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <conio.h>


    #include "slice.h"

    static char *tasker_names[] =
    {
    "None",
    "DesqView",
    "Windows 3.x (enhanced)",
    "OS/2 2.x"
    };

    void main(void)
    {
    int tasker = detect_multitasker();

    printf("Multitasker found: %s\r\n", tasker_names[tasker]);

    if (!tasker)
    exit(1);

    puts("Waiting for keystroke (no slicing...)");
    while (!kbhit())
    ;

    getch();

    puts("Waiting for keystroke (slicing...)");
    while (!kbhit())
    timeslice();

    getch();

    exit(0);
    }

    --- INN 2.7.2 (20240212 prerelease)
    * Origin: This echo is READ-ONLY. Send %HELP to FAQSERVER at (2:5020/181)