I'm responding to all of your mails (Jerome's) so far.

On at 2023-04-21 06:55 -0400, jer...@shidel.net wrote:
Hi All,

At present, the interface program for Logger just performs a slightly optimized brute force search for the Logger device driver. Although reliable, it is very slow compared to providing a simple interrupt call to test for installation.

I agree that scanning memory is not good enough. However, you could find the DOS device driver chain and scan it for your driver. It starts with the "NUL" device header in the DOS data segment (get approximate location with interrupt 21h function 52h) and ends when an offset of the "next device" pointer is 0FFFFh.

I also examined your implementation so far [1]. Here's some things I noticed:

1. You misspellt "it's location", should be "its".
2. You start the search at paragraph 0100h. It is a remote possibility for the initial low memory area part of DOS to take up less than 4096 bytes however.
3. Your %%Comparing loop can use lodsb \ scasb or even cmpsb in fact.
4. If SEARCH_LOW is defined then when bx is equal to dx (your .COM application's ds) and dx was above 0A000h it will loop back to %%Next, incrementing bx again. I don't understand the logic behind this. If bx was below-or-equal 0A000h then you set it to 0A000h here. I'm fairly sure this is not very good, because your application can load lower than the device driver but the latter can still be below 0A000h.
5. push bx \ pop es is not very useful when mov es, bx is also 2 bytes.
6. A comment at %%Done says that upon reaching there with CY, es equals "undefined (0xffff)". This is only true for when the SEARCH_LOW option is in use. Otherwise it might end at 9FFFh, I think. 7. It is confusing to initialise di to 9 for the segment to check then have it immediately incremented to 10 (the offset of the device driver name in the device header) in the first iteration of %%Comparing. Could do with a comment, at least.

The CMPDD macro (just above what I linked in [1]) is neat to be sure. The conditional code mechanism [2] also looks good.


[1]: https://gitlab.com/DOSx86/logger/-/blob/aae3dfddcdacfea18950a96ce9449767c20b2d66/source/logger/common.inc#L267 [2]: https://gitlab.com/DOSx86/logger/-/blob/aae3dfddcdacfea18950a96ce9449767c20b2d66/source/logger/common.inc#L1102


Looking at the different interrupts, I think I have come up with a solution that will work well for Logger and any other driver or program that needs such a check. So, I’d like to propose a “standard” we could use. I’d like to get your feedback on what I’m thinking…

As the others have mentioned, there is a "standard" already. AMIS provides everything you propose, and more. And if you think you don't want to support all of the bits, even those required for specification compliance, you can always pick and choose what to include.

Looking at RBIL, interrupt 0x2b is barely used by anything. Under MS-DOS and FreeDOS, this simply points to an IRET. Under IBM ROM-DOS, AH functions 0x00-0x03 do some things. But, all other calls do nothing.

https://fd.lod.bz/rbil/interrup/dos_kernel/2b.html <https://fd.lod.bz/rbil/interrup/dos_kernel/2b.html>

This is precisely the rationale of why AMIS (eventually) ended up on interrupt 2Dh. It is not generally in use, and points to an iret on MS-DOS v3 compatibles by default. I'm about to add a check for its validity to my TSR applications, which checks that the segment is nonzero and the offset is not equal to 0FFFFh [3]. (This check is only for the example for now, but I will pick it into all my TSRs eventually.)


[3]: https://hg.pushbx.org/ecm/tsr/rev/aafe8caaf4f5


An install check issuing this interrupt would be simple to perform. A program could set the Carry Flag,

Carry Flag is a nonstarter for a multiplex interrupt check, because you either need to return with retf 2 (likely scrambling the Direction Flag and Trace Flag and Interrupt Flag), or do a complicated dance to pass the CF or most arithmetic status flags to the fl word on the interrupt stack frame, eg using lahf [4] [5] (arithmetic flags except Overflow Flag) or a little rcr \ rol trick [6] (modifies Carry Flag only).


[4]: https://github.com/640-KB/GLaBIOS/issues/20
[5]: https://github.com/MobyGamer/softhddi/issues/1
[6]: https://hg.pushbx.org/ecm/seekext/file/bbfcfa0d1c0b/resident.asm#l254


load AH/AX with “check for install” function and set DS:BX to point to an identifier string (minimum 8 characters, no maximum). Then call the interrupt.

AMIS already provides a standard way to do this. Instead of providing a pointer to the resident handlers, however, it returns a pointer to the resident's signatures. Those take up at least 17 bytes, 8 bytes intended for "manufacturer", the next 8 bytes for "product", and then up to 64 bytes for a longer descriptive name (NUL-terminated). This longer string is optional in that it can just hold the empty string, and in that case you only need to allocate 17 bytes for all the signatures. The code to support this is about 20 bytes, so we get 37 bytes in total. And that is with a version code returned in cx.

-a 100
21C9:0100 cmp ah, 26
21C9:0103 je 110
21C9:0105 test al, al
21C9:0107 je 110
21C9:0109 dec al
21C9:010B mov cx, 100
21C9:010E mov di, 100
21C9:0111 mov dx, cs
21C9:0113 iret
21C9:0114
-

This compares to at least 18 bytes of resident code for your proposal, plus 8 bytes for the signature (= 26 bytes) that you store in the code segment, and that is with all registers free to change. I'm also not counting any code required to signal success and return a memory reference to the caller, which my AMIS example does both include (if you are willing to dual-use the signature pointer for its intended purpose plus pointing to your resident program).

-a 100
21C9:0100 cmp ah, 26
21C9:0103 je 110
21C9:0105 push cs
21C9:0106 pop es
21C9:0107 mov di, 100
21C9:010A mov cx, 4
21C9:010D repe cmpsw
21C9:010F je 110
21C9:0111 iret
21C9:0112
-

The AMIS design is also better in using and returning only GPRs for its installation check function, no segment registers. This helps to use the interface eg from DPMI applications.

On return, if the Carry Flag is still set, then the install check failed. If the Carry Flag is clear, it succeeded and other register would be modified to according to the needs of the programs.

Implementation for Logger (as an example) could be:

    CHECK:
    push cs
    pop  ds; set DS to our code segment
    stc; set the carry flag
    mov  ax, 0xfd00; set AX to install check function
    mov  bx, LOGGER_ID; offset to LOGGER device driver ID
    int  0x2b; call install check interrupt
    jc   NOT_INSTALLED; nothing changed, driver is not loaded
    mov  [DRIVER_PTR + 2], es; save segment of far call to driver
    function dispatcher
    mov  [DRIVER_PTR], di; save offset of far call to driver function
    dispatcher
    jmp  MAIN

    DRIVER_PTR:
    dw 0, 0; pointer to driver function dispatcher

    LOGGER_ID:
    db 'LOGGERxx'; Logger device driver identifier. 8+ Characters.

    MAIN:; do program stuff

    NOT_INSTALLED:; driver not loaded, die now with error message


As for the installed Driver, TSR or other extension, you just hook int 0x2b. If AX=0xFD00 and DS:BX points to an identifier you recognize, then it would clear the carry flag and return values in whatever registers required. Otherwise, it completely ignore it and just jump to the previous interrupt handler.

This raises several questions:

1. If it eventually chains the call, should it preserve all register values from the original caller? If yes, you are looking at significantly more code than needed by the minimum AMIS multiplexer. 2. Your callout is specific to each program as you provide the signature rather than letting the multiplexer return its signature. 3. If 2 wasn't already a problem then you still would have an interface that could modify registers unknown to the caller. Is it enough to preserve all 16-bit GPRs and segregs? 386 registers? FPU registers, MMX, XMM, etc?

It requires no additions to the kernel, is backwards compatible and should have a low probability of any collisions.

Any thoughts on using it as a very minimal “standard” extension installation check?

:-)

