[Back to TSR SWAG index] [Back to Main SWAG index] [Original]

    {
    CEES BINKHORST

    > I have a Turbo Pascal program running in DPMI mode that needs to
    > interface to a real mode TSR program.  The TSR program issues
    > INT$61 when it has data that needs to be serviced.  I've installed
    > an interupt service routine that works ok in real mode, but not DPMI.

    Have a look at the following. With some amendments it will do what you want.

    ;from c't 1/1992 #196
    .286p                  ;generate protected mode code for 286 or higher

    dpmitsr segment public ;dpmitsr: name of program
                           ;segment: indicates start of programcode for
                           ; 'dpmitsr'. see also end of code: 'dpmitsr ends'
                           ;public (without addition (name)): instruction
                           ; for linker to put all of it in one segment
         assume cs:dpmitsr, ds:dpmitsr ;as soon as program starts 'cs' and
                                       ; 'ds' cpu-registers (code and data segment
                                       ; segment registers) are filled with memory
                                       ; position of start of program 'dpmitsr'
                                       ;there are no seperate code en data
                                       ; segments
    olduserint     label word       ;
                   dw    ?, ?
    readmessage    db 'This text is in TSR and is be shown through a pointer.', 0
    writemessage   db 'This text is copied from TSR', 0
    writedata      equ $-offset writemessage ;calculate length 'writemessage' and
                                             ; use value later in program
    ;--------- procedure 'userint' is comparable with a pascal instruction if:
    ; case ah of 0: execute instruction 'message'
    ;            1: excute instruction 'read'
    ;            2: excute instruction 'write'
    userint     proc far ;new int 61h
                         ;userint: name has only measning within thsi text for
                         ; compiler - see also end 'userint endp'
                         ;proc far: instruktion for compiler to generate code
                         ; to push a segment:offset return address on the stack
                         ; (near proc pushes only offset)
                         ; this procedure is called from another
                         ; code segment (dos through int. in dpmiwin!)
                pushf    ;save flags
                cmp ah, 00h ;message instruction - see dpmiwin dcs.eax:=$00000000
                je  message
                cmp ah, 01h ;read instruction = dpmiwin dcs.eax:=$00000100
                je  read
                cmp ah, 02h ;write instruction = dpmiwin dcs.eax:=$00000200
                je  write
                popf        ;put flags back if ah is not 00, 01 or 02 in ah and
                jmp dword ptr cs:[olduserint]  ; continue with old interrupt
    ;---------- procedure 'message'
    message:    mov ax,0affeh     ;affe hex is in-memory mark of this program
                popf              ;put flags back
                iret              ;interrupt ends here and has only put
                                  ; affe hex in cpu register ax
                                  ;program dpmiwin will see it there and know then
                                  ; that 'dpmitsr' is loaded in memory
    ;---------- procedure 'read'
    read:       mov ax, seg dpmitsr  ;make registers es:di together pint to
                mov es, ax           ; string readmessage. this will then be used
                                     ; by 'dpmiwin' to put it on the screen
                mov di, offset readmessage
                popf              ;put flags back
                iret              ;interrupt ends now here
    ;---------- procedure 'write'
    write:      push cx           ;save registers
                push si
                push ds
                cld               ;direction flag = 0
                mov cx, seg dpmitsr
                mov ds, cx        ;make ds:si point to string writemessage
                mov cx, writedata ;get calculated length of string writemessage
                mov si, offset writemessage
                rep movsb         ; and copy string from ds:si to es:di.
                                  ; es:di are put in dpmicallstruc by dpmiwin
                pop ds            ;put registers back
                pop si
                pop cx
                popf              ;put flags back
                iret              ;interrupt now ends here
    userint     endp              ;end of code for procedure 'userint'
                                  ; van gehele echte interrupt dus
    ;---------- this code does not remain in memory
    install:    mov ax, seg dpmitsr   ;make ds:dx point to string hello$
                mov ds, ax
                mov dx, offset hello$ ;offset of message that it is installed
                                      ; as a memory-resident program
                mov ah, 09h           ;send string hello$ to (dos) screen
                int 21h               ; to signal installation of 'dpmitsr'
                mov ax, 03561h        ;what is old address of int. 61h
                int 21h
                mov [olduserint], bx  ; and save it in two steps
                mov [olduserint+2], es ;
                mov ax, 2561h         ;subfunction 25 of int. 21: pu new address
                mov dx, offset userint; int. 61h (procedure 'userint') in memory
                int 21h
                mov dx, offset install ;how many bytes (convert to paragraphs by:
    shr 4)
                shr dx, 4              ;  of program have to
                add dx, 011h           ;  remain in memory
                mov ax, 03100h         ;subfunction 31 of int. 21 with 'return
    code' 0
                int 21h                ;makes part of program resident
    hello$      db  13,10,'DPMITSR-example installed.',13,10,'$'
    dpmitsr     ends
                end install            ;end of installation procedure

    }
    program dpmiwin;               {from c't 1/1992 # 197}

    uses
      winprocs,
      wintypes,
      win31,
      wincrt;

    type
      tDPMICallStruc = Record  {for use by RMInterrupt}
        EDI, ESI, EBP, Reserved,
        EBX, EDX, ECX, EAX : longint;
        Flags, ES, DS, FS,
        GS, IP, CS, SP, SS : word;
      end;

    function RMInterrupt(IntNo, flags, copywords : byte;
                         var DPMICallStruc : tDPMICallStruc) : boolean;
    begin
      asm
        push es       {save es en di from protected mode on stack}
        push di
        mov bh, flags {if bit 0 is zero interrupt controller ...}
                      {... and A20-line will be reset. other bits must be zero}
        mov bl, intno {put interrupt nummer to be executed in register bl}
        mov cx, word ptr copywords {cx = number of words that are to be copied...}
                      { from... prot. mode to real mode stack}
        mov ax, 0300h {put DPMI simulate real mode interrupt nummer in register ax}
        les di, dpmiCallStruc  {16-bits pointer to record - 32 bits uses edi}
                      {les di, ...: load segment (2 bytes) dpmicallstruc in}
                      { register di en offset (ook 2 bytes) }
                      { in register es. in short load pointer to dpmicallstruc}
                      { in registers di:es                                 }
        int 31h       {excute interrupt nummer in bl in real-mode after filling }
               { cpu-registers with values from dpmicallstruc and return in}
               { protected mode with contents of cpu-registers at end of real-mode}
               { interrupt in dpmicallstruc. i.o.w. act as if dpmicallstruc  }
               { are the cpu-registers at the end of excuting the real-mode int.}
        jc @error
        mov ax, 1           {function succesfull}
        jmp @done
       @error:
        xor ax, ax          {make ax=0, function not succesfull}
       @done:
        pop di              {put es and di back}
        pop es
      end;
    end;

    var
      selector  : word;
      segment   : word;
      selseg    : longint;
      dcs       : tdpmicallstruc;
      printstrg : pchar;

    begin
      fillchar(dcs, sizeof(dcs), 0);    {zero dcs}
        {------- verify presence of dpmitsr in memory}
      dcs.eax := $00000000;  {just for clarity that ax is called with function 0}
                             { as contents is already zero because of use}
                             { of function filchar() on previous line. }
      rminterrupt($61, 0, 0, dcs);
      if (dcs.eax and $ffff = $affe) then
        writeln('DPMItsr in memory')
       else
         writeln('Something went wrong!');
                 {this part needs improvement.                 }
                 {if dpmitsr is not in memory then pc may crash,}
                 { which is not strange as then an interrupt  }
                 { is called that most likely is 0000:0000 in        }
                 { memory.                                        }
                 {this is to be substituted with a routine that first checks}
                 { that pointer of int. 61 is not 0000:0000.        }
        {------- read string through pointer}
      dcs.eax := $00000100;              {call int. 61 (=dpmitsr) with ah = 1}
      rminterrupt($61, 0, 0, dcs);
      selector := allocselector(word(nil)); {make new selector and fill with values:}
      setselectorbase(selector, longint(dcs.es) * 16);
                                          { base: es is put in by 'dpmitsr'}
      setselectorlimit(selector, longint($ffff));
                                         { and limit: $ffff is maximum value. this}
                                         { does not give problems because we put a}
                                         { 'zero-terminated' string on the screen.}
      printstrg := ptr(selector, word(dcs.edi));   {also di is put in by 'dpmitsr'  }
      writeln(printstrg);
      freeselector(selector);
        {------- read string by making a copy from real-mode memory to
                  Windows-memory in low 640k-area}
      selseg := globaldosalloc(100); {allocate 100 bytes Windows-memory below 640k.}
                                   {high word of longint 'selseg' is segment for }
                                   { use in real-mode and low word is selector    }
                                   { for use in protected mode.                 }
      if selseg <> 0 then
      begin
        selector := word(selseg and $ffff);  {determine selector}
        segment  := word(selseg shr 16);      {determine segment}
        dcs.eax  := $00000200;               {call int. 61 (=dpmitsr) with ah = 2   }
        dcs.es   := segment;                   {use segment for int. 61 in real-mode}
        dcs.edi  := 0;                       {offset is 0                          }
        rminterrupt($61, 0, 0, dcs);
        printstrg := ptr(selector, 0);
        writeln(printstrg);
        globaldosfree(selector);
      end;
    end.

    {
    To excute the program, dpmitsr.exe has to be executed before starting Windows.
    Dpmitsr will remain permanently in memory.

    Both DPMITSR.ASM and DPMIWIN.PAS were nicely running programs in early 1992
    with TPW. Now, under BPW an error is reported from the SYSTEM unit.
    However, as I now don't have the time to trace the error herewith the programs,
    as it will surely point the way for you to go.
    }

[Back to TSR SWAG index] [Back to Main SWAG index] [Original]