{
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.
}