I do have thoughts, and most of them go back to "use a standard that exists already". The backwards compatibility and kernel agnostic nature of AMIS is the same as for your proposal. Unlike your proposal so far, AMIS multiplexers can be scanned by other programs regardless of whether they "know" the particular multiplexer.


On at 2023-04-21 10:03 -0400, jer...@shidel.net wrote:
> Hi Eric and Bret,
>
> I looked at 2D, 2F and even absolute AMIS 7D.

Interrupt 7Dh was an early AMIS proposal. As far as I am aware it was never actually implemented.

Interrupt 2Fh has problems like being used for the FS redirector (function 11h), DOS internal functions often used by the redirector (function 12h), and the FreeDOS SHARE installation check callout and all its business logic (function 10h). And there was little agreement what exactly an installation check should entail, or imply.

>> On Apr 21, 2023, at 7:44 AM, Eric Auer <e.a...@jpberlin.de
>> <mailto:e.a...@jpberlin.de>> wrote:
>>
>>
>> Hi Jerome,
>>
>> there is no need to allocate a whole int 0x2b for just one driver.
>
> It need not be just for one driver.
>
> Anyone could hook 0x2b and is a very loose and light weight method with
> an extremely low possibility of collision or conflict. Also, nothing
> else uses 2B so it will not effect general system performance. We also
> don’t need to worry about Microsoft changing things and breaking it.

