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