While doing some research on DOS device drivers, I took a peek at the console drivers DISPLAY.SYS and ANSI.SYS that are part of the DOS 6.20 installation. Both have "Microsoft" stamped on, and so I'm not surprised to see that one copied some code from the other.
I could tell because they copied the errors as well:
In their search for a previous
CON:
driver, both DISPLAY.SYS and ANSI.SYS think that the driver chain ends with a full doubleword of -1. This is not true. On DOS 6.20, and even on DOS 2.11, the COM4 driver ends the chain with a Next Driver Pointer of 0070:FFFF. Luckily, this error will never show because the search is bound to be a successful one. There will always be the defaultCON:
driver present.In their search for a previous
CON:
driver, both DISPLAY.SYS and ANSI.SYS also don't properly check the 8th character of the device name.push di ; CX=8 push si lea di, [di+10] lea si, [si+10] repe cmpsb pop si pop di and cx, cx <<<<<< Anything goes because of this! jne <NotFound> <Found>
Both DISPLAY.SYS and ANSI.SYS have support for 20 driver functions, but ANSI.SYS erroneously can dispatch to a non-existing 21st function.
cmp al, 20 ja <HasError> <IsFine>
What amazed me is the way that these codes deal with the loading and storing of segment registers and the stack pointer. Often (but not always) they use an intermediate general purpose register. See next 4 snippets:
mov ax, [es:di+6] mov [cs:bx+4], ax mov ax, es <<<<<< mov [cs:bx+6], ax <<<<<< mov [cs:bx+6], es mov ax, [es:di+8] mov [cs:bx+8], ax mov ax, es <<<<<< mov [cs:bx+10], ax <<<<<< mov [cs:bx+10], es
mov di, [cs:0BABh] <<<<<< mov es, di <<<<<< mov es, [cs:0BABh] mov di, [cs:0BADh]
cli mov si, sp <<<<<< mov [cs:04D4h], si <<<<<< mov [cs:04D4h], sp mov si, ss <<<<<< mov [cs:04D2h], si <<<<<< mov [cs:04D2h], ss mov ax, 04D0h <<<<<< mov si, cs mov ss, si mov sp, ax <<<<<< mov sp, 04D0h sti
cli mov ax, [cs:04D4h] <<<<<< mov si, [cs:04D2h] <<<<<< mov ss, si <<<<<< mov ss, [cs:04D2h] mov sp, ax <<<<<< mov sp, [cs:04D4h] sti
I have checked the surrounding code for any dependencies on the values in the general purpose registers that were used, and found none.
So my question is: Why did the programmer(s) not write the shortest code possible in an OS that is confined to conventional memory? The 8086 has always allowed the shorter and faster instructions that I have added on the right hand side of the above snippets.
Somewhat related is their frequent use of the LEA <reg>, <var>
instruction instead of the one byte shorter MOV <reg>, OFFSET <var>
when it comes to loading an address.
This is definitely a "MASM thingy". I have seen this many times before in MASM programs. It would seem that having to insert OFFSET
is a bit demanding of our natural born laziness.
The question that remains is: why using intermediate general purpose registers when loading or storing segment registers and the stack pointer?