All true of AMIS as well.

>> There are mechanisms which already invite drivers to share them :-)
>>
>> INT 2D - ALTERNATE MULTIPLEX INTERRUPT SPECIFICATION (AMIS) [v3.6]
>>        AH = multiplex number
>>        AL = function
>>            00h installation check
>>            01h get private entry point
>>            02h uninstall
>>            03h request popup
>>            04h determine chained interrupts
>>            05h get hotkey list
>>            06h get device-driver information
>>            07h-0Fh reserved for future enhancements
>>                Return: AL = 00h (not implemented)
>>            other  application-dependent
>>        other registers vary by function (also see individual entries
>> below)
>>
>> You also are in good company there, for example screen thief uses it.
>
> https://fd.lod.bz/rbil/interrup/tsr/2d.html
> <https://fd.lod.bz/rbil/interrup/tsr/2d.html>
>
> I was looking at it. It seems to require a good deal of additional
> overhead and complexity for just a “hey, where are you?” install check.
> Which will increase the memory resident footprint a little unnecessarily.

As per my first examples, the numbers are actually fairly close for a minimal implementation of AMIS vs your proposal so far. To reiterate, you don't have to provide all the possible AMIS interfaces, even a reduced and possibly specification-noncompliant set of features is better to provide on the AMIS interface than brewing your own. Even minimal support for AMIS buys you more than your own custom interface.

> However, it is an (at least partially) accepted “standard” and possibly
> the better solution and would not increase the footprint that much. It
> is definitely worth further consideration.

Agreed.

>
>>
>> INT 2F - Multiplex - NOTES
>>        AH = identifier of program which is to handle the interrupt
>>           00h-3Fh reserved for IBM (for DOS)
>>           40h-7Fh reserved for Microsoft (for DOS)
>>           80h-B7h reserved for IBM
>>           B8h-BFh reserved for networks
>>           C0h-FFh reserved for applications
>>        AL is the function code
>>   This is a general mechanism for verifying the presence of a TSR and
>>   communicating with it.
>>
>> AMIS got introduced (by RBIL, sort of?) because so many apps use INT 2F,
>> but if you only need an install check, performance is no issue and you
>> can use INT 2F without problems.
>>
>> You can also use INT 2F just for detection and then acquire a pointer
>> to a fast call provided by your driver if you need something quick.
>
> I don’t think 2F would be a good choice. In part, you have the network
> redirector and other filesystem related functions on that interrupt.
> Every hook that gets installed there takes a little slice of time to
> process. Which in turn, can slow down normal file operations.
>
> Also, I think that there is to high a risk of having a possible
> collision with 2F with programs that just arbitrarily grab a fixed
> “unused” function to intercept.

Agreed on both counts.

> I think it comes down to either using 2D or 2B. I like 2B because there
> is almost no overhead and very simple to implement on both ends. But, 2D
> might be the better way to go and is a pre-existing semi-accepted standard.
>
> :-)

I would argue that AMIS is actually better designed, and there is no need to split up things with Yet Another Multiplex Interrupt standard.


On at 2023-04-21 11:04 -0400, jer...@shidel.net wrote:
>
>
>> On Apr 21, 2023, at 10:21 AM, tom ehlert <t...@drivesnapshot.de> wrote:
>>
>> Hi,
>>
>>
>>> At present, the interface program for Logger just performs a slightly optimized brute force search for the Logger device driver. Although reliable, it is very slow compared to providing a simple interrupt call to test for installation.
>>
>> given that this detection will be done once per program start: how many microseconds do you expect to save?
>
> Well, there is the catch.
>
> At present, the current semi-optimized brute force search is quick. The delay in finding the driver is not really noticeable when a user invokes the interface program to view, print or perform some other operation.
>
> But there is a delay and those delays can add up. For example, if the FreeDOS installer ran the interface program many times to put messages into the log or write the results of programs like FDISK there for debugging installation issues. All those executions will add up.
>
> But, like you said, how much performance will be gained? For example, if adding a comment to the log takes 1 second to load the interface program from floppy diskette and only 1/1000 of a second to find the driver, then improving that makes almost no sense at all.
>
> I should probably benchmark it to see if it is worth the bother. But, it would vary widely based on system and numerous other factors.

The AMIS route insures that you need to check at most 256 signatures, as that is its limit of unsigned 8-bit multiplex numbers. But in the common case, most of those multiplex numbers' installation check calls will return al != 0FFh so you will usually scan much fewer signatures than that. It is certainly going to be faster, and also more reliable, than your current check.

