Discussion:
DOS extension proposal
(too old to reply)
muta...@gmail.com
2023-05-21 04:07:07 UTC
Permalink
Ok, so this is initial thoughts. I don't think I've ever
actually written a TSR before, so I may have some
technically impossible suggestions.

Also note that this is an extension for RM16 and
potentially PM16 and the Turbo 186 (8-bit segment
shift instead of 4-bit). It has no relation to 32-bit.


My main interest is C programs, so I would like a
TSR for genuine MSDOS that is similar to MSVCRT.DLL.

My main interest is in huge memory model, and I intend
for PDOS/86 (built as huge memory model) to export its
own C library, so that it will be able to run PDOS-generic
8086 applications, which assume the C and Pos* library
functions are provided via a parameter on the stack.

I'm not sure how you're supposed to hook MSDOS at the
point that it executes an app. Like HX does. Perhaps the
"exec" facility is hooked and then you switch the name of
the executable to a stub and let MSDOS execute that stub
so that you regain control and can load the original program,
with the required parameter on the stack this time.

So it sounds like PDOS-generic apps (which don't execute
the INT instruction) can be run. This allows a pseudo-bios
to run under genuine MSDOS that is able to redirect a
PDOS-generic application to use an emulated disk instead
of bypassing PDOS-generic itself and going directly to
MSDOS via INT 21H.

Note that I can't (or perhaps won't) attempt to stop a "badly
behaved" executable from bypassing PDOS-generic and
going directly to MSDOS (or the BIOS).

That should cover most of what I want.

But that would still require the user to load the TSR in
advance. Otherwise the app would attempt to access
the stack and have a wild pointer.

So I need to be able to access the stack, but only if I know
it is valid.

If it is not valid, then I need to either load the TSR at this
point, or print a message (using INT 21H) to say that this
is a PDOS-generic application running under genuine MSDOS
which won't work without the TSR.

And I need to find out whether the stack is valid, without
doing an INT which will bypass the PDOS-generic
environment.

So I was thinking of using a field in the PSP.

I can see at offset 40H is some sort of version.

I was thinking that PDOS-generic for 8086 and the TSR
could populate this with a minor version number of
x'80', such that minor versions >= 128 are actually
PDOS, with the minor version having 128 subtracted.

I could use an environment variable instead, but I'm not
keen on pointers that have only a segment. I want a
proper far pointer.

This (running PDOS-generic apps) would basically bypass
some other issues I had.

One is that I want to eliminate the hardcode 4-bit shift in
my DOS startup code, designed to release memory, since
the Turbo Linker asks for maximum memory. The shift
would potentially be 8-bit on a Turbo 186, and when running
under a PM16 version of PDOS, ie PDOS/286, the segment
manipulation is more complicated. I intended to provide
huge pointer manipulation facilities in PDOS/86, and only
if they weren't present (because it was genuine MSDOS),
would I fall back to 4-bit segment shift assumptions.

Note that Watcom C (not sure about Microsoft C), generates
function calls to manipulate huge pointers (which all pointers
are in the huge memory model). Those functions can use the
PDOS/86-provided manipulation functions (if available),
otherwise use code that assumes 4-bit segment shifts.

Also note that I use a 32-bit size_t in PDPCLIB for huge
memory model, even though Watcom's library doesn't.

So I can use INT 21H extension functions to see if these
manipulation functions are available.

This would be useful if I wanted a traditional MSDOS
application that uses INT 21H to start running on an
appropriate Turbo 186/80286 using a flavor of PDOS,
to continue to work and make use of the extra memory
available.

So long as the traditional MSDOS program obeyed some
rules. Those rules are not actually traditional, but could
have been created in the 1980s. Basically you need to be
careful before you manipulate a segment register.

Normally C programs don't actually manipulate segment
registers. They are loaded with values that were set at
load time.

The PDOS-generic app would still load DGROUP into the
segment registers at startup. ie the segmentation will
necessitate some assembler, unlike the simple scheme
I have for PDOS-generic for 32-bit.

Still mulling over what that startup assembler should
ideally look like.

BFN. Paul.
muta...@gmail.com
2023-05-21 05:46:59 UTC
Permalink
Note that not directly doing an INT 21H has an analogy
on the Amiga.

On the Amiga, all system calls are vectored off a
hardcoded address 4.

