On Tue, 12 Jul 2022, C. Masloch wrote:

(Only replied-to portions copied)

Further, you calculate the size of the PSP block to keep resident by using test, add, and shifts:

         mov       dx, trans           ; Allow everything preceding the
         test      dx, 0x000F          ;   transient portion of GRAFTABL to
         jz        .20                 ;   remain resident. (Rounded up to
add dx, 0x10 ; the next paragraph, because MS-DOS
.20:      mov       cl, 4               ;   wants the size in paragraphs.)
         shr       dx, cl
         mov       ax, 0x3100
         int       0x21                ; TSR EXIT CODE 0

It is more efficient to change this like so:

       mov dx, trans
       add dx, 15
       mov cl, 4
       shr dx, cl

The "add dx, 15" makes the shr round up in its division. (I expect that the addition won't overflow here.) However, you can easily change it so that the addition is done by the assembler at build time, using "mov dx, trans + 15".

Prolly the 65C02 programmer in me. xD

Another problem (which is also little known) is that your use of interrupt 21h service 31h will retain all of your currently open process handles, as well as the entire system's System File Table (SFT) entries associated with these. This is not a problem by default because all your handles will be DUPlicated from the parent's, for your stdin, stdout, stderr, stdaux, and stdprn. That means they will share the same SFT entries as already used by the shell. However, if the user runs your program with output redirection (either to a file or a character device such as NUL), as in "graftabl > nul", then you will leak the SFT entry which was reserved for your process to use.

I assume that the people involved in the design of this DOS service expected that TSRs would generally want to keep around their PSPs, so that they could swap processes and then use their own handles as preserved in their process handle tables. However, in practice most TSRs never re-use their PSPs after the DOS TSR termination handling is done. So in that case, as it is for your application, you should explicitly free all handles before terminating.

Here's how I solved it [7] in FDAPM (and with equivalent code in FreeDOS SHARE):

        xor bx, bx              ; = 0
        mov cx, word [32h]      ; get amount of handles
.loop:
        mov ah, 3Eh
        int 21h                 ; close it
        inc bx                  ; next handle
        loop .loop              ; loop for all process handles -->

Hm. Probably a good point.

In your executable entrypoint you have a near jump to skip the buffer later used for the table data:

entry:    jmp       trans
         db        1021 dup 0x00

It is implied by the size of the buffer that the jump must be near, so it takes 3 bytes, and then you add another 1021 bytes to get a total of 1024 bytes. (I think the "dup" syntax is only supported by recent versions of NASM, but that's not important.) However, I'd prefer to use some calculation to get NASM to reliably fill the buffer, such as:

entry:  jmp trans
       times 1024 - ($ - entry) db 0


Alternatively, you could use my fill macro [10] like this:

entry:  fill 1024, 0, jmp trans


(Also, as I think you already suggested in this thread, for optimising the transient executable size you could put one of the tables into this buffer to save 1 KiB at the end of the executable. (Just the jump needs to stay, you could re-initialise it to hold the correct 3byte for the table start later.) Or stash some of the messages in there, as long as they're shorter than the 1 KiB size.)

(I've more recently done this with the CP437 table, because it's the default.)

You do not use an IBM Interrupt Sharing Protocol (IISP) [11] header for your interrupt hook. Therefore, you could optimise this part a bit, from:

old2F:    dd        0xFFFF0000
...
.1:       jmp far   [cs:old2F]


Into this:

.1:     jmp 0:0
old2F equ $ - 4


This is some self-modifying code (SMC) to stash the downlink into an immediate far jump instruction, instead of using the indirect far jump to refer to a different memory location in your code segment. The dollar sign is used in the equate to denote the current assembly position after the 5-byte instruction; it is offset by minus four so as to address the far pointer in the instruction's encoding. (As mentioned, you cannot do this if you use a standard IISP header, because that has a "jmp short $+18" (EBh 10h) instruction right in front of the downlink field.)

I believe it was from analyzing some MS-DOS 2.0 code that I got the technique I used here.

Next, you're using "or al, al" to check a register for zero. However, it is more idiomatic [12] to use "test al, al" instead, which right away hints (to a reader or even to the processor) that no change of the register occurs.

Also not sure where I got the "or" idiom, but I know that it doesn't alter the register data.

Additionally, you're using two instructions of the test or compare types to dispatch down three different paths. (These are handling the function 00h call, or the function 01h call, or anything else.) One comparison instruction suffices to do that however. Observe:

new2F:
...
       cmp al, 1
       ja .chain
       je .function_01
.function_00:

Hm, that's true.  Hadn't thought of that.

Other than your earlier use of "dup" syntax, you are also using "mov word ds:[bx], entry". The segment override outside the brackets is another MASMism that NASM has recently learned to support. I prefer the segment override within the brackets however.

It makes more sense with [ds:bx], yeah.

(Regarding a here-deleted convo regarding EMS and XMS memory)

I've been thinking about how to implement other DOS commands - I'm basically trying to create a compact reimplementation of the MS-DOS 6.21 userland, more or less, and I know I'm in way over my head - and trying to figure out XMS and EMS has been something I've been wracking my brain over for an idea to implement XCOPY, by creating a series of "packets" with commands like "create directory", "open file", "write to file", etc., and store as many of these "packets" in RAM, including XMS and EMS, as possible before dumping the data out to disk.

Also, I've been hacking on MS-DOS 2 DEBUG trying to figure out how to plug in MS-DOS 4 functions (EMS support and read/write on BIGFAT devices) without success, but that's another matter ;p

-uso.


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

Reply via email to