>>> Looking at the different interrupts, I think I have come up with a solution that will work well for Logger and any other driver or program that needs such a check. So, I’d like to propose a “standard” we could use. I’d like to get your feedback on what I’m thinking…
>>
>> setting a "standard" which is used probably exactly once ? we write year 2023 ;)
>>
>> https://xkcd.com/927/
>>
>> INT 2D has been mentioned by others
>
> Maybe go for 16 instead. It is an even binary number. LOL
>
> XKCD is great.

I would also suggest, if AMIS doesn't do what you want it to do, you have plenty of ways to strip off parts or add functions. The latter either as functions specific to your multiplexer on the interrupt 2Dh interface with private function numbers in al >= 10h, or on the private callback returned by AMIS function 01h.

Or, you could propose extensions to the specification with new return codes and/or new common AMIS function codes (al < 10h).

>>> Looking at RBIL, interrupt 0x2b is barely used by anything. Under MS-DOS and FreeDOS, this simply points to an IRET. Under IBM ROM-DOS, AH functions 0x00-0x03 do some things. But, all other calls do nothing.
>>
>>> https://fd.lod.bz/rbil/interrup/dos_kernel/2b.html <https://fd.lod.bz/rbil/interrup/dos_kernel/2b.html>
>>
>>> An install check issuing this interrupt would be simple to perform. A program could set the Carry Flag, load AH/AX with “check for install” function and set DS:BX to point to an identifier string (minimum 8 characters, no maximum). Then call the interrupt. On return, if the Carry Flag is still set, then the install check failed. If the Carry Flag is clear, it succeeded and other register would be modified to according to the needs of the programs.
>>
>>> Implementation for Logger (as an example) could be:
>>
>>>
>>> CHECK:
>>>         push cs
>>>         pop  ds                         ; set DS to our code segment
>>>         stc                             ; set the carry flag
>>> mov ax, 0xfd00 ; set AX to install check function >>> mov bx, LOGGER_ID ; offset to LOGGER device driver ID
>>>         int  0x2b                       ; call install check interrupt
>>> jc NOT_INSTALLED ; nothing changed, driver is not loaded
>>
>>                                            ;you should add
>>           cmp ax, MAGIC_VALUE
>>           jne   NOT_INSTALLED
>>
>> your proposed INT2B might be used by some other software that for some crazy reasons
>> clears the carry flag.
>
> Your correct of course. :-)
>
> In implementation, I would probably have it return the pointer to the driver header and verify the signature of the driver again by the caller.

Then it buys precisely nothing over using an AMIS-compliant installation check.

>> anyway, INT2D is probably the better choice anyway.
>
> Probably, I just don’t like to add extra code and increase the resident footprint without good cause.
>
> :-)

More on that later.


On at 2023-04-21 11:21 -0400, jer...@shidel.net wrote:
> Hi Eric,
>
>> On Apr 21, 2023, at 10:27 AM, Eric Auer <e.a...@jpberlin.de> wrote:
>>
>>
>> Hi Jerome,
>>
>> if you are worried about collisions, use AMIS int 2d.
>> Overhead is low and it is less crowded than MUX int 2f.
>>
>> RBIL was famous enough to make AMIS more widespread. FreeDOS is too
>> niche to re-invent that wheel and declare int 2b to be a new trend.

I agree with Eric.

>> Why would your RESIDENT driver have to do install checks?
>> You can do those in the TRANSIENT part and just store the
>> results as static data in the resident driver part.
>
> There driver does do an install check at start up to prevent multiple instances. > However, all of that code is discarded after initialization. But, you still need to > “store the results” in the resident portion and respond to the interrupt calls
> properly.
>
> There is probably little difference in the resident portion between using 2D or > 2B. Only, writing both versions could say for certain which is larger and by
> how much. But, RBIL says 64-bytes + 22 per hooked interrupt. A simple ID
> string comparison and set return values won't use much.
>
> But we are taking about bytes and the savings may not be worth it.

The numbers from the interrupt list are about a minimum fully compliant multiplexer. You don't have to follow all of that.

Regardless, the "22 bytes per hooked interrupt" is mainly due to needing a 18-byte structure known as the IBM Interrupt Sharing Protocol (IISP) header for each hooked interrupt. Besides AMIS, I also want to advertise the use of the IISP. You can use it regardless of whether your multiplexer uses AMIS or not. And there's two sides to using it, you can benefit from other programs providing these headers to you, as well as you can offer them from your resident program.