Which prevents PDOS-generic or some other emulator
from redirecting the syscalls to a different system.

Unless you were to use hardware paging or whatever
to redirect address 4 somewhere else. But I don't want
to rely on such hardware existing, or able to be
implemented (with privilege).

So what I did was create a "new traditional standard"
(basically something that could/should have been done
in the 1980s) where Amiga apps first check register
d6 (there's a bit more to it than that) to see what the
base address is. If d6 is set to some value then use
that, otherwise use the normal address 4. (something
similar to that).

So that gives the ability of a software override.

So it seems I'm trying to construct the 8086 equivalent.
Plus other things all lumped in to one big barrel of
confusion.

BFN. Paul.
muta...@gmail.com
2023-05-21 06:36:18 UTC
Permalink
Note that the direct equivalent of the Amiga on the 8086
would be if I checked the size at offset 0x80 in the PSP
and if the command length was greater than 0x80, which
is impossible, then it is a fake length, and it means the
caller is PDOS, and subtract x'80' from the length to get
the actual length, and now you know whether it is a
PDOS environment or not.

On the Amiga, that would allow D6-aware programs to
run on an Amigados semi-clone.

But that's still using the Amigados API, not PDOS-generic.

PDOS-generic doesn't care about D6/location 4 and the
startup code of apps just assumes it is PDOS-generic.

I guess the 8086 equivalent would be if I had an Amigados
semi-clone that had enhancements and I wished to
support both enhancement-aware apps and traditional
apps.

BFN. Paul.
muta...@gmail.com
2023-05-21 06:45:24 UTC
Permalink
And so the equivalent would be -

PDOS-generic programs just assume that the
required data is on the stack and carry on.

But traditional programs are free to do INT 21H.

So two different startup routines are required.

Which I already have anyway.

So PDOS-generic can be largely ignored (*).

For traditional programs, I need them to do INT 21H
calls to see if extensions are available before deciding
to hardcode a segment shift of 4 bits.

And continue to allow them to overcome the traditional
issue with linkers asking for all memory.

BFN. Paul.


(*) I could still put in some checks in the PSP so that they
could gracefully exit via INT 21H after reporting via 21H
that this requires a PDOS environment.
muta...@gmail.com
2023-05-21 06:56:01 UTC
Permalink
Post by ***@gmail.com
(*) I could still put in some checks in the PSP so that they
could gracefully exit via INT 21H after reporting via 21H
that this requires a PDOS environment.
Another possibility would be to do what Windows does
with PE and have a stub that terminates gracefully, and
Windows bypasses the stub.

But that requires a relatively expensive change in the tools.

Getting the startup code to inspect the PSP is cheap.

And setting a minor version number of 128+ is cheap too.

Or some other appropriate field in the PSP.

BFN. Paul.
JJ
2023-05-21 10:35:58 UTC
Permalink
Post by ***@gmail.com
Ok, so this is initial thoughts. I don't think I've ever
actually written a TSR before, so I may have some
technically impossible suggestions.
Also note that this is an extension for RM16 and
potentially PM16 and the Turbo 186 (8-bit segment
shift instead of 4-bit). It has no relation to 32-bit.
My main interest is C programs, so I would like a
TSR for genuine MSDOS that is similar to MSVCRT.DLL.
My main interest is in huge memory model, and I intend
for PDOS/86 (built as huge memory model) to export its
own C library, so that it will be able to run PDOS-generic
8086 applications, which assume the C and Pos* library
functions are provided via a parameter on the stack.
I'm not sure how you're supposed to hook MSDOS at the
point that it executes an app. Like HX does. Perhaps the
"exec" facility is hooked and then you switch the name of
the executable to a stub and let MSDOS execute that stub
so that you regain control and can load the original program,
with the required parameter on the stack this time.
So it sounds like PDOS-generic apps (which don't execute
the INT instruction) can be run. This allows a pseudo-bios
to run under genuine MSDOS that is able to redirect a
PDOS-generic application to use an emulated disk instead
of bypassing PDOS-generic itself and going directly to
MSDOS via INT 21H.
Note that I can't (or perhaps won't) attempt to stop a "badly
behaved" executable from bypassing PDOS-generic and
going directly to MSDOS (or the BIOS).
That should cover most of what I want.
But that would still require the user to load the TSR in
advance. Otherwise the app would attempt to access
the stack and have a wild pointer.
So I need to be able to access the stack, but only if I know
it is valid.
If it is not valid, then I need to either load the TSR at this
point, or print a message (using INT 21H) to say that this
is a PDOS-generic application running under genuine MSDOS
which won't work without the TSR.
And I need to find out whether the stack is valid, without
doing an INT which will bypass the PDOS-generic
environment.
So I was thinking of using a field in the PSP.
I can see at offset 40H is some sort of version.
I was thinking that PDOS-generic for 8086 and the TSR
could populate this with a minor version number of
x'80', such that minor versions >= 128 are actually
PDOS, with the minor version having 128 subtracted.
I could use an environment variable instead, but I'm not
keen on pointers that have only a segment. I want a
proper far pointer.
This (running PDOS-generic apps) would basically bypass
some other issues I had.
One is that I want to eliminate the hardcode 4-bit shift in
my DOS startup code, designed to release memory, since
the Turbo Linker asks for maximum memory. The shift
would potentially be 8-bit on a Turbo 186, and when running
under a PM16 version of PDOS, ie PDOS/286, the segment
manipulation is more complicated. I intended to provide
huge pointer manipulation facilities in PDOS/86, and only
if they weren't present (because it was genuine MSDOS),
would I fall back to 4-bit segment shift assumptions.
Note that Watcom C (not sure about Microsoft C), generates
function calls to manipulate huge pointers (which all pointers
are in the huge memory model). Those functions can use the
PDOS/86-provided manipulation functions (if available),
otherwise use code that assumes 4-bit segment shifts.
Also note that I use a 32-bit size_t in PDPCLIB for huge
memory model, even though Watcom's library doesn't.
So I can use INT 21H extension functions to see if these
manipulation functions are available.
This would be useful if I wanted a traditional MSDOS
application that uses INT 21H to start running on an
appropriate Turbo 186/80286 using a flavor of PDOS,
to continue to work and make use of the extra memory
available.
So long as the traditional MSDOS program obeyed some
rules. Those rules are not actually traditional, but could
have been created in the 1980s. Basically you need to be
careful before you manipulate a segment register.
Normally C programs don't actually manipulate segment
registers. They are loaded with values that were set at
load time.
The PDOS-generic app would still load DGROUP into the
segment registers at startup. ie the segmentation will
necessitate some assembler, unlike the simple scheme
I have for PDOS-generic for 32-bit.
Still mulling over what that startup assembler should
ideally look like.
BFN. Paul.
If you're writing a system extension which are not compatible with MS-DOS,
you can't tell MS-DOS programs to obey by the extension's rules, because
most MS-DOS programs already built to obey MS-DOS rules. Otherwise none of
MS-DOS program will work with that extension.
muta...@gmail.com
2023-05-21 21:23:19 UTC
Permalink
Post by JJ
Post by ***@gmail.com
Still mulling over what that startup assembler should
ideally look like.
If you're writing a system extension which are not compatible with MS-DOS,
you can't tell MS-DOS programs to obey by the extension's rules, because
most MS-DOS programs already built to obey MS-DOS rules. Otherwise none of
MS-DOS program will work with that extension.
Sure. And even today, in 2023, my own executables fall into
this category, as they have for about 3 decades.

My own executables have zero chance of working on a
Turbo 186 because the startup code assumes segment
shifts will be 4 bits.

Back circa 1987 I should have sat down and prepared
for the introduction of the Turbo 186 (8-bit segment shifts).

As the saying goes "better late than never".

So I'm hoping that by 2024, whenever I build an executable,
the executable is prepared for the Turbo 186. And a similar
scheme (lots of selectors lined up) for PM16.

I can't "fix" everyone else's executables, but I at least want to
fix my own.

That's my question.

How do I fix my executables? The number 4 (seen below) is out
(for starters).

BFN. Paul.


; It seems that even compact/large/huge require the same thing -
; they expect DS=SS and may use near pointers and the stack is
; in the DGROUP for this reason. I haven't actually observed
; near pointers being used though.

mov bx,ss
mov ax,ds
sub bx,ax
mov cl,4
shl bx,cl

mov bp, sp
add bp, bx
mov ss, dx
mov sp, bp

Loading...