Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Request More Help With 16 Bit DLL

9 views
Skip to first unread message

Wildman

unread,
Sep 24, 2012, 12:04:26 AM9/24/12
to
Hey japheth, the template you provided worked great. It loaded
and unloaded properly without any errors. Everything I had tried
before either resulted in a GPF error or the system just froze.
Thanks again.

I am working on a VB3 program that will open or close the cdrom
tray. Since VB3 does not support any direct method of doing
this, including mci commands and dos interrupts, I had to go to
the dll.

The dll contains 4 procedures (not counting DllMain & WEP) and
they all work perfectly except for one. This proc actually
opens or closes the cd tray. It uses the Device Driver Request
function of the Multiplex (int 2Fh). The function requires a
data structure of 23 bytes called the Device Driver Request
Header. Here is a brief explanation of the important parts of
the header:
HdrLen contains the length of the header
CmdCode contains the IOCTL command code, 0Ch = output
TransOff & TransSeg point to CBCode
CBCode contains the MSCDEX command code, 0 = open, 5 = close

After the header is initialized, the interrupt is called with
ax=1510h, cx=drive number and es:bx point to the header.

I do not know if the proc is not working because there is an
error in program flow or logic or if it is something else.
The interrupt does write to the header. Could this be the
problem? I know the nature of protected mode is to prevent
one program from writing over another. If this is the case,
is there a way to create the dll to allow this, maybe some
directive required in the .def file? Or am I using the
wrong addressing mode initializing the header?

Below is the header structure and the code for the procedure.
I would appreciate any help or suggestions.

.data

; Device Driver Request Header


HdrLen BYTE 17h

SubUnit BYTE 0

CmdCode BYTE 0Ch

DevStat WORD 0

Reserved BYTE 8 DUP (0)

Mode BYTE 0

TransOff WORD 0

TransSeg WORD 0

CBCode BYTE 0

DevParms DWORD 0


.code

SetTray proc far pascal uses ds wDrive:word, wCmd:word


mov ax,dgroup ; Point ds to dgroup

mov ds,ax

mov ax,wCmd ; Set MSCDEX command (only low byte needed)

mov CBCode,al

mov bx,offset CBCode ; Set Transfer Address

mov TransOff,bx

mov bx,ds

mov TransSeg,bx

mov es,bx ; Point es:bx to the DDRH

mov bx,offset HdrLen

mov cx,wDrive ; Move drive number into cx

mov ax,1510h ; Device Driver Request function

int 2Fh ; Call Multiplex

ret


SetTray endp

japheth

unread,
Sep 24, 2012, 2:57:45 AM9/24/12
to
> The interrupt does write to the header.  Could this be the
> problem?

Yes. However, there are several possible problems. It depends on what
task the DOS-extender ( here: Win16 ) will do and what it won't do.

You're inherently assuming that - at least - the following things are
done by the extender:

1. the address of the device driver request header in ES:BX is
translated to a real-mode address
2. the request header is located in conventional memory ( or is copied
to this address space )
3. the transfer address in the request header is translated to a real-
mode address.

My guess is that at least point 3. is NOT done by the extender. Hence
you'll have to use a real-mode address to fill TransOff and TransSeg.
SInce it's just one byte, you can - for a first test - probably use a
byte in the BIOS data area, perhaps 0040h:00F0h. For a final version,
you'll have to a) allocate some conventional DOS memory for the
transfer region or b) ensure that the dll's data segment is in
conventional memory.

Wildman

unread,
Sep 24, 2012, 2:29:39 PM9/24/12
to
On Sun, 23 Sep 2012 23:57:45 -0700, japheth wrote:

>> <snip>

After reading your reply, I believe my problem is related
to the fact that the dos interrupt is expecting real mode
addresses and it is getting PM addresses. Two possible
fixes come to mind.....

1) I understand that a PM address can be calculated like
this: real-mode-segment * 10h + offset. If this is the
case, cannot the reverse be done?
real-mode-segment = PM-address / 10h
offset = PM-address MOD 10h