If you want to learn what it is and what it is for, I can offer my KEEPHOOK manual [7] as well as Chris Dunford's document on the protocol, INTSHARE.DOC (an ASCII plain text file), which I have mirrored on our server [8].


[7]: https://pushbx.org/ecm/doc/tsr/keephook.htm
[8]: https://pushbx.org/ecm/test/20211110/intshare/INTSHARE.DOC


>> So the risk of wasting RAM by using AMIS are minimal :-)
>>
>> I agree that MUX can get crowded. However, for disk I/O,
>> the actual I/O still is the main bottleneck (unless you
>> are a large cache with a very inefficient algorithm) and
>> you would not do install checks for each invocation anyway.
>>
>> One example of slow call chains which you can feel may
>> be old ANSI variants and slow system and VGA BIOS. But
>> that was long ago and even those were not THAT slow.
>>
>> AMIS can be expected to be much faster than MUX and it
>> will not be in the way for your CDEX performance either.
>>
>> If you want to avoid the hassle of scanning for a free
>> magic number, I would just hardcode one. Collision risk
>> will still be lower than when grabbing the whole int 2b.

I disagree, one of AMIS's greatest strength is the flexibility and common interface to use. Scanning for a free multiplex number is a transient operation for most any AMIS multiplexer. So I would not recommend "hardcoding" an AMIS multiplex number.

>> I agree with Tom: Use int 2d, it is the better choice and
>> 2023 is not the year for MORE standards for simple things.
>
> The only advantages I see in using 2B instead are as follows, it is
> very light weight, it uses very little memory to implement, it has
> almost no chance of conflicts and only being used for an install check
> there is no impact on performance of any aspect of the system.
>
> Unless I just stay with the brute force search, 2D is probably the
> better choice. There are even suggestions that memory managers
> can query it to retrieve the driver name. Although, I don’t know
> if the FreeDOS mem command makes use of that or not.

MEM does not, but tools like our AMITSRS [9] can. My debugger also can scan the AMIS interrupt lists in its "DIL" command [10]. And of course the advanced deinstallation method described in the KEEPHOOK manual can benefit from using AMIS multiplexers and/or IISP headers.


Something not yet mentioned is that you can make use of AMIS, to some degree, to implement uninstallation of your multiplexer as well. Some notes on that:

* If you return codes 03h or 04h or 06h or 07h from the AMIS uninstall function, you should have already deallocated resources like your XMS memory and additional DOS memory blocks. If you do not do that, then the REMOVE tool may attempt to uninstall you by unhooking all interrupts, unlinking device headers, and freeing your main memory block. This is an area of AMIS that could still see improvements.
* You can again make use of other programs' AMIS and IISP use.
* It is possible to uninstall a device driver installed from (FD)CONFIG.SYS, for an example refer to the description of the QC command in lDebug's manual section on "Invoking the debugger as a device driver" [11] or the source that implements the QC command [12] [13].


Furthermore, I would also suggest to merge the two executables into one dual-mode program. You can do either a .COM style flat-format executable that doubles as a flat-format device driver, like so in DRVEXCH [14]:

  section .text

  org 0  ; it is a device driver

; device driver header
devdrvrhdr
  jmp short runcom      ; will be patched to -1 later
  dw -1                 ; pointer to next device driver header
dw 1000000000000000b ; device driver attributes (character device driver)
  dw stratfunc          ; offset to device driver strategy routine
  dw intfunc            ; offset to device driver interrupt routine
  db "DRVEXCH$"         ; device driver name


... Or, you can make an MZ .EXE style segmented executable that also can be loaded as a device driver (like the original Microsoft EMM386.EXE). You can implement this in the same way as the dual-mode flat-format file (a jump in the device header that is overwritten with -1 by the device init function), and/or just set up a different entrypoint for application mode in your MZ header.


[9]: https://pushbx.org/ecm/web/#projects-amitsrs
[10]: https://pushbx.org/ecm/doc/ldebug.htm#cmddi
[11]: https://pushbx.org/ecm/doc/ldebug.htm#invoking-device
[12]: https://hg.pushbx.org/ecm/ldebug/file/ae5ecb16ae6d/source/debug.asm#l4580 [13]: https://hg.pushbx.org/ecm/ldebug/file/ae5ecb16ae6d/source/debug.asm#l5175
[14]: https://www.bttr-software.de/products/drvexch/


Regards,
ecm


_______________________________________________
Freedos-devel mailing list
Freedos-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/freedos-devel

Reply via email to