Hi,

> On Apr 21, 2023, at 12:43 PM, C. Masloch <pus...@ulukai.org> wrote:
> 
> 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.

Yeah, that is just temporary function to locate the driver. If it was going to 
stick with a brute force search, it could use optimized more and other fixes.

But it would be better to just rip that section out for a better install check 
procedure. Like the code comment says, “Not perfect or fast. But, good enough 
for now.”

I was starting to improve it after getting your reply. But, it is better to 
just replace it now.

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

You make a lot of good points. :-)

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

It could preserve the flags, then test against the “install check function” 
(like AX=0xfd00). If not equal, restore flags and jmp to previous handler. If 
equal, push es, di, bx, and ax. Load registers and string compare. If not 
match, restore registers and jump to previous handler. If it does match, it can 
return a pointer. 

It most likely would require a little more code, but possible less data for the 
structures. I think it would require writing both versions to see which uses a 
less bytes in total. 

> 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?

You would only modify the registers required to test for the signature. If you 
don’t match, you don’t modify anything. If you do match, than you could modify 
whatever. This really is not very different than a combined version of the 
Install Check (0x4300) + Get Driver Address (0x4310) that are used for XMS on 
Interrupt 0x2F. 

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

Overall, using AMIS is probably the best solution.

:-)

Jerome



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

Reply via email to