2) Allocate a dos memory block using function 100h of
int 31h and move the request header there. The function
will return the real-mode segment of the allocated block,
which can be used for the Multiplex call. It also returns
the starting selector number for the block which is used
for deallocating.

If I go with the second option, I have a few questions.
Would it be better to allocate the memory, setup the
request header and store the segment/selector numbers
in variables in DllMain and deallocate in WEP or should
it all be done in the SetTray procedure every time it
is called? The latter seems to me to be the worse way.

Anyway, I have not started rewriting any code yet. I
wanted your insight on the best approach.

I can't thank you enough for you help!

japheth

unread,
Sep 24, 2012, 11:33:46 PM9/24/12
to
> 1) I understand that a PM address can be calculated like
> this: real-mode-segment * 10h + offset.  If this is the
> case, cannot the reverse be done?

The problem is that a protected-mode selector:offset address may give
a linear address that is beyond conventional memory.

> 2) Allocate a dos memory block using function 100h of
> int 31h and move the request header there.  The function
> will return the real-mode segment of the allocated block,
> which can be used for the Multiplex call.  It also returns
> the starting selector number for the block which is used
> for deallocating.

I remember vaguely that in Win16 you'll have to replace the DOS-memory
DPMI functions calls by Win16 calls of GlobalDOSAlloc() and
GlobalDOSFree().

> If I go with the second option, I have a few questions.
> Would it be better to allocate the memory, setup the
> request header and store the segment/selector numbers
> in variables in DllMain and deallocate in WEP or should
> it all be done in the SetTray procedure every time it
> is called?  The latter seems to me to be the worse way.

This is a matter of taste. Both strategies look ok to me.

Rod Pemberton

unread,
Sep 25, 2012, 6:00:04 AM9/25/12
to
"Wildman" <best...@yahoo.com> wrote in message
news:nE18s.165$do6...@newsfe19.iad...
> On Sun, 23 Sep 2012 23:57:45 -0700, japheth wrote:
>
> >> <snip>
>
> After reading your reply, I believe my problem is related
> to the fact that the dos interrupt is expecting real mode
> addresses and it is getting PM addresses. Two possible
> fixes come to mind.....
>
> 1) I understand that a PM address can be calculated like
> this: real-mode-segment * 10h + offset.
>

That isn't the case.

That computes the physical address (upto 1MB+64KB-17B).

PM uses relative addressing as base+offset. The "address" when you're
looking at PM code is actually the "offset" from the segment's base address.
Each PM selector has a PM descriptor which has the base address of the
segment and the length of the segment, known as the limit. You'd have to
compute whether the physical address you have matches an address within the
PM segment. If the base address is zero and the limit is 4GB (entire
segment) and paging is disabled, that's easy. The addresses are equivalent.
If the limit is less than 4GB, you must check to see if the address is
within the segment's range. If the base is non-zero, you must compute where
the address is relative to the base address and check the range. If paging
is enabled, you'll want to physically map the memory region, if it's not
already. Typically, OSes and DPMI hosts physically map the first 1MB or so.
If paging is enabled and you can't physically map the memory region, I've
got no idea how to locate the compute the address, if it's even possible.
You'd probably be adding lots of values from the paging tables together ...


Rod Pemberton




Wildman

unread,
Sep 25, 2012, 11:57:26 AM9/25/12
to
On Tue, 25 Sep 2012 06:00:04 -0400, Rod Pemberton wrote:

> <snip>

Thanks for the post. Even as was posting it I somehow knew
that it could not be that simple. I think japheth's suggestion
about allocating memory for the request header is the way to
go but so far I haven't found any info on calling the win16
api using masm. All the examples I have found have been
written using C, unfortunately I don't speak C. I don't have
anything against it other than the fact that it seemes a
little cryptic to me. Since programming is a hobby, I stuck
with what I liked, BASIC of different flavors and assembly.

japheth

unread,
Sep 25, 2012, 12:24:22 PM9/25/12
to
> go but so far I haven't found any info on calling the win16
> api using masm.

See

http://www.japheth.de/JWasm/Win16.html

Wildman

unread,
Sep 25, 2012, 12:43:42 PM9/25/12
to
On Mon, 24 Sep 2012 20:33:46 -0700, japheth wrote:

> <snip>

> I remember vaguely that in Win16 you'll have to replace the DOS-memory
> DPMI functions calls by Win16 calls of GlobalDOSAlloc() and
> GlobalDOSFree().
>
I would like to give this a try. Would you happen to have an example
of calling an api from a dll using masm, including the correct syntax
for the IMPORTS section of the .def file? Everything I have found on
the web has been for masm32. Also, I have not been able to find a 16
bit version of windows.inc. None came with the version of masm6 I
have. I hope that don't turn out to be a problem.

Wildman

unread,
Sep 25, 2012, 4:15:40 PM9/25/12
to
Thanks for the link. It will be a big help. But, if I may
impose on you again, I have a couple more questions.

When an api is called that returns a value, how is that value
returned. I would assume it is returned in ax but in the case
of GlobalDOSAlloc, it returns a double word. How is that done?
Maybe one word in ax and the other in another register?

Also I want wondering about the .def file. Do I need entries
in the IMPORTS section? Do I just use the name or the ordinal
number? I did find a web site that has a list ordinal numbers
for all the api's. I would appreciate an example of what an
import entry looks like.

japheth

unread,
Sep 26, 2012, 2:06:48 AM9/26/12
to
> When an api is called that returns a value, how is that value
> returned.  I would assume it is returned in ax but in the case
> of GlobalDOSAlloc, it returns a double word.  How is that done?
> Maybe one word in ax and the other in another register?

If you read the sample carefully, you'll find several times a "cwd"
opcode. As you may be aware, this instruction "sign-extends" ax to
register pair dx:ax.

> Also I want wondering about the .def file.  Do I need entries
> in the IMPORTS section?  Do I just use the name or the ordinal
> number?  I did find a web site that has a list ordinal numbers
> for all the api's.  I would appreciate an example of what an
> import entry looks like.

I don't think you need any import entries in the .def file. The proper
way to resolve external references in Win16 is using an import library
( quite similar as it's done in Win32 ). The MS name of the Win16
import library was libw.lib ( Borland used "import.lib", IIRC ). If
you don't have this lib, you may be able to create one - see the
comments in the Win16 sample.

Wildman

unread,
Sep 26, 2012, 12:25:22 PM9/26/12
to
On Tue, 25 Sep 2012 23:06:48 -0700, japheth wrote:

>> When an api is called that returns a value, how is that value returned.
>>  I would assume it is returned in ax but in the case of GlobalDOSAlloc,
>> it returns a double word.  How is that done? Maybe one word in ax and
>> the other in another register?
>
> If you read the sample carefully, you'll find several times a "cwd"
> opcode. As you may be aware, this instruction "sign-extends" ax to
> register pair dx:ax.
>
I was able to answer this question myself. I found a forum with a
similar question. I should have known anyway. Most or all dos calls
that return a dw use dx:ax. I was not familiar with the cwd opcode.
I mentioned earlier that I was an amateur and still learning. :-)

>> Also I want wondering about the .def file.  Do I need entries in the
>> IMPORTS section?  Do I just use the name or the ordinal number?  I did
>> find a web site that has a list ordinal numbers for all the api's.  I
>> would appreciate an example of what an import entry looks like.
>
> I don't think you need any import entries in the .def file. The proper
> way to resolve external references in Win16 is using an import library (
> quite similar as it's done in Win32 ). The MS name of the Win16 import
> library was libw.lib ( Borland used "import.lib", IIRC ). If you don't
> have this lib, you may be able to create one - see the comments in the
> Win16 sample.
>
I do have libw.lib. It came with the win 3.1 SDK.

Ok, it looks like I have everything I need to start writing code.
No way I could have done it without your help. Thank you!!!!!!!!!
On the slim chance that I may be able to do something for you, I
am at your service.
0 new messages