First steps towards system programming under MS-DOS 7/Examples of executable files composition

Chapter 9 Examples of executable files' composition

All examples of interpretable, executable and configuration files, presented here in the 9th chapter, have been successfully tested on several different computers with processors from 486SL (dated 1992) to Pentium-D (dated 2006). However, one can't foresee everything in advance. In any case the presented examples should be regarded as advisable schemes, where anyone is allowed to select personally relevant solutions. Their applicability is left for you to decide and to implement.

Though personal problems may various, some elementary operational habits are common for all who dare to descend deeper into low-level programming. First of all, how a program's text, printed in a book, can be transferred into an executable file. For this purpose you have to launch an editor program, capable to save non-formatted text files. Most widely known WORD and WORDPAD programs wouldn't fit, but NOTEPAD and EDIT.COM are quite suitable. However, in MS-DOS any national (non-American) language commentaries can be inserted by EDIT.COM editor only (6.09), and only if national adaptation drivers are loaded beforehand (examples in articles 9.01 and 9.04).

When editor program is launched yet, then item NEW in menu FILE enables to open a new empty window, where you may write any desired text. Texts of executable files, presented in this book, normally should be typed line-by-line starting close to the left border of each line. Then text should be saved in a file by SAVE AS command in menu FILE. The SAVE AS command suggests to specify a name and a suffix for new file. Each file should be given just that suffix (2.01-02), which reflects file's status: BAT – for batchfiles, SCR – for command files, executed by DEBUG.EXE, SYS, MNU or EXT suffixes – for various configuration files.

Long files may be saved several times: first time by SAVE AS command, and later by SAVE command. If text is to be corrected on-the-fly, then parts of an unfinished file may be tested separately, as it is shown in article 9.07-02. Editor program fulfills its mission, when a textual file with specified name is completely typed, checked and saved.

If not specified otherwise, successful execution or interpretation of all program examples, presented in this chapter, implies that the following normal conditions are met :

  • name of current command interpreter (COMMAND.COM or some other) with preceding full path must be defined by a value of environmental variable %COMSPEC%;
  • attributes H (hidden) and S (system) shouldn't be assigned to those files or utilities, which are to be called for or referred to by the program under test (note 1 to 9.11-02);
  • paths to all those files or utilities, which are to be called for or referred to by the program under test, must be specified in a value of environmental variable PATH (2.02-02);
  • synonymous utilities, unsuitable under MS-DOS 7, must not be present in the current directory and along all the paths, specified in a value of environmental variable PATH (2.02-02);
  • value of the TEMP environmental variable must specify a path to an existing directory, dedicated for temporary files, on a writable media, having enough free space for this purpose.

Note that the status of the %TEMP% directory implies that any file in this directory may be deleted or overwritten. Current values of environment variables may be checked with SET command (3.26). Original values of environment variables as well as other execution conditions should be defined in configuration files. Various examples of such definitions are shown in articles 9.04, 9.09. But it's expedient to start from the simplest configuration files, presented in the next article 9.01.

9.01 Simple configuration files edit

MS-DOS 7 loading process is configurable and may lead to different results. The IO.SYS loader accepts loading parameters from MSDOS.SYS file (5.01-01) and takes into account presence of main DOS files in the root directory of boot disk. Complete list of DOS files, which should be present in root directory, is described in article 9.11-02. Among these files are two optional, but very important files : CONFIG.SYS and AUTOEXEC.BAT. Just these two files define main features of final DOS configuration. Though MS-DOS 7 is able to load itself without these two files, but implicit default configuration is too poor and cannot be considered sufficient.

MS-DOS 7 can be made effective, convenient and friendly, but the user has to bother about that. In modern computers even simple configurations must include drivers for extended memory, for "mouse" pointing device and for CD-ROM drive. DOS can't be convenient without adequate file manager. Examples of configuration files, presented in this book, also load codepages, enabling to use both American and some national (non-American) language.

Simple versions of configuration files may be used for loading MS-DOS 7 from removable media, but are most suitable for MS-DOS 7 installation on HDDs: either for temporarily installation after formatting or for permanent installation as a possible choice among several alternative operating systems (example in 9.11-02).

9.01-01 Simple version of CONFIG.SYS file edit

CONFIG.SYS file is an interpretable command file: each its line is a command. Term "interpretable" implies that commands are executed not directly by CPU, but via a mediator – a command interpreter program. Mission of CONFIG.SYS commands interpretation is incumbent on IO.SYS loader.

The presented example of CONFIG.SYS file shows explicitly all the drivers, which should be loaded, and preferable order of their loading. Drivers are expected to be found in \DOS\DRV directory. If you intend to use other directory structure, path specifications must be corrected accordingly. Note that the paths are shown without disk's letter-name. Such specifications will suit for loading from any bootable disk.

device=\DOS\DRV\Himem.sys
device=\DOS\DRV\Emm386.exe ram v
dos=high,umb,noauto
buffershigh=20,0
fileshigh=30
lastdrivehigh=Z
fcbshigh=1,0
stackshigh=9,256
numlock off
country=007,866,\DOS\DRV\Country.sys
devicehigh=\DOS\DRV\Dblbuff.sys
devicehigh=\DOS\DRV\Ifshlp.sys
devicehigh=\DOS\DRV\Setver.exe
devicehigh=\DOS\DRV\Display.sys con=(ega,,1)
devicehigh=\DOS\DRV\Oakcdrom.sys /D:CD001
installhigh=\DOS\DRV\Mscdex.exe /D:CD001 /E /L:O /M:13
installhigh=\DOS\DRV\Mouse.com
shell=\Command.com \ /E:2016 /L:511 /U:255 /p

A help concerning all specified commands and drivers can be found in chapters 4 ("Configuration commands") and 5 ("Selected drivers"). Most drivers can be taken from \WINDOWS and \WINDOWS\COMMAND directories of WINDOWS-95/98 operating system, except MOUSE.COM (5.03-02) from MS-DOS6.22 release and OAKCDROM.SYS (5.09-01), which is copied from WINDOWS-95/98 rescue diskette. There is a lot of other mouse and CD-ROM drivers (GMOUSE.COM, VIDE-CDD.SYS, ECSCDIDE.SYS, etc.), which can work with a variety of device models and can be used here instead of MOUSE.COM and OAKCDROM.SYS.

The order of lines in CONFIG.SYS file must conform to the following general rule: the drivers providing some support must be loaded before a need arises for this support. Upper memory drivers (HIMEM.SYS and then EMM386.EXE) must be loaded with DEVICE commands before upper memory access will be required for DEVICEHIGH and INSTALLHIGH commands. INSTALL and INSTALLHIGH commands, used to load executable drivers, must be placed after all DEVICE and DEVICEHIGH commands, but before the SHELL command. SETVER.EXE driver must be loaded before any other driver, which needs to be deceived about DOS version number. Though there are no such drivers in the shown example of CONFIG.SYS file, loading of SETVER.EXE gives an opportunity to execute later version-specific utilities from earlier versions of DOS (PRINT.EXE, QBASIC.EXE, TREE.COM, etc.).

Special attention should be paid to the NOAUTO parameter of the DOS command in the 3rd line: it cancels default loading of some drivers (DBLBUFF.SYS, DRVSPACE.BIN, HIMEM.SYS, IFSHLP.SYS) as well as a search for these drivers along default paths. In fact the NOAUTO parameter enables to use MS-DOS 7 as a standalone operating system.

Note that a path to COMMAND.COM file in the last line of CONFIG.SYS file is reduced to a single backslash " \ ". This is enough for finding COMMAND.COM file in the root directory, but is not enough for proper definition of path to COMMAND.COM file in the value of COMSPEC environmental variable. Of course, a particular disk's lettername may be specified inside CONFIG.SYS file. Such examples are shown in articles 4.26 and 6.04. However, in practice it's not convenient to exchange disk's letter-name in several places each time you have to boot your PC from some other disk. Therefore, here definition of a particular disk's letter-name is postponed until execution of the last configuration file AUTOEXEC.BAT (9.01-02). Postponed disk's letter-name assignment enables to correct letter-name in a single place and provides opportunity for automatic disk's letter-name determination (examples in 9.01-03 and 9.09-02).

9.01-02 Simple version of AUTOEXEC.BAT file edit

As far as functional capabilities of IO.SYS loader are limited, some configuration operations can't be expressed as commands in CONFIG.SYS file. Such operations constitute another configuration file – AUTOEXEC.BAT. It is also an interpretable command file, but its mission is to employ capabilities of a more powerful command interpreter – COMMAND.COM – for performing a number of final configuration operations.

Presented version of AUTOEXEC.BAT file implies presence of directory structure on the bootable disk: directory \TEMP for temporary files and directory \DOS with subdirectories \DOS\OTH, \DOS\MS7, \DOS\VC4, \DOS\DRV. This structure can suit for both diskettes and fixed disks, but if you intend to use any other structure, all paths and references must be changed accordingly. The file is devised for booting from disk C:. If it is to be used for loading from any other disk, reference to disk C: in the second line must be replaced with reference to that disk, which actually will be used. Note that it is a single reference, which must be corrected; all other references to actual disk will be exchanged automatically.

Here is the proposed simple version of AUTOEXEC.BAT :

@echo off
set dsk=C:
set comspec=%dsk%\Command.com
if not exist TEMP\nul %comspec% nul /f /c md TEMP
if exist TEMP\nul set Temp=%dsk%\TEMP
prompt $p$g
set dircmd= /A /O:GNE /P
path ;
path=%dsk%\DOS\VC4;%dsk%\DOS\OTH;%dsk%\DOS\MS7;%dsk%\
Mode.com con codepage prepare=((866) %dsk%\DOS\DRV\Ega3.cpi)
Mode.com con codepage select=866
Keyb.com ru,866,%dsk%\DOS\DRV\Keybrd3.sys
set VC=%dsk%\DOS\VC4
Vc.com /TSR /no2E /noswap

Command in the first line of the shown file turns echo flag off, just as it is done in ordinary batch files. The second line specifies letter-name of the current disk and assigns it as a value to environmental variable DSK. Note that there must be no spare spaces at the end of second line. Multiple references to DSK variable in the following lines insert lettername of the current disk into all relevant paths. This enables to specify disk's letter-name only once. The third line assigns correct value to COMSPEC environmental variable, which hasn't got proper value during interpretation of CONFIG.SYS file. Since this moment MS-DOS 7 is ready to a change of the current disk.

Lines 4 and 5 deal with directory TEMP for temporary files. First, existence of this directory is checked. If it doesn't exist, an attempt is made to create TEMP directory. Then its existence is checked once more, and in case of success a path to TEMP directory is assigned as a value to environmental variable TEMP. This procedure guarantees a valid path for temporary files on any writable disk. On the other hand, absence of TEMP variable after this procedure certainly indicates that current disk is non-writable.

The following group of operations assigns values for other variables: DIRCMD, PROMPT, PATH. The shown values should be regarded as examples, which need to be corrected according to actual directory structure on your disk. It is implied that value of the PATH variable (2.02-02) contains actual paths to all utilities, specified in the following lines : MODE.COM, KEYB.COM and VC.EXE, as well as other paths which you may want to supplement.

Execution of MODE.COM and of KEYB.COM activates desirable national codepage for display and corresponding keyboard's layout. If Russian codepage 866 is not the one you need, you may change it, but beforehand it should be checked in table A.02-2 whether the associated data tables (EGA3.CPI and KEYBRD3.SYS) contain the data you need. If not, these data tables must be changed too. Of course, the choice of 437th (American) codepage makes you free to omit all lines with MODE.COM and KEYB.SYS, because this codepage is activated by default.

The last two lines in AUTOEXEC.BAT file serve to launch VC.COM – the Volkov Commander file manager (6.25). Other file managers, for example, Norton Commander (NC.EXE) and Dos Navigator (DN.EXE) may be launched in a similar way. If you don't intend to use file manager, then the shown two last lines are not needed.

9.01-03 Automatic determination of disk's letter-name edit

It were convenient to have a set of self-adaptive configuration files, which can load MS-DOS 7 properly from any disk without manual correction of disk's letter-name. This opportunity can be easily implemented, if you have assembled yet the REASSIGN.COM utility, proposed in part 9.06, or have got any other functionally equivalent utility. All you need is to replace fixed assignment ("set dsk=C:") in the 2nd line of AUTOEXEC.BAT file (9.01-02) with the following two lines, performing automatic determination of disk's letter-name :

set dsk=33
\DOS\OTH\Reassign.com dsk

Of course, the path example ("\DOS\OTH\") must be changed, if necessary, according to the directory, where the utility actually can be found. After execution of these commands the letter-name of current disk becomes a value of DSK variable, and then all other relevant specifications are corrected automatically, as it is shown in article 9.01-02. Automatic disk's letter-name determination doesn't necessarily need a special utility; it may be achieved exclusively by standard MS-DOS's means. But implementation of this idea is not so simple and, besides that, requiring access to a writable disk. Therefore, presented here version of AUTOEXEC.BAT file wouldn't suit for loading MS-DOS 7 from a CD-ROM, but it can be used and actually has been used for MS-DOS 7 single-fold relocation onto a hard disk after formatting.

@echo off
if exist ..\nul goto L19
prompt=@echo off$_Set dsk$q$N:$_goto L7
%comspec% /f /c $.bat > $.bat
type Autoexec.bat >> $.bat
for %%Z in ("del A" "ren $.bat A" "A") do %%Zutoexec.bat
:L7
set comspec=%dsk%\Command.com
set Temp=%dsk%\TEMP
if not exist %Temp%\nul md %Temp%
prompt $p$g
set dircmd= /A /O:GNE /P
path ;
path=%dsk%\DOS\VC4;%dsk%\DOS\OTH;%dsk%\DOS\MS7;%dsk%\
Mode.com con codepage prepare=((866) %dsk%\DOS\DRV\Ega3.cpi)
Mode.com con codepage select=866
Keyb.com ru,866,%dsk%\DOS\DRV\Keybrd3.sys
set VC=%dsk%\DOS\VC4
Vc.com /TSR /no2E /noswap
:L19

Lines from 8 to 18 in this version of AUTOEXEC.BAT file perform ordinary functions, described in part 9.01-02. But lines 2–6 and labels are specific for this version. In order to make lines search easier the digits in label names represent ordinal numbers of corresponding lines.

The 2nd line presents a check whether a parent directory exists for the current one. If it exists, the current directory can't be disk's root directory, and then we exit via L19 label. Hence, this file will do nothing, while it is stored anywhere inside directory structure. It becomes functional after it is moved into the root directory of a disk: then the parent directory doesn't exist, and the check in line 2 lets to proceed to the next line.

In the following line 3 the PROMPT command (3.22) sets a new prompt, which is issued only once while launching the command interpreter in the 4th line. There command interpreter formally executes an empty file $.BAT, created just before by DOS while preparing redirection in the same line. The result is written into the same $.BAT file. As far as it was initially empty, it will be filled with nothing but new prompt. Let's assume that current disk is D: ; then contents of $.BAT file will look as follows :

@echo off
Set dsk=D:
goto L7

Note that letter-name D: in the second line of $.BAT hasn't been preset beforehand, it is the actual current disk's letter-name, returned by DOS as an element of command prompt.

The TYPE command in the following line 5 reads AUTOEXEC.BAT file and sends its copy via output redirection so that it is appended to the shown three original lines in $.BAT file.

Cycle FOR in the 6th line of AUTOEXEC.BAT performs three operations by sequential substitutions of three different values for dummy parameter %%Z. First substitution gives del AUTOEXEC.BAT and AUTOEXEC.BAT file ceases to exist. Then the second substitution produces command ren $.BAT AUTOEXEC.BAT and former $.BAT file becomes renamed into AUTOEXEC.BAT. The third substitution gives AUTOEXEC.BAT, that is, the command to execute the new file AUTOEXEC.BAT beginning from its first line.

Since the name of this new file isn't preceded by the CALL command (3.02), there will be no return to the executed former batch file, which currently doesn't exist yet. Note that replacement of currently executed file can't be performed unless all replacement operations are written in one line.

The new AUTOEXEC.BAT file begins with those three lines of former $.BAT file, which have been shown above. In course of their execution the letter-name of current disk is assigned as a value to DSK environmental variable and an unconditional jump is performed to label L7. Thus a group of lines, containing operations of self-modification, is bypassed for ever more. Starting from label L7 ordinary operations of AUTOEXEC.BAT file will be performed. During their execution the found letter-name of current disk will be automatically inserted into all paths, where it must be specified.

9.02 Command files, interpreted by debugger DEBUG.EXE edit

9.02-01 Boot sector saving and restoration edit

In every logical disk the first sector is boot sector. It contains parameter block BPB (A.03-4), an executable code part and specifications of those files, which should be given control for loading operating system. Boot sector is written by FORMAT.COM utility (6.15) each time the disk is formatted, and may be rewritten by SYS.COM utility (6.24), when disk is made bootable. Several special programs, such as DDO (Dynamic Drive Overlay), use non-standard boot sector configurations, which can't be restored by utilities, supplied with MS-DOS 7. You may need to save boot sector into a file in order to restore it later after occasional data distortion, or virus infection, or overwriting in course of operating system installation (examples in article 9.11-02). Though main command interpreter – COMMAND.COM – doesn't provide access to boot sector, for debugger utility DEBUG.EXE the boot sector saving is an easy task, solved exclusively with debugger's internal commands. Here is an example of command file, which induces DEBUG.EXE to do copy boot sector from disk C: into a file:

L CS:100 2 0 1
r BX
0000
r CX
200
n Bootsect.dat
w CS:100
q

The first line is a command to read boot sector from disk C: and to write its copy into memory starting from address CS:100 and on. Lines 2–5 specify length of data area (00000200h), which should be copied into a file. Line 6 specifies a name for the file to be created. Line 7 causes data copying from memory into a file. The last command ("Q") terminates debugger's session.

The text of the shown command file should be typed in editor program's window (NOTEPAD or EDIT.COM) and saved in current directory as a file with any suitable name (let it be named SAVEBOOT.SCR). Then this file should be sent to debugger by a command DEBUG.EXE < SAVEBOOT.SCR

The result of execution is a 512-byte file BOOTSECT.DAT, appearing in current directory. This file is an exact image of boot sector on disk C:. You may read boot sector from any other valid logical disk in a similar way (6.05-10). In order to access disk A:, for example, you have to replace the first line in SAVEBOOT.SCR with the following one :

L CS:100 0 0 1

Of course, you can't address to a drive for removable media unless it has a valid removable media inside.

The reverse operation of writing a boot sector back to disk is performed by an even simpler command file :

n Bootsect.dat
L CS:100
w CS:100 2 0 1
q

Here the first line announces a name of file, containing boot sector image; this file must be present in current directory. The second line reads sector data from this file into memory, and the third line writes the sector data from memory back onto its original disk (disk C: in this example). Text of the shown command file should be typed in editor program's window and then saved into a file with any suitable name, for example, RESTBOOT.SCR. As far as interpretation of RESTBOOT.SCR implies direct access to a disk, this must be done under DOS, but not inside the "DOS box" under WINDOWS OS. Restoration of boot sector will happen, when command file RESTBOOT.SCR will be sent to its interpreter via input redirection:

DEBUG.EXE < RESTBOOT.SCR

With described command files the boot sector restoration becomes an easy operation. Nevertheless, this operation shouldn't be used for boot sector transplantation. Even transplantation between diskettes of the same format raises a number of problems with uniqueness of their serial number and volume label, since the latter is duplicated in the root directory. Generally each boot sector is unique and can be suitable for no other disk except its original disk.

9.02-02 Copying Master Boot Record into a file edit

Each time you switch your computer on, it loads an installed operating system from its HDD (hard disk drive). This ordinary procedure includes several stages, and one important stage is execution of Master Boot Record (MBR), which specifies HDD's structure and directs loading process further. As any other record on a disk MBR is subjected to natural degradation, it may be damaged by an occasional fault or by virus. In any such case you will have to boot your computer from a recovery disc or from emergency diskette in order to restore MBR.

For Windows-95/98/ME operating systems you may try to restore MBR with FDISK.EXE utility (6.13), but it can't restore partition table, which is written in the same sector together with MBR (A.13-5). Specific forms of MBR, used by some other operating systems, by DDO (Dynamic Drive Overlay) and by boot managers, also can't be restored by FDISK.EXE.

Meanwhile, there is a simple and universal solution: to store a copy of MBR together with partition table in a file on a removable media (compact disk or diskette). Then you will be able to restore MBR and partition table whenever you need from this file.

MS-DOS 7 doesn't provide special means to copy MBR, but supplies debugger DEBUG.EXE, which enables you to write any succession of machine commands and can execute it at once. This article presents a command file with a succession of commands, inducing DEBUG.EXE to copy MBR into a file. Text of this command file looks as follows :

a 100
mov          DX,0080       ;prepare access to head 00h of HDD 80h
mov          CX,0001       ;specify start at cylinder 00h, sector 01h
mov          BX,0200       ;load BX with offset 0200h for data buffer
mov          AX,0201       ;specify function 02 of INT13, copy 1 sector
int          13            ;call INT13 handler, copy sector to buffer
mov          [01F6],AH     ;save exit code in memory cell 01F6h
ret                        ;quit execution of "G=100" instruction
org          130           ;write next commands from cell 130 and on
mov          AL,[01F6]     ;copy exit code into AL register
mov          AH,4C         ;specify termination function 4C (8.02-55)
int          21            ;call INT21 handler, terminate session

g =100
n Mbr.dat
r CX
0200
r BX
0000
w CS:0200
d 01F6,L1
g =130

Proposed commands should be typed in a window of editor program, as recommended in introduction article to chapter 9. Comments to the right of semicolons are not executed by DEBUG.EXE and hence may be omitted. Notice the empty line between "INT 21" and "{{{1}}}" commands: it is important (7.01-04) and must be present. Then text should be saved in a file, which may be named, for example, READ_MBR.SCR.

If your computer is configured to boot not from logical disk C:, but from any other logical disk (D:, E: or other), you have to check actual number of bootable physical HDD, for example, by command FDISK.EXE /status

From displayed table you will be able to determine the number of bootable physical HDD; if it is not number 1, then HDD's code 80h in second line of READ_MBR.SCR file must be corrected. If bootable drive is HDD number 2, then 81h HDD's code should be specified, if bootable drive is HDD number 3 — the 82h HDD's code, and so on.

Now it's time to explain how READ_MBR.SCR works. Its first line "A 100" switches DEBUG.EXE in assembler mode of operation for writing codes of machine commands into memory starting at address CS:0100h. While DEBUG.EXE stays in assembler mode, it allows to insert comments after semicolon sign, so the role of commands in lines 2–12 is evident from these comments. More detailed information about each command can be found in chapter 7. Two successions of machine codes are prepared: one starts from memory cell 100h, and the other, announced by ORG command in line 9, starts from memory cell 130h. Translation of commands into machine codes continues until empty line 13 is encountered : it forces DEBUG.EXE to leave assembler mode of operation. Commands in following lines are not added to prepared successions of machine codes, but rather are executed at once.

Command "G" in line 14 initiates execution of the first prepared machine codes succession from address CS:0100h. In the course of execution the MBR together with partition table are read from HDD and are written into memory from cell 0200h and on. Exit code of reading operation is temporary stored in arbitrary chosen free memory cell 01F6h. Execution of the first prepared machine codes succession is finished by RET command (7.03-73) in line 8, which returns control back to debugger DEBUG.EXE.

DEBUG.EXE continues its job from command "N" (6.05-12) in line 15. It prepares name (MBR.DAT) for the file, which is to be created. You may specify other name or add a preceding path, if file is to be created elsewhere beyond the current directory. Commands in lines 16–19 specify the length (00000200h) of the file to be created. The command "W" (6.05-19) in line 20 reads data from memory, starting at address CS:0200h, and writes these data into a file, which has its length and name (MBR.DAT) specified beforehand. Command "D" (6.05-04) in 21st line displays reading operation exit code, stored in memory cell 01F6h.

The last command "G =130" in line 22 initiates execution of the second prepared succession of machine codes. Exit code of reading operation is copied from memory cell 01F6h into AL register, and then DOS's INT 21\AH=4Ch function (8.02-55) terminates debugger's session. Simultaneously the exit code from AL register becomes the errorlevel value, left behind by terminated debugger's session, so that later it may be checked with conditional "if errorlevel" command (3.15-03).

Before command file READ_MBR.SCR will be interpreted, you have to assure yourself that it exists in the current directory on a writable media. As far as MBR will be saved in a file named MBR.DAT, any synonymous file should be removed from current directory, otherwise it will be overwritten without prompt. Having finished preparatory checks, you may send READ_MBR.SCR to debugger for interpretation :

DEBUG.EXE < READ_MBR.SCR

During interpretation a message "Program terminated normally" appears, but it means nothing more than DEBUG.EXE has successfully got control back after execution of the first succession of machine codes. Outcome of MBR reading attempt is represented by exit code – a hexadecimal number, displayed in penultimate row on the screen. Non-zero exit code informs about failure of MBR reading attempt. In this case a MBR.DAT file will be created containing nothing but garbage. On condition "if errorlevel 1" (3.15-03) it should be automatically deleted. Interpretation of non-zero exit codes according to table A.06-1 may help to reveal the cause of failure.

Exit code value 00h signifies success of MBR reading attempt. In this case file MBR.DAT contains a copy of MBR together with disk's partition table. An example of MBR.DAT file dump is shown in fig. 12 (in article A.13-5).

9.02-03 Restoration of Master Boot Record (MBR). edit

MBR failure is a relatively rare event, but no one has a guarantee to avoid it. Once something similar may happen to you. Then you have to test and exclude other suppositions: wrong BIOS settings, CMOS memory failure, disk's surface damage, boot sector overwriting, etc. Most convincing experiment is to save an image of current MBR into a file, as it is shown in preceding article 9.02-02, and to compare it with original MBR image, which you have saved beforehand in another file. Comparison may be performed by FC.EXE utility (6.12). If files differ, MBR must be rewritten or restored.

MS-DOS 7 doesn't provide special means for writing MBR image from a file onto a hard disk, but you may prepare a command file, which will force DEBUG.EXE to do this job. Let's assume that the command file is named WriteMBR.SCR, that the copy of the original MBR image is named MBR.DAT, and that both files exist in current directory. The contents of WriteMBR.SCR may look like this :

A 100
mov          DX,0080       ;prepare to access head 00h, hard disk 80h
mov          CX,0001       ;specify start at cylinder 00h, sector 01h
mov          BX,0200       ;load BX with offset 0200 of data buffer
mov          AX,0301       ;specify INT13 function 03h, write 1 sector
int          13            ;call INT13 handler, write data into sector
mov          [01F6],AH     ;save exit code in a memory cell
ret                        ;quit execution of DEBUG's "G" instruction

N MBR.DAT
L CS:0200
G =0100
d 01F6,L1
q

The "A 100" command in first line of this command file switches DEBUG.EXE into assembler mode, so that commands in following lines 2–8 are not executed at once, but rather are translated into machine codes written into memory cells from CS:0100h and on. While DEBUG.EXE stays in assembler mode, it allows to supply each line with commentaries. Therefore, mission of commands in lines 2–8 is clear from command file itself. Empty line 9 forces DEBUG.EXE to exit assembler mode. Then "N" command (6.05-12) in line 10 specifies name of the file to be loaded, and "L" command (6.05-10) in line 11 loads MBR image from MBR.DAT file into memory starting at address CS:0200h.

The "G" command (6.05-07) in line 12 initiates execution of prepared machine codes from address CS:0100h. Execution proceeds until in line 8 the RET command (7.03-73) is encountered, which returns control back to DEBUG.EXE. Execution of commands in WriteMBR.SCR file is then continued from line 13 with "D 01F6,L1" command, which displays exit code, left after execution of INT13\AH=03h writing function. The last line 14 with "Q" command terminates debugger's session.

Naturally, if your bootable disk is not the first physical HDD, you must change code of physical disk in the 2nd line of WriteMBR.SCR file just as it is described in preceding article 9.02-02. When you are sure that the code of physical disk is specified properly, that the MBR does need to be restored, that all the necessary files are ready and in the current directory, then you may send WriteMBR.SCR file for execution with command

DEBUG.EXE < WriteMBR.SCR

After execution a hexadecimal exit code will be displayed on the screen. Interpretation of any exit code can be found in table A.06-1. Exit code 00h signifies that the MBR has been restored successfully.

Note 1: experiments with WriteMBR.SCR in order to ensure yourself shouldn't be performed upon HDDs which are currently in use! This may lead to unrepairable data loss! You may subject to experiments only those HDDs (both new and not new), which contain nothing worth saving.

Note 2: WriteMBR.SCR file needs direct access to disk. Therefore, MBR restoration shouldn't be performed inside "DOS box" under WINDOWS OS, it should be performed under genuine MS-DOS 7.

9.03 Examples of batch files edit

COMMAND.COM interpreter accepts ordinary command files via redirection, just as DEBUG.EXE does (9.02). But batch files represent a special class of command files, recognized and accepted by COMMAND.COM just from command line, without redirection. Moreover, in batch files COMMAND.COM can execute several important commands (3.02, 3.14, 3.21, 3.27), which can't be executed in ordinary command files or from command line. Because of these reasons a variety of command files for COMMAND.COM interpreter eventually is confined to class of batch files.

The simplest batch file in this book is AUTOEXEC.BAT file, presented in article 9.01-02. The articles below present somewhat less simple examples of batch files. These examples demonstrate techniques of batch programming, which may be useful far beyond the purposes of the shown batch files.

9.03-01 Batch file ARC.BAT for archiving edit

Term archiving according to computer terminology implies compression of several files into a combined file-archive. This meaning survived from those times long ago, when computers were not reliable, and personnel had to save multiple files as a combined data stream written onto magnetic tape media. Since then most everything has evolved, but interest in archiving hasn't vanished. Archiving decreases loss of free disk's space in clusters, makes faster both copying and defragmentation. File's size compression is essential because of limited transmission speed in communication networks. Packing of multiple files into one archive is convenient and is widely used for delivering large program products.

Known archiving algorithms are numerous, but only two of them — ZIP and RAR — have got widespread practical appreciation. ZIP algorithm, developed by Phil Katz in early 1990s, doesn't provide utmost compression, but has two other advantages: speed and compatibility. Archives, packed by original ZIP algorithm, can be unpacked by a lot of other archiving programs. Programs for packing and unpacking ZIP archives can be downloaded, for example, from site http://comp.site3k.net/?/comp/pkzip.html .

The RAR algorithm, developed by Eugene Roshal in middle 1990s, provides better compression and is able to emend partially damaged archives having internal recovery record. But new versions of WINRAR program (for WINDOWS OS) create archives, which can't be unpacked by earlier versions of RAR archivers. This incompatibility may let you down against your addressees. Therefore, for forming RAR archives a non-newest, but sufficiently good free version 2.50 of RAR.EXE program (dated 1999) should be preferred. This version of RAR archiver can be downloaded from internet, for example, from site http://dosprogram.narod.ru/arc/index.html .

In order to make archive usage convenient, Volkov Commander file manager enables to enter inside archives with a mouse button click and to treat their contents almost as contents of ordinary directories (details in article 6.25-04). Archive creation from file manager's menu is also much more convenient, than from command line. But file manager can't prevent user's mistakes in data preparation for archiving; moreover, finding a cause of a failure sometimes becomes even more difficult. Therefore, an idea has emerged to write a command file, which will check the data transferred from Volkov Commander file manager to archiver program. This idea was implemented in a batch file, i.e. a command file for COMMAND.COM interpreter. Batch file ARC.BAT prevented archiver's failures, and error messages, sent by ARC.BAT to display, helped to elicit the cause of each error.

As years passed, number of checks in ARC.BAT file has grown up to seven. When a time has come to decide, which batch file should be presented as a useful and relatively simple example, then the ARC.BAT file was considered the most suitable.

Principle of ARC.BAT file is based on presumption that in file manager's active panel the user chooses with right mouse's button a group of files, which are to be included into a new archive, and then with the left mouse's button highlights a filename, which will be assigned to the new archive. An example of such selection of files is shown in fig.5 (in article 6.25-01). After that a mouse button's click on corresponding menu item is enough, and new archive is created yet. If only one file manager's panel is opened at that moment, then archive will be created in current directory. If both file manager's panels are opened, then destination directory will be that shown in opposite (inactive) panel.

In the course of described procedure the states of both panels and names of selected files are sent via the file manager's macrocommands, invoked by certain character combinations shown in article 6.25-02. These character combinations should be specified in a line of menu file VC.MNU (6.25-02) so that each data item, returned by macrocommand, becomes a value of a separate dummy parameter for ARC.BAT file. In particular, for creation of RAR archives the menu line invoking ARC.BAT may look like

@Arc.bat RAR !: !~\ !~ !~@ %: %~\

When this line in menu file is interpreted by Volkov Commander file manager, then each character combination, invoking a macrocommand, will be replaced by data item, returned by that macrocommand. Then command line with all performed replacements will be sent to command interpreter COMMAND.COM. The latter sets ordinal correspondence between data items in command line and dummy parameters of batch file ARC.BAT. Inside batch file the dummy parameters are denoted by ordinal numbers of corresponding data items in original command line — digits from 0 to 9, preceded by a percent sign (2.03-03). Lines of ARC.BAT file will be interpreted by COMMAND.COM, and then designators of dummy parameters will be replaced by corresponding data items. According to the shown order of data items the dummy parameters will get the following substitutions :

%0 – name of currently processed file (ARC.BAT)
%1 – RAR (or ZIP): requested archive type
%2 – letter-name of a disk, shown in active panel
%3 – path to a directory, opened in active panel
%4 – filename, highlighted by left mouse's button
%5 – file-list of files, selected by right mouse's button
%6 – letter-name of a disk, shown in inactive panel
%7 – path to a directory, opened in inactive panel

The data, received from file manager, will be complemented by other data, requested by ARC.BAT file from operating system. All these data will be taken into account in order to reveal mistakes, which may inhibit successful execution of archiving procedures. According to results of checks either an archiver program will be called for, or a comprehensive error message will be displayed. Full text of ARC.BAT file, comprising command lines with all checks, is shown below.

@echo off
set V1=02
if %1"==ZIP" set V1=Pkzip.exe
if %1"==RAR" set V1=Rar.exe
if %6"==" set V1=02
if %V1%==02 echo Parameters are invalid or not defined!
if %V1%==02 goto END
set V2=08
for %%Z in (%path%) do if exist %%Z\%V1% set V2=%%Z\%V1%
rem ============ Line 10 ============
if %V2%==08 echo The %V1% archiver hasn't been found!
if %V2%==08 goto END
set V3=13
for %%Z in (%path%) do if exist %%Z\Find.exe set V3=%%Z\Find.exe
if %V3%==13 echo The Find.exe utility hasn't been found!
if %V3%==13 goto END
if %7"==" echo Archive in inactive panel must be closed!
if %7"==" goto END
if %4"==.." echo Name for the archive isn't chosen!
rem ============ Line 20 ============
if %4"==.." goto END
%V3% /C /I /V ".%1" %5 | %V3% ": 0" > nul
if not errorlevel 1 echo Chosen file(s) - already %1-archive(s)!
if not errorlevel 1 goto END
%V3% /I "%4.%1" %5 > nul
if not errorlevel 1 if %2%3"==%6%7" echo Conflicting filenames!
if not errorlevel 1 if %2%3"==%6%7" goto END
ctty nul
%comspec% /f /c copy %V3% %6%7%4.%1 /Y | %V3% "1 f"
rem ============ Line 30 ============
ctty con
if errorlevel 1 echo Non-writable target disk or overwrite denied
if errorlevel 1 goto END
del %6%7%4.%1
if %1==RAR %V2% a -s- -rr -ems- -w%5\.. %6%7%4.%1 @%5
if %1==RAR if errorlevel 1 goto END
for %%Z in (1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) do echo.
if %1==ZIP %V2% %6%7%4.%1 -ex -P -wHS -jhsr @%5
if %1==ZIP if errorlevel 1 goto END
rem ============ Line 40 ============
echo Archive %4.%1 is written into the %6%7 directory.
:END

Structure of ARC.BAT file is primitive: it is just a succession of checks. No cycles, no subroutines. Jumps are performed, when check conditions are not met. Destination of all jumps is a single label END in the last line. In order to make ordinal search for lines easier, each tenth line is a comment announcing line's number.

The first line of ARC.BAT file switches off ECHO flag, because otherwise it will be difficult to notice the displayed messages. Commands in lines 2–7 check value of the first dummy parameter and presence of 6th dummy parameter's value. If either of these conditions is unmet, an error message informs that parameters are invalid or undefined. But if both conditions are met, then name of archiver program, which is to be called for, is assigned as a value to variable V1.

Command in the 9th line checks whether the selected archiver program can be found in either of those directories, which are specified by paths in value of PATH variable (2.02-02). Proper value of PATH variable should be prepared beforehand: it is one of required conditions, stipulated in introduction article to chapter 9. Examples of value assignments to PATH variable are shown in all sets of configuration files, presented in this book. But whether the selected archiver program will actually be found – this is your own responsibility, though. If archiver program wouldn't be found, then an error message will be displayed, and jump from line 12 to the END label will terminate execution. If archiver program will be found, then its name with preceding path will be assigned as a value to variable V2.

Similarly a command in line 14 arranges a search for FIND.EXE utility (6.14), which is used in several further checks. If FIND.EXE utility will be found, its name with preceding path will be assigned as a value to variable V3.

Commands in lines 17 and 18 of ARC.BAT file check whether a value of the 7th dummy parameter is empty. This value is empty, when inactive file manager's panel shows not a directory, but some other archive. In this case the error message, displayed by ARC.BAT, will prompt the user for the archive in the inactive panel to be closed.

The 19th line of ARC.BAT file checks that name, which the user had to highlight with mouse's left button click. This name later will be assigned to the created archive. But sometimes highlighted line in file manager's panel is left pointing at parent directory, represented by double dot alias. In this case error message will inform about invalid name choice.

Command in the 22nd line of ARC.BAT checks contents of file-list with names of those files, which should be included into new archive. It's just the moment to remind that value of the V3 variable, substituted twice in the 22nd line, is FIND.EXE utility name with preceding path. Being called for the first time, the FIND.EXE utility counts in file-list the filenames not belonging to that type of archives, which is to be created. Presence of such archive in prepared group of files is allowed, but repetitive compression of archives only is inept: reliability of data storage will be decreased. Count result is transferred via intermediate redirection, and then the same FIND.EXE utility, being called for the second time, checks whether the count result is zero. If files of other types wouldn't be found in prepared group, then an error message will point out that the selected files already are archives of the requested type.

Command in line 25 of ARC.BAT files searches inside the selected group for a file with the same name and the same suffix, which are specified for the new archive. If synonymous file exists yet, then new archive must be created outside the current directory, otherwise this synonymous file will be overwritten before it is included into new archive. If such situation is detected, then an error message will inform about conflict of filenames in current directory.

In the 29th line of ARC.BAT file an attempt is undertaken to write a file, synonymous to new archive, into destination directory, where new archive is to be written. Destination directory is addressed with all those precautions, which are needed for addressing to an inaccessible media: the CTTY NUL command in line 28 blocks up panic error messages, and a call for COMMAND.COM interpreter with /f parameter guarantees nonstop execution. It should be reminded that the value of the COMSPEC variable, substituted in the 29th line, is just the name of COMMAND.COM interpreter with preceding path. This value is assigned to COMSPEC variable automatically, when command interpreter is launched for the first time (6.04).

An attempt, undertaken in the 29th line, may fail, if destination directory contains yet a synonymous file, protected by HRS attributes (6.01). Another cause of failure may be non-writable or write-protected media. In any case the outcome of writing attempt is reflected by a message, sent by COPY command into STDOUT channel. But in line 29 STDOUT channel is redirected to FIND.EXE utility. If the latter registers writing attempt failure, then execution is terminated because of impossibility to create a file with prescribed name in destination directory. But in case of success the written file will be deleted by DEL command (3.09) in the 34th line. Thus the last obstacle to a success of archiver's mission will be removed.

Before any program is called for, the way this program displays its messages should be taken into account. The RAR.EXE archiver, called in line 35, forms its screen field bypassing STDOUT channel, so that screen needs to be cleared later. When screen is cleared by CLS command, then the following concluding message is shown in the upper screen's row and becomes hidden under file manager's panels. Therefore, in line 37 screen is cleared by FOR command, shifting cursor 20 rows down. Besides that, file manager's panels must not be unfolded to their full length. Length of Volkov Commander panels may be trimmed by mouse or by arrow keys while ALT-F11 or ALT-F12 key combinations are kept pressed. After that selected panel's size should be stored in VC.INI file by SHIFT-F9 keystroke.

The PKZIP archiver sends its messages via STDOUT channel, therefore it is called in line 38 after the screen is cleared. Due to preceding checks probability of archiving procedures failure is infinitesimal, nevertheless it is not neglected. Therefore, in lines 36 and 39 provisions are made for jumps to final END label, so that error message, sent by archiver program, will remain visible after interpretation of ARC.BAT terminates. When archiver mission is finished successfully, then ECHO command in the 41st line displays concluding message, informing about how the created archive is named and in which directory it is written.

Prepared batch file ARC.BAT should be stored in a directory, which may be accessed via paths, specified in value of PATH variable (2.02-02). Common directory with other file manager's files should be preferred. An example of Volkov Commander's menu file VC.MNU with lines launching ARC.BAT is shown in article 6.25-02.

9.03-02 Batch file for media status testing edit

When you have to repair an unknown computer, the first problem is to explore whether there are disk drives and whether these are accessible. Powerful utilities MSD.EXE (in MS-DOS6.22) and NDIAGS.EXE (from Norton Utilities software release) are aimed at solving this problem; both are compatible with MS-DOS 7, but both don't report media status — whether the media is present, formatted, writable, etc.

Presented batch file — let's name it DISK.BAT — provides a short, but comprehensive survey of disk media status. The main idea is to test disk's accessibility by reading volume's label and then to test disk's writeability by writing the same label back. This is safe, because both success and failure of writing procedure wouldn't inflict changes on disk under test.

On the other hand, the DISK.BAT file may be regarded as a source of several non-obvious solution examples for some urgent tasks :

  • incorporation of subroutines into a batch file ;
  • non-stop testing of disks, including inaccessible ones ;
  • avoiding undesirable error messages ;
  • generation of temporary command files ;
  • catching STDOUT output into an environmental variable.

Full text of DISK.BAT file is shown below. In order to make ordinal search for lines easier, each tenth line is a comment announcing line's number. Besides that, labels in DISK.BAT are named after numbers of corresponding lines; for example, label L28 denotes line 28, which marks end of the main part and start of a subroutine.

@echo off
if %1"==&" if not %2"==" goto L%2
if %Path%"==" %0 & 79 4 PATH
if %Temp%"==" %0 & 79 4 TEMP
Call %0 & 28 A Attrib D Debug F Find L Label
if VA"==" goto L87
set V1=%Path%
set Path=%1
set V2=%Path%
rem ============ Line 10 ============
set Path=%V1%
set V1=
Call %0 & 39 A B C D E F G H I J K L M N O P Q R S
if %V1%"==" if not %1"==" %0 & 79 5
if %1"==" set V1=A C D E F G O R
ctty nul
%VA% -H -R -S %Temp%\tmp.*
echo e 100 'call %%1 & 46' 20 > %Temp%\tmp.scr
echo w >> %Temp%\tmp.scr
rem ============ Line 20 ============
echo q >> %Temp%\tmp.scr
set Dircmd=
for %%Z in (%V1%) do call %0 & 53 %%Z: %1
del %Temp%\tmp.*
ctty con
for %%Z in (A D F L 1 2) do set V%%Z=
goto L87
:L28
shift
rem ============ Line 30 ============
shift
set VL=
for %%Z in (%path%) do if exist %%Z\%2.exe set VL=%%Z\%2.exe
if %VL%"==" echo Error: the %2.exe utility hasn't been found!
if %VL%"==" set VA=
if not %VL%"==" set V%1=%VL%
if not %4"==" goto L28
goto L87
:L39
rem ============ Line 40 ============
shift
if %V2%"==%2" set V1=%2
if %V2%"==%2:" set V1=%2
if not %3"==" goto L39
goto L87
:L46
set V1=%6
if not %7"==" set V1=%6 %7
if not %8"==" set V1=%6 %7 %8
rem ============ Line 50 ============
if %5"==has" set V1=NO NAME
goto L87
:L53
%comspec% /f /c Dir /-p %3\nul > %Temp%\tmp.txt
%VF% "Volume in d" < %Temp%\tmp.txt > %Temp%\tmp.bat
if errorlevel 1 %0 & 79 6 %3 %4
%VF% "Directory of " < %Temp%\tmp.txt
if errorlevel 1 %0 & 79 7 %3
%VF% "0 bytes free" < %Temp%\tmp.txt
rem ============ Line 60 ============
if not errorlevel 1 %0 & 79 8 %3
%VD% %Temp%\tmp.bat < %Temp%\tmp.scr
call %Temp%\tmp.bat %0
set V2=if errorlevel 20 del %Temp%\tmp.bat
%comspec% /f /c for %%Z in ("%VL% %3%V1%" "%V2%") do %%Z
%VF% "Volume Seria" < %Temp%\tmp.txt
if errorlevel 1 if not exist %Temp%\tmp.bat %0 & 79 9 %3
if errorlevel 1 if exist %Temp%\tmp.bat %0 & 73 1 %3
if not errorlevel 1 if not exist %Temp%\tmp.bat %0 & 73 2 %3
rem ============ Line 70 ============
if not errorlevel 1 if exist %Temp%\tmp.bat %0 & 73 3 %3
goto L87
:L73
if %3"==1" if %3"==A:" echo Disk %4 (%V1%) is writable > con
if %3"==1" if not %3"==A:" echo Disk %4 (%V1%) is a RAM-disk > con
if %3"==2" echo Disk %4 (%V1%) is write-protected > con
if %3"==3" echo Disk %4 (%V1%) is writable > con
Dir /a:ARD /-p /v %4\ | %Dsk%\DOS\MS7\Find.exe "otal d" > con
:L79
rem ============ Line 80 ============
if %3"==4" echo The %4 variable is not defined
if %3"==5" echo Wrong parameter, it must be a diskletter or none
if %3"==6" if not %5"==" echo Letter %4 doesn't refer to a disk > con
if %3"==7" echo Disk %4 has no media inside > con
if %3"==8" echo Disk %4 is probably a CD-ROM (no free space) > con
if %3"==9" echo Disk %4 is not formatted > con
:L87

The second line in DISK.BAT file is a check for the 1st dummy parameter's value. If it is ampersand, a jump to subroutine is performed; if don't, execution of the main program's part is continued. Note that address for the jump ("goto L%2") is not fixed, but rather is represented by value of the second dummy parameter. This enables to arrange subroutines as parts of the main batch file, otherwise subroutines have to be separate files.

Lines 3–22 specify preparation operations. First values of PATH and TEMP variables are checked. The PATH variable (2.02-02) must specify paths to MS-DOS 7 files, the TEMP variable must specify a path to directory for temporary files. If either of these variables is not defined, then a jump to label L79 is performed, an error message is displayed, and interpretation of DISK.BAT file terminates. Examples of value assignments to PATH and TEMP variables are shown in all sets of configuration files, presented in this book.

The next check in the 5th line calls for a subroutine, starting from label L28. This subroutine writes into environmental variables the names and paths of all utilities, which will be used for performing further tests. Names of these utilities, listed just in the 5th line, are transferred to subroutine via its dummy parameters. Main operation of particular path extraction from PATH variable's value is performed in line 33. If a path isn't found, the ECHO command in line 34 displays an error message. Until list of dummy parameters isn't empty, a jump from line 37 to L28 label will repeat the same cycle for finding next path to the next utility. Finally paths to Attrib.exe, Debug.exe, Find.exe, Label.exe utilities will become values of variables VA, VD, VF, VL, respectively.

After return from L28 subroutine the command interpreter continues execution of commands in lines 7–11. These operations convert disk's letter-name, specified by user in command line, to upper case. But user may specify some other sign instead of disk's lettername. Therefore, another subroutine, located in lines 39–45 of DISK.BAT, is called from line 13 in order to determine, whether the specified sign belongs to list of disk's letternames. If specified sign is rejected, then a jump is performed from line 14 to label L79, an error message is displayed, and execution terminates. If specified sign is indeed a lettername, it will be assigned as a value to variable V1, and then this disk only will be examined. But DISK.BAT may be launched without parameters, and then a predetermined group of disks should be examined. A list of corresponding letter-names is assigned as a value to variable V1 in line 15.

Disks examination procedures need three auxiliary files to be created in directory for temporary files: TMP.SCR, TMP.TXT, TMP.BAT. In order to guarantee creation of those auxiliary files, operation in line 17 takes off attributes from those synonymous files, which may exist yet in that directory. Value of variable VA, substituted in line 17, is name of ATTRIB.EXE utility with preceding path. The first of auxiliary files – TMP.SCR – is created by output redirections in lines 18–21. Contents and purpose of this file will be explained later.

The FOR command in the line 23 launches the main exploration cycle, sequentially for each of the disks under test. Disks to be tested are represented by value of the V1 variable. Disk's examination is performed by a subroutine, located in lines 53–72 of DISK.BAT file. For examination of one disk a call for this subroutine looks like

call %0 & 53 %%Z: %1

where the word "call" means a command to return into the same FOR cycle each time when execution of L53 subroutine terminates. Dummy parameter "%0" causes substitution of batch file name: DISK.BAT or some other, if this file will be named otherwise.

Parameters "& 53" specify label L53 as destination for the jump, which is to be performed from the second line. Substitution of disk's letter-name for "%%Z:" parameter specifies the disk to be examined.

It's important to notice that FOR cycle in line 24 is preceded by a CTTY NUL command (3.07) in line 16, which disrupts default communications with DOS's IN/OUT functions except those specified explicitly. This enables to avoid numerous error messages, which otherwise inevitably will be caused by access attempts to invalid or nonexistent disks. But error messages may be useful for debugging the DISK.BAT file itself; therefore during its first execution it is better to have the CTTY NUL command disabled by preceding it with REM command (3.24). If everything goes well, the preceding REM command in line 16 should be removed.

Disk's examination subroutine starts at label :L53. The first test in line 54 is performed by command

%Comspec% /f /c Dir /-p %3\nul

Just before execution, the name of variable %COMSPEC% will be replaced by its value, that is, by the name of COMMAND.COM interpreter with its path. Hence, a separate resident module of command interpreter will be loaded. The "/f" parameter forces this resident module to work non-stop, automatically answering "FAIL" to all queries about errors. The "/c" parameter means that resident module must execute only one following command (DIR) and unload itself just afterwards. The message, sent by DIR command into STDOUT channel, is redirected in line 54 into auxiliary file TMP.TXT.

The contents of TMP.TXT file are examined by FIND.EXE utility four times, in lines 55, 57, 59, and 66. Name FIND.EXE is substituted in these lines for name of VF variable. The first examination enables to reject nonexistent drives; the second – drives for removable media having no media inside, the third examination reveals CD/DVD-ROM drives. If examination conditions are not met, then a jump to label L79 is performed, and a corresponding message is displayed on the screen. Through the first three examinations those disks only will pass, which exist, have a media inside and are not CD/DVD-ROMs. A string, selected by FIND.EXE utility during the first examination in line 55, is sent via output redirection and is written into auxiliary file TMP.BAT; its contents, for example, may look like

Volume in drive D is EXTENDED1

After the first three examinations, in line 62, contents of TMP.BAT file are transferred as data to DEBUG.EXE, which accepts commands from command file TMP.SCR. The latter is created beforehand by redirections in lines 18–21; it contains the following lines :

e 100 'call %1 & 46' 20
w
q

In the first line command "e 100" (6.05-05) forces DEBUG.EXE to overwrite a part of the loaded string, which thus is transformed to

call %1 & 46 ve D is EXTENDED1

The second command "w" (6.05-19) makes DEBUG.EXE to write the transformed string back into file TMP.SCR, and the third command "q" closes debugger's session. After inflicted transformation the TMP.BAT file contains a CALL command (3.02) of COMMAND.COM interpreter. Execution of this CALL command will invoke that program, which will be defined by first dummy parameter %1 of TMP.BAT file. Just that happens, when TMP.BAT file is executed in line 63 of DISK.BAT file. But the first parameter of TMP.BAT file in line 63 is %0 dummy parameter of DISK.BAT file, i.e. the DISK.BAT file itself. Hence, command in line 63 performs a recursive call for DISK.BAT file. While this recursive call is executed, the "& 46" parameters define target label L46 for a jump from the second line, and the sixth parameter ("EXTENDED1" in the shown example) is volume's label of the examined disk. Therefore, control is transferred to a subroutine in lines 46–51 of DISK.BAT file. This subroutine replaces former value of V1 variable with a value of 6th dummy parameter – volume's label of the examined disk. Thus mission of L46 subroutine is fulfilled, and command interpreter returns to line 64 of DISK.BAT file, to subroutine L53.

Line 64 of DISK.BAT file prepares the value of the V2 variable with a single purpose: to avoid wrap-around of command string in line 65, which were otherwise too long. Cycle FOR in line 65 performs two operations, defined by values of variables VL, V1 and V2. The first operation is an attempt to write volume label back onto the same disk. The second operation is a conditional deletion of TMP.BAT file, if errorlevel value, returned by preceding writing attempt, informs about failure of this attempt. Since this moment existence of TMP.BAT file is an evidence of label writing attempt success and, consequently, signifies writeability of the examined disk. A check in line 66 reveals whether disk's serial number is present in file TMP.TXT. The result is represented by errorlevel value. This errolevel value together with existence of TMP.BAT file are those two arguments, which are enough for media status identification by conditional control transfers in lines 67–71 of DISK.BAT file. Control is transferred either to subroutine L73 or to subroutine L79, which both send to CON device (to display) messages, informing about status of the examined media.

Subroutine L73 is executed when examined media is found accessible, so that a command in line 78 will be able to show size of disk under test and percent of occupied disk's space. Subroutine L79 is executed when status of examined media leaves no hope to get more information about it. Subroutines L73 or L79 terminate examination of one disk. Then control is returned to continuation of cycle FOR in line 23, where disk examination procedure was originally called for.

Cycle FOR in line 23 will continue to call for disk's examination subroutine L53, sequentially specifying next disk's letter-names from list of disks to be examined. When all disks are examined, then DEL command in line 24 deletes remaining auxiliary files, CTTY CON command in line 25 restores default communications of DOS's I/O functions, and cycle FOR in line 26 deletes all local environmental variables. A jump from line 27 to final label L87 terminates execution of DISK.BAT file. While using the DISK.BAT file it should be taken into account that it needs direct access to disks under test. Therefore, DISK.BAT file shouldn't be launched inside "DOS box" under WINDOWS OS, it may be launched only under MS-DOS 7 or MS-DOS 8. Besides that, all 5 necessary conditions of successful execution, stipulated in introduction article to chapter 9, certainly must be met.

9.04 Configuration files with relocation to RAM-disk edit

Presented here versions of configuration files (CONFIG.SYS and AUTOEXEC.BAT) implement two alternatives: either ordinary booting mode or booting with arrangement of a virtual RAM-disk, followed by DOS relocation onto this RAM-disk. Virtual RAM-disk is much faster, than any real disk, its usage decreases load and wear of physical drives. But for computer's reparatory purposes a RAM-disk is essential because of other reason. When a computer boots itself from a removable media – a diskette or a CD-ROM – the drive, containing the media, is permanently busy with its mission. You can't insert other media in the drive unless operating system is relocated elsewhere. In these circumstances RAM-disk is the most suitable target to relocate DOS. The RAMDRIVE.SYS driver (5.05-01) supplied in WINDOWS-95/98 release enables to arrange virtual RAM-disks under MS-DOS 7. Common problem for most RAMdisk drivers, including RAMDRIVE.SYS, is that the letter-name, assigned to virtual RAM-disk, isn't preset in advance. RAM-disk is given the first available letter-name after those assigned to fixed logical disk(s). But number of fixed logical disks in various computers may differ. Therefore, the letter-name, assigned to RAM-disk, can't be known beforehand, it has to be determined. For this purpose MS-DOS 8 employs two special files : batch file FINDRAMD.BAT and an executable file FINDRAMD.EXE. The proposed pair of configuration files (CONFIG.SYS and AUTOEXEC.BAT) solves letter-name determination problem otherwise, exclusively by MS-DOS 7's means and without auxiliary files.

9.04-01 CONFIG.SYS file loading RAMDRIVE.SYS driver edit

This version of CONFIG.SYS file presents an example of a relatively simple block structure. The first block with reserved name [menu] offers two alternatives. When it is executed, two headers of menu items are shown, and you are invited to select one with UP and DOWN arrow keys. After your confirmation of your choice with ENTER keystroke, the loader IO.SYS assigns a name of the chosen alternative ("relocation" or "ordinary") as a value to the CONFIG environmental variable, and then proceeds to interpretation of commands in synonymous block of CONFIG.SYS file.

[menu]
numlock off
menuitem=relocation, Relocate DOS onto a 5.6 Mb RAM-disk
menuitem=ordinary, MS-DOS 7.10, ordinary loading
menudefault=relocation,20

[relocation]
include=ordinary
devicehigh=\DOS\DRV\Ramdrive.sys 5600 /E

[ordinary]
accdate c- d- e- rdevice=\
DOS\DRV\Himem.sys /v
device=\DOS\DRV\Emm386.exe ram v
dos=high,umb,noauto
buffershigh=30,0
fileshigh=30
lastdrivehigh=Z
fcbshigh=1,0
stackshigh=8,256
country=007,866,\DOS\DRV\Country.sys
devicehigh=\DOS\DRV\Dblbuff.sys
devicehigh=\DOS\DRV\Ifshlp.sys
devicehigh=\DOS\DRV\Setver.exe
devicehigh=\DOS\DRV\Atapimgr.sys /W:6 /NDR /T:5 /LUN
devicehigh=\DOS\DRV\Oakcdrom.sys /D:CD001

[common]
installhigh=\DOS\DRV\Ctmouse.exe
installhigh=\DOS\DRV\Keyrus.com
shell=\Command.com \ /E:2016 /L:511 /U:255 /p

Block [ordinary] is similar to version of CONFIG.SYS file, presented in article 9.08-01, but there are two differences. First, the ATAPIMGR.SYS driver (5.07-01) is loaded in order to enable access to DVD-ROM disks. Second difference is that MSCDEX.EXE TSR program (5.08-03) is not loaded in this CONFIG.SYS file, because its loading here may affect allotment of disk's letter-names. An equivalent TSR program SHSUCDX.COM (5.08-04) will be loaded later from the next configuration file AUTOEXEC.BAT (9.04-02).

Naturally, all paths, specified in CONFIG.SYS file, must be correlated with actual directory structure in your computer. If you choose the [ordinary] alternative, MS-DOS 7 will be loaded in an ordinary way, without relocation.

Block [relocation] consists of two lines. Command "include=" in the first line forces IO.SYS loader to execute all the lines of block [ordinary]. Then line 2 in block [relocation] loads Microsoft's RAM-disk driver RAMDRIVE.SYS (5.05-01). The latter creates a 5600 kb virtual RAM-disk in extended memory according to specified options. This size of RAM-disk can be afforded in all computers having 8 Mb of RAM memory or more, that is, in all modern and even in somewhat obsolete computers.

The last block in this CONFIG.SYS file has a reserved name [common]. A block with this name is executed always, irrespective of the chosen alternative. Commands in [common] block load "mouse's" driver CTMOUSE.EXE (5.03-03) and combined driver KEYRUS.COM (5.02-05) for switching codepages and keyboard's layouts. The last line in [common] block transfers control to command interpreter COMMAND.COM : the latter has to complete configuration procedure with execution of the last configuration file AUTOEXEC.BAT (9.04-02).

9.04-02 AUTOEXEC.BAT file with relocation to RAM-disk edit

This version of AUTOEXEC.BAT file is devised for loading MS-DOS 7 from logical disk A: with at least one directory \DOS. Logical disk A: may be represented by a real diskette or may be emulated by BIOS from an image stored in a CD-ROM disc. If you intend to use other disk to boot your computer, you have to

  • change disk's letter-name assignment in the 5th line ;
  • exclude this disk from list of disks to be tested (line 3 in "_relocation" section)
  • correct conditions of "IF" commands in lines 4 and 5 in "_relocation" section.

Of course, the required corrections may be made automatically, if letter-name of current disk is determined, for example, by REASSIGN.COM utility (9.06), as it is shown in article 9.01-03. But here it's better to concentrate your attention on other problem – on a search for that letter-name, which is assigned to RAM-disk. Most often similar loading scenarios are initiated just from logical disk A:.

@echo off
if %1"==J" if not %2"==" goto _%2
prompt $p$g
set dircmd= /A /O:GNE /P
set dsk=A:
set comspec=%dsk%\Command.com
goto _%config%

:_relocation
echo.
echo Seeking RAM-disk as the last valid disk...
for %%Z in (C D E F G H I J K L) do call \Autoexec.bat J test %%Z:
if A:==%dsk% echo RAM-disk is not found!
if A:==%dsk% goto _ordinary
echo RAM-disk is assumed to be %dsk%
echo.
set comspec=%dsk%\Command.com
\DOS\MS7\Xcopy.exe \*.* %dsk%\ /S /E
%dsk%\Autoexec.bat J ordinary

:_ordinary
%dsk%
cd \
if not exist TEMP\nul %comspec% nul /f /c md TEMP
if exist TEMP\nul set Temp=%dsk%\TEMP
if %Temp%"==" echo Note: the TEMP variable is left not defined!
Lh \DOS\DRV\Shsucdx.com /D:?CD001 /L:N /~+ /R /Q
set VC=%dsk%\DOS\VC4
path ;
path=%VC%;%dsk%\DOS\OTH;%dsk%\DOS\MS7
Vc.com /TSR /no2E /noswap
goto _end

:_test
echo Testing disk %3 ...
%comspec% nul /f /c if exist %3\nul cd DOS
if exist ..\nul set dsk=%3
if exist ..\nul echo              valid
if not exist ..\nul echo              inaccessible
cd \
:_end

This version of AUTOEXEC.BAT begins with a conditional jump in line 2, which enables recursive subroutine calls. When AUTOEXEC.BAT is interpreted for the first time, this jump is not performed. After that ordinary value assignments follow. The 7th line performs a jump to a label, which is stored as a value of CONFIG variable since execution of CONFIG.SYS file (9.04-01). This value may be either "relocation" (if you have chosen DOS relocation) or "ordinary" (if you prefer to leave DOS "as it is").

If "ordinary" alternative is chosen, then commands in section "_ordinary" are interpreted. These include value assignments for variables TEMP and PATH, loading of SHSUCDX.COM TSR program for access to CD/DVD-ROMs and launching VC.COM file manager. Finally MS-DOS 7 stays active on that disk, which was used to boot the computer.

If "relocation" alternative is chosen, then commands in section "_relocation" are interpreted. In the 3rd line after "_relocation" label a cycle FOR is executed, which recursively calls for a test subroutine in the last section "_test" of the same AUTOEXEC.BAT file. Test procedure is applied to a number of disks, specified by their letter-names inside brackets of the FOR command. This is done in order to find the last letter-name, corresponding to a valid, accessible disk. Just this letter-name corresponds to RAM-disk, if IFS and network drivers are not loaded yet. The latter condition was the reason to postpone loading of SHSUCDX.COM TSR program until test cycle comes to end.

Disk's letter-names are sequentially sent to "_test" subroutine via the third dummy parameter %3. Second line after the "_test" label executes a presence test for the root directory of any given disk. This test can be applied even to inaccessible and nonexistent disks. If root directory exists in the disk under test, then on the current disk the current directory is changed to \DOS. The following lines check existence of the parent directory for the current one : the \DOS directory has a parent (the root), but the root itself has no parent. If current directory has been changed, then letter-name of the disk under test is assigned as a value to environmental variable DSK. The last line in "_test" section returns current directory to the root.

In a FOR cycle, being given a sequence of disk's letter-names, the "_test" procedure sequentially overwrites former values of DSK variable with next values, each representing a letter-name of next accessible disk. But letter-name of the last valid disk will not be overwritten, it will be the value of DSK variable after the FOR cycle comes to end. This value will be just the letter-name of RAM-disk.

When FOR cycle terminates, interpretation of commands in "_relocation" section continues. The COMSPEC variable's value is reassigned once more, this time pointing to the root directory of RAM-disk. Then XCOPY.EXE copies all non-hidden files together with whole directory structure from original (current) disk to RAM-disk. Hidden files (IO.SYS and MSDOS.SYS) at that moment have finished their mission and are no more needed. Note that utility XCOPY.EXE, which performs copying, must be able to find its library file XCOPY32.EXE in the same directory (i.e. in \DOS\MS7).

The last line in section "_relocation" recursively transfers control not to original AUTOEXEC.BAT file, but to its copy in the root directory of RAM-disk, because value of DSK variable has been changed and now points just at RAM-disk. Because of the same reason all further addressing, defined by the DSK variable, will also refer to RAM-disk.

Interpretation of AUTOEXEC.BAT file's copy continues from first line in section "_ordinary". Operation in its first line acquires great importance: it changes current disk to RAM-disk. Since that time original bootable disk becomes abandoned, all its files are closed yet, and it may be ejected off the drive. Now active operating system is a copy of MS-DOS 7 on RAM-disk. All following operations in section "_ordinary" will be performed as if MS-DOS 7 were normally loaded from RAM-disk.

9.05 Examples of simple utilities edit

Though debugger DEBUG.EXE (6.05) is not the most convenient instrument for making executable programs, nevertheless it enables to write simple programs of COM format. Remarkable feature of COM format is that program is laid out in PC's memory for execution just as it is presented in file. Therefore, programmer's experience should start from writing the most simple programs of COM format. Simplest programs are not structured, don't appeal to disks, to files, to user. Simplicity enables to ignore some restrictions, including restrictions on allocated memory space (note 2 to 8.02-50). Further in this part two examples of very simple programs are presented. Both were written spontaneously for solving unexpected problem. Later in internet more perfect programs have been found for solving the same problems. Nevertheless, here the simplest original versions are presented, because perfection shouldn't be the main purpose for a beginner. At this stage our main purpose – your ability to understand the concept and to implement it.

9.05-01 Blue color brightness correction edit

An impetus for writing this simplest program was a display changeover from former CRT to a newer LCD. Due to different modulation characteristics of LCD screen, the customary dark blue color of file manager's panels became too bright, causing visual irritation. In order to return former color to file manager's panels the BLUE.COM program was written. It has effect on videocard's digital-to-analog converter (DAC) so that blue color brightness is decreased. BLUE.COM program is designed for videomode 03h only and has no adaptation capabilities. Nevertheless, it has proved itself useful ; perhaps you'll find it useful too.

BLUE.COM file is produced by debugger DEBUG.EXE as a result of command sequence execution. This command sequence should be written into command file BLUE.SCR by means of editor program (as described in introduction article to chapter 9) and then sent to debugger via input redirection :

Debug.exe < Blue.scr

Command file BLUE.SCR has to contain the following lines :

A 100
MOV          AX,1010       ;100 Specify brightness function (8.01-24)
MOV          BX,0001       ;103 Prepare DAC's register number
MOV          CX,0015       ;106 CL - blue color brightness,
MOV          DX,0000       ;109               CH - green, DH - red
INT          10            ;10C Call for BIOS's INT10 handler
MOV          AX,4C00       ;10E Specify DOS's exit function (8.02-55)
INT          21            ;111 Call for DOS's INT21 handler

N Blue.com
R BX
0000
R CX
0013
W
Q

The first command "A 100" switches debugger DEBUG.EXE into assembler mode and specifies start address CS:0100h for writing machine codes. The next 7 lines define all actions of BLUE.COM program. Meaning of each action is explained by a comment after semicolon in each corresponding line. The 9th line is left empty (7.01-04), it will force DEBUG.EXE to exit assembler mode. Then "N" command (6.05-12) announces a name for the file, which is to be created, and its length (00000013h = 19 bytes) is written into registers BX and CX. The "W" command in line 15 creates file BLUE.COM and copies there the prepared machine codes. The "Q" command in the last line terminates debugger's session.

While command file BLUE.SCR is processed, the debugger displays a listing on the screen. Listing enables to monitor correctness of BLUE.SCR file's typesetting by comparison of actual offsets in listing with proper offsets, presented just after semicolon as the first item of comment in each line. Comparison technique is described in article 9.07-01. If listing doesn't reveal errors, then BLUE.COM utility is ready for execution. Further testing for such simple programs is not needed. If you are not satisfied with that blue color brightness level, which is set by BLUE.COM utility, then brightness value, written into CX register in the 4th line of BLUE.SCR file, may be corrected as you want. The corrected BLUE.SCR file should be sent once more to DEBUG.EXE via redirection, so that new version of BLUE.COM utility will be created.

The BLUE.COM utility should be launched from that line of AUTOEXEC.BAT file, which precedes launching of Volkov Commander file manager. Besides that, default brightness levels are reset anew, for example, by SCANDISK.EXE utility (6.21) and by LXPIC.EXE viewer, mentioned in relation to VC.EXT file (6.25-03). After such programs the BLUE.COM utility should be executed once more. This may be done automatically, if BLUE.COM is launched from the next line of common batch file or of common configuration file (VC.EXT, for example). Such examples are not presented here, because necessity of color correction depends on personal visual perception. Nevertheless, blue color correction, performed by BLUE.COM utility, has been implemented for making screenshots fig.3 (in article 6.09) and fig.5 (in article 6.25-01).

9.05-02 How an ATX computer can be switched off edit

One more impetus for writing a simple program has emerged in 1999, when computers of ATX form-factor have ousted former AT computers. Newer ATX computers were designed for being switched off by operating system, and adjustable role of power button in ATX computers was difficult to foresee. DOS has never had a utility for switching power off. Since such utility was needed, it has been written, and it has got name TURN_OFF.COM.

TURN_OFF.COM file is produced by debugger DEBUG.EXE as a result of command sequence execution. This command sequence should be written into command file TURN_OFF.SCR by means of editor program (as described in introduction article to chapter 9) and then sent to debugger via input redirection :

Debug.exe < Turn_off.scr

Command file TURN_OFF.SCR has to contain the following lines :

a 100
mov          AX,5301       ;100 Specify APM activation function
mov          BX,0000       ;103 Prepare identifier of APM BIOS
int          15            ;106 Call INT15 handler for activation
mov          AX,530E       ;108 Specify request for APM emulation
mov          BX,0000       ;10B Prepare identifier of APM BIOS
mov          CX,0102       ;10E Request emulation of version 1.2
int          15            ;111 Call INT15 handler for emulation
mov          AX,5307       ;113 Specify power supply mode function
mov          BX,0001       ;116 Prepare all device's identifier
mov          CX,0003       ;119 Request power OFF operation
int          15            ;11C Call INT15 handler for power OFF
mov          AX,4C00       ;11E Specify DOS's exit function code
int          21            ;121 Call DOS's INT21 handler for exit

n turn_off.com
r BX
0000
r CX
0023
w
q

Proposed command file TURN_OFF.SCR is indeed very simple, it doesn't specify conditional jumps, it uses two types only of machine codes, and produced executable file is as short as 35 (23h) bytes.

The first command "A 100" switches debugger DEBUG.EXE into assembler mode and specifies start address CS:0100h for writing machine codes. The next 13 lines define all actions of TURN_OFF.COM program. Meaning of each action is explained by a comment after semicolon in each corresponding line. More detailed information about INT 15\AH=53h functions can be found in articles 8.01-70 – 8.01-72.

The 15th line is left empty (7.01-04), it will force DEBUG.EXE to exit assembler mode. Then "N" command (6.05-12) announces a name for the file, which is to be created, and its length (00000023h = 35 bytes) is written into registers BX and CX. The "W" command in 21st line creates file TURN_OFF.COM and copies there the prepared machine codes. The "Q" command in the last line terminates debugger's session.

In order to monitor correctness of TURN_OFF.SCR file's typesetting a comment in each line starts with proper offset of corresponding machine command. This offset should be compared with actual offset, shown by debugger in displayed listing. Comparison technique is described in article 9.07-01. It may be expedient to check the length of created file either with DIR command (3.10) or by length indication in file manager's panels. If both listing and length check don't reveal errors, then TURN_OFF.COM utility is ready for execution. Further testing for such simple programs usually isn't necessary.

While using the TURN_OFF.COM utility it should be taken into account that most part of computers produced in previous millennium have no APM system and therefore will ignore calls for INT 15\AH=53h functions. Besides that, TURN_OFF.COM utility shouldn't be run inside "DOS box" under WINDOWS OS, it must run under native DOS only. It is convenient to launch TURN_OFF.COM via file manager's menu (example in article 6.25-02).

It were desirable, of course, to make provisions against probable errors : against execution inside "DOS box" and against absence of APM system in a particular computer. However, then TURN_OFF.COM utility were not so simple. On the other hand, relevant supplementary checks are also not too complex. For example, recognition of WINDOWS OS operating environment is implemented in lines 13Ah–141h of a file in article 9.10-02.

Multiple examples of error indication are shown in articles 9.06, 9.08 and 9.10. Having acquainted yourself with these examples, you will be able to upgrade TURN_OFF.COM utility as you want.

Note 1: unprepared loss of power supply causes loss of these data, which may be not written yet at that moment by SMARTDRV.EXE driver (5.06-01) from cache buffer to disk. This is equally relevant for both unexpected mains power loss and for switching power OFF with TURN_OFF.COM utility. If you intend to employ SMARTDRV.EXE driver, you may force data writing to disk just before TURN_OFF.COM utility is launched, but it's safer to disable write-behind caching at all.

9.06 Value assignment to an environmental variable edit

Long time ago DOS has been developed as general purpose operating system, and its set of commands has been formed according to this purpose. But now DOS's role has specialized, so that former set of commands now seems partially redundant and partially deficient. This article presents a utility performing three operations, which replenish the most urgent deficiencies of DOS's commands set. In particular, just these three operations enable to implement adaptive loading scenario, described in section 9.09.

As far as proposed utility replaces current value of some environmental variable with requested other data, it has been named REASSIGN.COM. File REASSIGN.COM is not large — in all 992 bytes long — because it relies on cooperation with DOS's internal SET command (3.26) and doesn't duplicate its functions. The SET command prepares an environmental variable, and the first character of prepared variable's value defines operation for REASSIGN.COM utility :

  1. report size of largest free XMS memory block ;
  2. accept input from keyboard ;
  3. report letter-name of the current disk.

The rest characters in prepared value are ignored, but their presence reserves space for the new value. If this space is not enough to contain the returned result, then an error message is displayed. If the result doesn't occupy the whole reserved space, its rest part is filled with space characters (20h).

When computer boots from any removable media, letter-name of the current disk is assigned by BIOS. Function 3 of REASSIGN.COM utility enables to determine the assigned letter-name :

set disk=33
Reassign disk
echo Current disk is %disk%

Prepared value 33 will be replaced by a new one, for example, D:. Since assigned letter-name becomes known, all further addressing in configuration files can be adapted automatically.

The next problem of adaptive loading procedures is to determine feasible size of RAM-disk for DOS relocation, whereas the amount of available memory isn't known beforehand. But function 1 of REASSIGN.COM utility gives the answer :

set xms=11111
Reassign xms
echo Largest free XMS memory block is %xms% kb

The last line in the example above displays the returned result. Now you are ready to specify size of RAM-disk, and function 2 of REASSIGN.COM utility will accept the desired value :

echo Specify the size of RAM-disk in kb:
set ramdisk=22222
Reassign ramdisk
Tdsk.exe R: %ramdisk% 512 /M /F:2

During execution of function 2 the REASSIGN.COM utility invites you to input the desired value via keyboard. Wrong digits may be erased with BackSpace keystroke. When data input is completed, further execution should be resumed by ENTER keystroke. Command in the last line of the shown example substitutes desired size value into a set of parameters for TDSK.EXE driver (5.05-02), which arranges RAM-disk of desired size. Of course, REASSIGN.COM utility can find a lot of other applications, besides the mentioned ones.

It is important to note that all shown examples can't be executed from command lines, presented by Norton Commander, Volkov Commander or similar file managers. The reason is that file managers execute each command line inside a separate environment. Hence a value, set by SET command in one command line, becomes unavailable in the next command line. But ordinary command lines as well as lines inside batch files are executed in common environment, so that there all shown examples will be executed properly.

REASSIGN.COM utility is produced by debugger DEBUG.EXE as a result of command sequence execution. This command sequence should be written into command file REASSIGN.SCR by means of editor program (as described in introduction article to chapter 9). Verbal commentaries may be omitted. Special attention should be paid to empty line — 8th from the end. It must be there, because empty line forces DEBUG.EXE to exit assembler mode (7.01-04). Then command file REASSIGN.SCR should be sent to debugger via input redirection :

Debug.exe < Reassign.scr

Command file REASSIGN.SCR has to contain the following lines :

a 100
      ;************* Reassign.com **************
      ;********* Section 1: initial preparations
         ; 110 - target offset for jump from line 104
cmp          SP,2010       ; 100 Allotted less than 8 kb?
jbe          0110          ;*104 If yes, leave it intact
mov          SP,1FFE       ; 106 Set stack's top at 8 kb
mov          BX,0200       ; 109 Request for 8 kb space
mov          AH,4A         ; 10C Call for free MCB
int          21            ; 10E        creation function
mov          DX,03A8       ;=110 Message 4 offset (help)
cmp byte ptr [005D],20     ; 113 Is 1st byte in FCB free?
jz           0151          ;*118 If yes, go to display help
cmp byte ptr [005D],3F     ; 11A 1st byte - question mark?
jz           0151          ;*11F If yes, go to display help
cld                        ; 121 Set count upwards (DF=UP)
      ;********** Section 2: search results analysis
call         0173          ;*122 Call for PSP test subroutine
cmp byte ptr [0165],F7     ; 125 1st or 2nd cycle errors?
ja           0151          ;*12A Exit, if yes
mov          DX,0345       ;*12C Prepare message 2 offset
les          DI,[00F8]     ; 12F ES:DI - address of variable
ES:                        ; 133 Read 1st character in
mov          BL,[DI]       ; 134          variable's value
cmp          BL,31         ; 136 Lower limit: function 1
jb           0151          ;*139 Exit, if value is less
cmp          BL,33         ; 13B Upper limit: function 3
ja           0151          ;*13E Exit, if value is greater
shl          BL,1          ; 140 multiply by 2
mov          BH,00         ; 142 Prepare BX for calculation
call         [BX+0105]     ;*144 Call functional subroutines
jz           0151          ;*148 ZR state - message display
jb           015F          ;*14A CY - fail, errorlevel in DH
      ;************* Section 3: execution conclusion
         ; 151 - target for 118, 11F, 12A, 139, 13E, 148
         ; 15F - target for jump from lines 14A, 14F
call         01F9          ;*14C Call for copying subroutine
jmp          015F          ;*14F Jump to termination
mov          BX,DX         ;=151 DS:DX - message address
mov          CX,[BX-02]    ; 153 CX = number of characters
mov          BX,0001       ; 156 BX = handle to STDOUT
mov          AH,40         ; 159 Call for DOS's message
int          21            ; 15B            display function
mov          DH,F0         ; 15D Errorlevel = F0h
mov          AL,DH         ;=15F Restore errorlevel
mov          AH,4C         ; 161 Call for DOS's program
int          21            ; 163        termination function
      ;************** Section 4: data block.
         ; 165 - used in lines 125, 1D1, 1D6, 1F4, 205
dw           00FC
         ; 167 = (2*31 + 105) used in lines 144, 221, 22D
         ; 169 - used in lines 225, 236, 240, 247, 252, 280
dw           0213,029A,02DC,0000,0000,0000
      ;************** Section 5: PSP test subroutine
         ; 173 - target for calls from lines 122, 1A3
         ; 19F - target for a jump from line 197
         ; 1A6 - target for jumps from 17A, 183, 18F, 19D
xor          DI,DI         ;=173 Write zero in DI register
ES:                        ; 175 Does segment start
cmp word ptr [DI],20CD     ; 176          from CD20 command?
jnz          01A6          ;*17A Exit, if not
ES:                        ; 17C Is there CD21 command
cmp word ptr [0050],21CD   ; 17D            at offset 0050h?
jnz          01A6          ;*183 Exit, if not
push         ES            ; 185 Save PSP segment address
ES:                        ; 186 Load environment's segment
mov          ES,[002C]     ; 187            into ES register
call         01B3          ; 18B Call for name search
pop          ES            ; 18E Restore PSP address in ES
jz           01A6          ;*18F Exit, if name isn't found
mov          AX,ES         ; 191 Load PSP segment into AX
mov          DI,0016       ; 193 Is word in ES:[0016] cell
scasw                      ; 196     equal to segment in AX?
jnz          019F          ;*197 If no, word is parent's PSP
mov          DI,0010       ; 199 Is word in ES:[0010] cell
scasw                      ; 19C equal to segment in AX?
jz           01A6          ;*19D If yes, exit, nothing found
ES:                        ;=19F Load parent's PSP segment
mov          ES,[DI-02]    ; 1A0            into ES register
call          0173         ; 1A3 Call for PSP test subroutine
ret                        ;=1A6 Return from subroutine
      ;************* Section 6: name search subroutine
         ; 1A7 - target for jumps from lines 1C8, 1CF
         ; 1B3 - target for call from line 18B
         ; 1DF - target for jumps from lines 1B1, 1BA
mov          CX,0100       ;=1A7 Set number of comparisons
mov          AL,00         ; 1AA Compare with zero value
repnz                      ; 1AC Repeat till 00 is found
scasb                      ; 1AD Compare [ES:DI] with AL=00
cmp          CX,0000       ; 1AE If number of repetitions
jz           01DF          ;*1B1        expires, exit search
mov          DX,0318       ;=1B3 Entrance point to search
ES:                        ; 1B6 If [DI]==00h, hence
cmp byte ptr [DI],00       ; 1B7     environment is searched
jz           01DF          ;*1BA     up to end, exit search
mov          SI,005D       ; 1BC Name address - in DS:SI
mov          CX,000A       ; 1BF Set number of comparisons
repz                       ; 1C2 Repeat until difference
cmpsb                      ; 1C3 Compare [DS:SI] and [ES:DI]
cmp byte ptr [SI-01],20    ; 1C4 Does name end with space?
jnz          01A7          ;*1C8 If not, compare next name
ES:                        ; 1CA Is there equality sign
cmp byte ptr [DI-01],3D    ; 1CB            after the name?
jnz          01A7          ;*1CF If not, compare next name
sub byte ptr [0165],04     ;*1D1 Shift record offset by 04
mov          SI,[0165]     ;*1D6 Load record offset into SI
mov          [SI],DI       ; 1DA Save name's address
mov          [SI+02],ES    ; 1DC Save environment's segment
ret                        ;=1DF Return from subroutine
      ;******** Section 7: value copying subroutine
         ; 1E0 - target for jump from line 210
         ; 1E5 - target for jump from line 1F1
         ; 1F3 - target for jumps from lines 1E9, 1EE
         ; 1F9 - target for a call from line 14C
         ; 1FB - target for jump from line 202
         ; 204 - target for jump from line 1FF
CS:                        ;=1E0 Load source address
lds          SI,[00F8]     ; 1E1               into DS:SI
ES:                        ;=1E5 Is there free space at
cmp byte ptr [DI],00       ; 1E6        destination address?
jz           01F3          ;*1E9 If not, start next cycle
cmp byte ptr [SI],00       ; 1EB Is the source empty?
jz           01F3          ;*1EE If yes, start next cycle
movsb                      ; 1F0 Let's copy one byte
jmp          01E5          ;*1F1 Jump to check next byte
CS:                        ;=1F3 Calculate cell offset with
add word ptr [0165],0004   ; 1F4    next destination address
mov          AL,20         ;=1F9 Subroutine entrance point
ES:                        ;=1FB Is there a space to fill
cmp byte ptr [DI],00       ; 1FC     at destination address?
jz           0204          ;*1FF If not, finish filling
stosb                      ; 201 Send space to destination
jmp          01FB          ;*202 Go to check next byte
CS:                        ;=204 Load offset of cell with
mov          SI,[0165]     ; 205 destination address in SI
CS:                        ; 209 Load destination address
les          DI,[SI]       ; 20A                  into ES:DI
cmp          SI,00F8       ; 20C Is cell offset the last?
jnz          01E0          ;*210 If not, start next cycle
ret                        ; 212 Return from subroutine
      ;****** Section 8: function 1, XMS-memory space
         ; 213 - target address, stored in cell 167
         ; 256 - target for jumps from lines 23E, 247, 250
         ; 25B - target for jump from line 263
         ; 269 - target for jump from line 277
         ; 275 - target for jump from line 272
         ; 284 - target for jump from line 27E
         ; 285 - target for jumps from lines 21A, 234
mov          AX,4300       ;=213 Function 1 entrance point
int          2F            ; 216 Check whether HIMEM.SYS
cmp          AL,80         ; 218         driver is installed
jnz          0285          ;*21A Exit, if not installed
mov          AX,4310       ; 21C Driver's entrance request
int          2F            ; 21F Entrance address - in ES:BX
mov          [0167],BX     ; 221 Store entrance offset
mov          [0169],ES     ; 225 Store entrance segment
mov          BL,00         ; 229 Prepare 00h in BL register
mov          AH,08         ; 22B Code of XMS space request
call far     [0167]        ; 22D Send request to HIMEM.SYS
cmp          BL,00         ; 231 Is request satisfied?
jnz          0285          ;*234 Exit, if not
mov byte ptr [0169],00     ; 236 Let errorlevel be 00 if
cmp          AX,1900       ; 23B   free XMS area is not
jb           0256          ;*23E   enough for 6 Mb disk
inc byte ptr [0169]        ; 240 Let errorlevel be 01 if
cmp          AX,4900       ; 244   5600 Kb XMS-disk can
jb           0256          ;*247   be arranged
inc byte ptr [0169]        ; 249 Let errorlevel be 02 if
cmp          AX,8C00       ; 24D   XMS-disk must be limited
jb           0256          ;*250   to 16 Mb, if not, then
inc byte ptr [0169]        ; 252   let errorlevel be 03
mov          BP,SP         ;=256 Store current SP state
mov          BX,000A       ; 258 Set decimal divisor
xor          DX,DX         ;=25B Write zero into DX register
div          BX            ; 25D Divide by decimal divisor
push         DX            ; 25F Push remainder into stack
cmp          AX,0000       ; 260 Is dividing finished?
jnz          025B          ;*263 If not, go to next cycle
les          DI,[00F8]     ; 265 Target address - into ES:DI
pop          AX            ;=269 Pop a digit out of stack
add          AL,30         ; 26A Translate digit to ASCII
mov          SI,DI         ; 26C Store DI state in SI
ES:                        ; 26E Is there free space
cmp byte ptr [DI],00       ; 26F          at target address?
jz           0275          ;*272 Skip writing, if not
stosb                      ; 274 Write digit, increment DI
cmp          SP,BP         ;=275 All digits are popped?
jb           0269          ;*277 If not, go pop next digit
mov          DX,036F       ;*279 Prepare 3rd message offset
cmp          DI,SI         ; 27C Has writing been skipped?
jz           0284          ;*27E If skipped, skip errorlevel
mov          DH,[0169]     ; 280 Read errorlevel into DH
ret                        ;=284 Return from subroutine
mov          DH,FF         ;=285 Prepare errorlevel = FF
stc                        ; 287 Indicate failure by CF
ret                        ; 288 Return from subroutine
      ;******* Section 9: function 2, keyboard input
         ; 289 - target for jump from line 2AD
         ; 29A - target from 169, 28D, 291, 295, 2B4, 2BD
         ; 2BF - target for jump from line 2A8
         ; 2C7 - target for jumps from lines 2A3, 2C3
ES:                        ;=289 Let's check whether
cmp byte ptr [DI],00       ; 28A              buffer is full
jz           029A          ;*28D If yes, wait 1Bh, ODh, 08h
cmp          AL,20         ; 28F Characters below 20h
jb           029A          ;*291       shouldn't be accepted
cmp          AL,3D         ; 293 Equality sign
jz           029A          ;*295       shouldn't be accepted
int          29            ; 297 Display inputted character
stosb                      ; 299 Copy it into ES:DI buffer
mov          AH,10         ;=29A Function 2 entrance point
int          16            ; 29C Read inputted character
mov          DH,FF         ; 29E Errorlevel = FFh
cmp          AH,01         ; 2A0 Is the ESC key pressed?
jz           02C7          ;*2A3 If yes, no copying, exit
cmp          AH,1C         ; 2A5 Is the ENTER key pressed?
jz           02BF          ;*2A8 If yes, copy and then exit
cmp          AH,0E         ; 2AA Is BackSpace key pressed?
jnz          0289          ;*2AD If not, start next cycle
ES:                        ; 2AF Check, whether offset
cmp byte ptr [DI-01],3D    ; 2B0     in DI would point ahead
jz           029A          ;*2B4     to buffer's first cell
mov          AX,2008       ; 2B6 Output backspace sign and
call         02D2          ;*2B9          a space via INT 29
dec          DI            ; 2BC Decrement offset in DI by 1
jmp          029A          ;*2BD Return to start of cycle
cmp          DI,[00F8]     ;=2BF Has offset in DI changed?
jz           02C7          ;*2C3 If not, let's leave DH = FF
mov          DH,00         ; 2C5 If yes, let's set DH = 00
mov          AX,0A0D       ;=2C7 Output of line feed and
call         02D2          ;*2CA         CR signs via INT 29
cmp          DH,20         ; 2CD Set flags by comparison
cmc                        ; 2D0 Reverse state of CF flag
ret                        ; 2D1 Return from subroutine
      ;***** Section 10: function 2, output subroutine
         ; 2D2 - target for calls from lines 2B9, 2CA
         ; 2D5 - cycle return offset from line 2D9
mov          CX,0003       ;=2D2 Preset repetitions limit
int          29            ;=2D5 Send character to display
xchg         AL,AH         ; 2D7 Exchange characters
loop         02D5          ;*2D9 Cycle iteration check
ret                        ; 2DB Return from subroutine
      ;***** Section 11: function 3, disk determination
         ; 2DC - target address, stored in cell 16B
         ; 2EE - target for call from line 2E9
         ; 2F6 - target for call from line 30C
         ; 305 - target for call from line 2FE
         ; 30E - target for calls from lines 2F9, 308
mov          AH,19         ;=2DC Function 3 entrance point
int          21            ; 2DE Determine current disk
mov          DL,AL         ; 2E0 Copy disk number into DL
add          AL,41         ; 2E2 Translate number into ASCII
stosb                      ; 2E4 Copy ASCII code into ES:DI
ES:                        ; 2E5       and increment DI by 1
cmp byte ptr [DI],00       ; 2E6 Is buffer full?
jz           02EE          ;*2E9 If yes, don't append colon
mov          AL,3A         ; 2EB ASCII code of colon - in AL
stosb                      ; 2ED Copy colon's code to ES:DI
mov          DH,00         ;=2EE Prepare zero errorlevel
push         DI            ; 2F0 Save DI pointer in stack
mov          AX,0803       ; 2F1 Query for 1st DDT address
int          2F            ; 2F4 1st DDT address - in DS:DI
cmp          [DI+04],AL    ;=2F6 Is it a floppy disk?
ja           030E          ;*2F9 If no, don't search further
cmp          [DI+04],AH    ; 2FB If disk drive is the same,
jz           0305          ;*2FE        it should be skipped
mov          AH,[DI+04]    ; 300 Read disk drive number
inc          DH            ; 303 Increment number of drives
cmp word ptr [DI],FFFF     ;=305 Is this DDT the last?
jz           030E          ;*308 If yes, no further search
lds          DI,[DI]       ; 30A Next DDT address - in DS:DI
jmp          02F6          ;*30C Go to investigate next DDT
pop          DI            ;=30E Restore DI state from stack
push         CS            ; 30F Restore original
pop          DS            ; 310               segment in DS
cmp          DH,FF         ; 311 Clear ZF flag to NZ state
clc                        ; 314 Clear CF flag to NC state
ret                        ; 315 Return from subroutine
      ;********************* Section 12: message texts
dw           002B
         ; 318 - 1st message, mentioned at 1B3
db           0D 0A 'ERROR: specified name hasn'
db           27 't been found' 0D 0A
dw           0028
         ; 345 - 2nd message, mentioned at 12C
db           0D 0A 'ERROR: invalid value of the'
db           20 'variable' 0D 0A
dw           0037
         ; 36F - 3rd message, mentioned at 279
db           0D 0A 'ERROR: no space for new value,'
db           20 'old one is too short' 0D 0A
dw           0138
         ; 3A8 - 4th message (help), mentioned at 110
db           0D 0A 09 'Reassign.com overwrites value'
db           20 'of existing variable' 0D 0A 'Usage:'
db           0D 0A 09 'Reassign Anyname' 0D 0A 'Anyn'
db           'ame - name example (up to 8 letters) o'
db           'f a variable' 0D 0A 'The first in its'
db           20 'value must be a digit 1, 2 or 3 - i'
db           't defines function:' 0D 0A 09 '1 - get'
db           20 'size of largest free XMS block' 0D 0A
db           09 '2 - accept keyboard' 27 's input' 0D
db           0A 09 '3 - get current disk letter' 0D 0A
         ; 4E0

n Reassign.com
rbx
0000
rcx
03E0
w
q

Text of REASSIGN.COM utility begins with procedure releasing that memory, which certainly wouldn't be needed (details in note 5 to A.12-7). Then in lines 110–11F a check is performed for that name of environmental variable, which should be read by command interpreter from command line, translated to upper case and written into first FCB block (note 4 to A.07-1) starting at offset 005Dh. If a cell at offset 005Dh is empty or contains an interrogation sign, then REASSIGN.COM displays help message and returns control to command interpreter, leaving errorlevel 240. Otherwise the cell at offset 005Dh is considered containing a name of that environmental variable, which is to have its value reassigned.

Section 2 begins in line 122 with a call for variable's name search in current and in underlying environments. Search procedure includes PSP test subroutine, presented in section 5, and name search subroutine, presented in section 6. Commands in lines 175–183 check specific PSP signatures in cells at offsets 0000h and 0050h. If PSP validity is confirmed, then address of corresponding environment is read from a cell at offset 002Ch and is sent to name search subroutine, called for from line 18B.

Name search subroutine inspects the whole environment space and exits with ZF flag set, if the requested name isn't found. But when the search ends successfully, then full address (segment: offset) of that variable's value is written into a memory cell. Offset of this memory cell is calculated by subtraction of 4 from a pointer, stored at offset 0165. Therefore, addresses, found in different environments, don't overwrite each other, but rather are stored side-by-side at the end of the PSP, belonging to REASSIGN.COM utility.

If name search subroutine returns with ZF flag cleared, then PSP test subroutine has to "descend" into underlying ("parent") PSP and to continue its tests there. Pointers in current PSP cells at offsets ES:[0016] and ES:[0010] are regarded as "parent" PSP candidate addresses. With respect to selected "parent" PSP candidate the PSP test subroutine in line 1A3 recursively calls for itself. All checks are repeated in that underlying PSP and in its environment. Recursive descent into underlying PSP may be repeated several times. It comes to end, when it reaches the "bottom" PSP, or when the last PSP is found invalid, or when the requested variable's name isn't found in the last environment.

After termination of PSP test subroutine execution of operations in section 2 continues. There in line 12F the address of variable's value, related to the nearest ("upper") environment, is written into registers ES:DI. This address points just at the first character of variable's value, which defines requested function for REASSIGN.COM utility. This character must be a digit, corresponding to either of ASCII codes 31h, 32h, 33h. If there is some other code, then REASSIGN.COM displays error message and returns control back to command interpreter. But if all checks are successfully passed, then in line 144 a subroutine is called for, which is to execute the requested function. Address of subroutine's entrance point is taken from a list at offset 0167h in section 4.

Execution of function 1 begins from entrance point 0213h in section 8. First a call for INT 2F\AX=4300h (8.03-22) detects whether HIMEM.SYS driver (5.04-01) is loaded or not. If it isn't loaded, then REASSIGN.COM displays no error message, but leaves errorlevel 255 and returns control back to command interpreter. When HIMEM.SYS driver is loaded, then a call for INT 2F\AX=4310h (8.03-23) follows in order to get driver's entrance point address. Returned full address is used in line 22D for a far call to driver's 08h function (A.12-3). It returns several parameters, including size of the largest free XMS-memory block. In lines 236–252 a value of errorlevel is set in order to prompt a feasible size of RAM-disk. Commands in lines 256–263 convert hexadecimal XMS block size into decimal number. Then in lines 265–277 the digits of decimal number are transformed into ASCII codes and are written in place of variable's value in the nearest ("upper") environment. If there is no place enough, REASSIGN.COM displays error message, leaves errorlevel 240 and returns control to command interpreter. If place is sufficient, execution returns from subroutine to final section 3, where a copying subroutine is called for from line 14C. It appends written value with spaces (if needed) and copies it into underlying environments. Thus execution of function 1 terminates.

Execution of function 2 begins from its entrance point 029Ah in section 9. Inputted characters are sequentially accepted and written in place of variable's value in the nearest ("upper") environment. Input by means of INT 16\AH=10h (in line 29C) and display via INT 29 (in line 297) are not subjected to DOS's I/O redirections. Commands in lines 28D–29B patch the place of preceding character with a space, when user presses the BackSpace key. REASSIGN.COM terminates input cycle after ENTER or ESC keystroke. After ESC keystroke commands in lines 2CD–2D0 set flags so that new value is not copied into underlying environments. REASSIGN.COM exits, leaving errorlevel 255 and preserving variable's former value. After ENTER keystroke a return from subroutine and all final operations are performed just as it was described above for function 1. The variable gets a new value, and execution comes to end leaving errorlevel 000.

Execution of function 3 starts from its entrance point 02DCh in section 11. Number of current disk is requested with INT 21\AH=19h, converted into ASCII code of disk's lettername and written in place of variable's former value. If there is a place for at least one more character, then letter-name is appended with a colon.

As far as current disk certainly exists, errorlevel for function 3 is charged with another mission : the determination of the number of floppy drives. That number must be known to adaptive loading procedures, because in computers having one floppy drive, all requests to other floppies are automatically readdressed by MS-DOS to the single drive A:. Floppy problem is complicated further by the possibility of floppy drive emulation, which is not reflected in CMOS memory data.

In order to determine actual number of floppy drives the commands in lines 2F1–30C appeal to DDT tables (A.03-2) for information about correspondence between logical disks and physical drives. This information enables to count actual disks, ignoring repeated references to the same physical drive. After that execution returns from subroutine to final section 3. Having accomplished function 3, REASSIGN.COM leaves errorlevel value, which helps to define proper disks testing order (example in 9.09-02).

More detailed explanation of each command is given in commentaries, complementing each line after a semicolon.

It's worth paying attention to modular structure of REASSIGN.COM utility. Each function is performed by a separate subroutine, independent from other functions. Moreover, there are 3 unfilled positions (16D, 16F, 171) in list of entrance point addresses, so that you may add entrance points for your own subroutines. But before updating REASSIGN.COM, you have to bother about proper typesetting of the current assembler text. It is not as simple as texts in preceding part 9.05. You shouldn't dare to execute at once that file, which will be created by DEBUG.EXE in response to the text you have typed before this text is thoroughly verified.

Some practical recommendations, given in following part 9.07, will help you to detect errors in typesetting, to test executable files and to avoid undesirable consequent complications.

Note 1: if there is any synonymous variable in some underlying environment, then REASSIGN.COM will assign new value to that variable too, and this new value may be truncated according to length of former one. Repeated usage of one name in different contexts should be avoided.

9.07 Some advices on checking and testing edit

Ordinary practice of verifying assembler texts includes listing analysis, correction of revealed errors and following step-by-step tracing. Error correction is necessary anyway, but user can't expect considerable help from DEBUG.EXE debugger: it doesn't reveal nonsyntactic errors and doesn't provide automatic linking, as more perfect assemblers do. Despite DEBUG.EXE debugger's obvious drawbacks, in some circumstances it has no suitable alternatives. This happens in uncertainty conditions and also when debugging affects essential functions or data structures of either DOS or BIOS. Though debugger's capabilities are limited, nevertheless these capabilities may be used effectively. Examples of debugger's capabilities usage are shown below in part 9.07.

9.07-01 Listing analysis edit

Presence of errors should be assumed in any assembler text one has typed. When DEBUG.EXE performs commands, constituting assembler text, it displays listing. Errors, which are found by DEBUG.EXE, are marked in that listing. Since listing scrolls over the screen rapidly, error marks can easily be missed. In order to examine the shown part of listing more attentively, you may suspend execution, pressing the PAUSE key. After that any other keystroke resumes execution, which then may be suspended at desired moments as many times as you will. Manipulations with PAUSE key were sufficient in obsolete computers, but modern computers shift listing too rapidly. Therefore, listing may be examined more conveniently later, after it is redirected and written into a file, for example :

Debug.exe < Reassign.scr > Listing.txt

Stored file LISTING.TXT may be printed or looked through with any viewer or editor program. A part of REASSIGN.COM program's (9.06) listing is shown in fig. 6. In LISTING.TXT all the errors, detected by DEBUG.EXE, are pointed at from the following line with a word ^Error (preceded by a caret pointer). The caret points at that character in preceding line, which can't be interpreted by DEBUG.EXE. Most often this is sufficient for understanding how the error should be corrected. A caret in fig. 6 points at an error in preceding line: indeed, there is "imp" typed instead of "jmp". Sometimes the caret points at the end of preceding line or at the semicolon sign, where a comment starts. This happens, when some required element in command specification is absent. Which particular element is absent – this can be cleared up from chapter 7, presenting all forms of machine command specifications, acceptable for DEBUG.EXE.

 
Fig. 6

In any case a line with an error is not assembled, all offsets below become shifted, and after a single error all following addressing goes wrong. Registered errors must be corrected before any further actions are taken.

Informative listing enables to reveal even those errors, which are not recognized by DEBUG.EXE. Therefore, you have to prepare a sufficiently informative assembler text : correct offset values should be specified in comments, preferably at every line. Almost each line of assembler texts, presented in parts 9.06, 9.08 and 9.10, includes a comment, where correct offset of corresponding machine command is specified just after semicolon sign. Correct offset should be compared with actual offset, displayed in listing at start of each line. Mismatch of these offsets signifies presence of an error in some preceding line. In fig. 6 compared offsets are different from line 147 and on, therefore an error should be expected in preceding line at offset 144. Indeed, comparison of an assembler command at offset 144 in fig.6 with the same line in original text reveals the error: command "call [BX+005]" has been typed instead of "call [BX+0105]". DEBUG.EXE hasn't registered this error. Similar errors emerge during typing of data and textual messages in assembler texts. In any case offsets mismatch should induce a search for its reason and finally must be corrected.

Addresses inside machine commands also may contain errors, which are not detected by DEBUG.EXE. If a comment in assembler line begins with an asterisk, hence this assembler command includes a target address. It must be equal to actual address of first byte in that machine command or in that data block, which have the same correct address specified in a comment. In order to make visual search for target lines easier, comments to these lines are preceded with equality sign. Besides that, separate lines with comments just under a header of each section specify positions of commands with target addresses pointing to this section.

The last important offset to be checked is the one marking that empty line, which forces DEBUG.EXE to exit its assembler mode of operation. In assembler text, presented in part 9.06, this empty line is the 8th from the end. As far as machine codes of ordinary programs are written starting at offset 100h, offset of empty line must be exactly 100h greater, than the whole length of assembled program. If you want to save assembled code in a file, you have to preset the CX register with file length value just 100h bytes less than the actual offset returned in response to the empty line. If you prosecute another aim – to detect possible offset shift errors, then you have to compare the returned offset of empty line with file length value, preset in register CX. In particular, in the third line from the end of assembler text, presented in part 9.06, the CX register is preset with hexadecimal number 03E0h. Hence, in listing the offset value 04E0h, returned in response to the empty line, will signify absence of offset shifts up to the last processed assembler command.

9.07-02 Interactive debugging opportunities edit

When effect of some command or of an interrupt handler needs to be clarified, it is not difficult to type 5–7 lines and to force DEBUG.EXE to execute these command lines at once. But long successions of command lines cannot be composed so easily. You'll be compelled to prepare long successions of commands as textual command files, which then are to be sent to the interpreter. For DEBUG.EXE the only way to accept command files is via input redirection. When input is redirected, DEBUG.EXE doesn't accept commands from keyboard, and all advantages of interactive debugging become lost. This is usually taken for granted as having no alternatives. But this opinion is wrong.

Via input redirection the DEBUG.EXE debugger is ready to accept and to execute those commands, which will cancel input redirection and restore debugger's interactive capabilities. Required succession of commands may look as follows :

CS:                       ; Restore JFT contents for
mov word ptr [0018],0101  ;          the program under test
mov          AH,62        ; A query for segment address
int          21           ;           of debugger's own PSP
mov          DS,BX        ; Restore JFT contents
mov word ptr [0018],0101  ;                for the debugger
int          20           ; Return control to debugger

The first two command lines restore references to the first SFT table entry in cells of JFT table (note 3 to A.07-1) at offsets CS:0018 and CS:0019. These cells correspond to STDIN and STDOUT channels, and appeals to the first SFT table entry (A.01-4) activate the CON device driver. Hence the performed substitution restores normal interaction with display and keyboard for commands of the program under test. The same operation may be done by INT 21\AH=46h function (8.02-48), but it isn't applied here, because in this particular case the performed substitution is not enough.

DEBUG.EXE doesn't obey to references in that copy of its JFT, which is formed for the program under test. Changes also must be applied to original JFT – to that one, which is formed by COMMAND.COM interpreter inside debugger's own PSP. Therefore, in the 4th line of presented example a call for INT 21\AH=62h (8.02-73) function returns segment address of that original PSP in BX register. In the 5th and 6th lines of the presented example this segment address is used in order to make the same substitution in original debugger's JFT. Thus all preparations are made for restoration of normal interaction between debugger and the user. The last peculiarity is that control is returned to DEBUG.EXE not with RET command (not as in examples in part 9.02), but with a call for INT 20 handler (8.02-01). Unlike the RET command, the INT 20 handler leaves stack pointer in its original state, normal for interactive operation mode.

In order to avoid confusion with commands of the program under test, the proposed commands may be appended to redirected command file in a form of machine codes. These codes can be arbitrary located in any free memory space. Let's assume that memory after offset F00h is available and is certainly free. Then the lines which should replace ordinary last commands "w" and "q" in command file, will look like this:

e F00 2E C7 06 18 00 01 01 B4 62 CD 21 8E DB C7 06 18 00 01 01 CD 20
g=F00

If a command file with these two final lines is sent to DEBUG.EXE via input redirection, then all preceding command lines will be executed as usual, but after that interactive operation mode of DEBUG.EXE will be restored (instead of control transfer to COMMAND.COM interpreter). An obvious advantage of a return to interactive operation mode is that there is no more need to determine beforehand the length of a file, which is to be saved. Actual length of assembled code is shown in listing, and required length value may be then (post factum) inputted by the user into CX register from command line. One more advantage is that there are no more restrictions (note 1 to 6.05) on testing of commands, which appeal to STDIN and STDOUT channels. At last, there is no more need to save the processed program in a file after each correction: now that's enough to insert corrections just into original assembler text.

The mentioned advantages are especially important, when a program is composed of several sequentially complemented parts. Sequential debugging promotes early detection of non-syntactic errors and makes emending of their consequences easier. Advantages of sequential modular composition have evinced themselves to full extent during preparation of those programs, which are presented completed in articles 9.06, 9.08, 9.10.

9.07-03 Batch-file control over tests edit

When after errors correction the last listing reveals no more errors, then it is time to cope with all the rest mass of errors in course of testing.

That executable file, which is produced at the last assembling iteration, is usually too raw to be launched at once. First it should be conveyed to debugger. DEBUG.EXE can accept a file just from command line, as it is shown in introduction article to part 6.05. Nevertheless, there are some reasons to prefer debugging arrangement by means of a specially prepared batch file. Batch file enables you to get rid of tedious retyping command lines anew each time you will need to repeat the test. Next, batch file always provides common environment. Besides that, batch file can specify auxiliary functions, making test results more informative.

General composition of test batch file includes preparation part, main test execution part and a final part, where test results are analyzed and displayed. Particular implementation of each part, of course, must reflect specific features of the program under test. Let's consider an example of a test batch file, written especially for testing the 2nd function of REASSIGN.COM utility (9.06):

@echo off
set input=22222
echo Original value of input=%input%
Debug.exe Reassign.com input < Reassign.scr
set E=
set Z=00
set N=
:ErrCycle
for %%Y in (0 1 2 %N%) do if errorlevel %E%%%Y%Z% set E=%E%%%Y
if %Z%"==" goto OUT
if %Z%"==0" set Z=
if %Z%"==00" set Z=0
set N=3 4 5
if not %E%"==2" if not %E%"==25" set N=%N% 6 7 8 9
goto ErrCycle
:OUT
echo Errorlevel is %E%
echo New value of input=%input%
pause

The first three lines in proposed batch file represent preparation part ; the fourth line, the main test execution part. It should be taken into account that during debugging you may easily lose orientation in successions of processed commands. Therefore, a source program listing with comments should be prepared beforehand.

In the 4th line DEBUG.EXE gets a group of parameters from command line and, besides that, accepts a command file via input redirection. Here the main purpose of command line parameters is their participation in filling PSP for the program under test. In the simplest case file REASSIGN.COM, specified in command line, may be empty at all, but owing to presence of its name in command line this name together with following parameters, if there are any, will be written into dedicated PSP fields (A.07-1). Of course, the file REASSIGN.COM may be not empty, and then its code will be written by debugger into memory starting from address CS:0100h and on. This feature is convenient for preparation to debugging of relatively large executable files.

Input redirection in the 4th line will force DEBUG.EXE to assemble commands from file REASSIGN.SCR. Translated machine commands will be written into memory starting from arbitrary specified address, either overwriting or complementing that code, which has been initially copied from REASSIGN.COM file. Therefore, file REASSIGN.SCR may contain only those parts of assembler text, which need further debugging. The latter doesn't relate to assembler texts, presented in this book: there is no sense to divide complete texts. In any case the assembler text, sent via redirection, must end with those two command lines, proposed in article 9.07-02. These commands will force DEBUG.EXE to escape from redirection into interactive operation mode.

Waiting for commands from keyboard, DEBUG.EXE presents its prompt — a blinking underscore — and thus invites the user to act further. It's better to begin from testing program parts (or subroutines) with commands "G" (=Go, 6.05-07) and "P" (=Proceed, 6.05-14). Testing program parts is less difficult, than testing step-by-step. Special attention should be paid to states of flags and registers in critical points — for example, in points of return from subroutines. If some part of program returns wrong results, it should be subjected to step-by-step debugging. Even if some part of program causes hanging, it wouldn't take much time to reset computer and to launch the same test batch file anew. Testing step-by-step enables to cope with the most stubborn bugs.

When debugger's session is to be closed in order to make a correction, then you may enter the "Q" (= Quit) command and then press ENTER. After such termination DEBUG.EXE always leaves zero errorlevel. But sometimes it is important to check errorlevel, returned by the program under test. In the latter case debugger's session should be terminated by a call for DOS's INT 21\AH=4Ch function (8.02-55), just as the program under test must be terminated, when it is executed beyond the debugger's shell. After termination of debugger's session the commands in final part of test batch file will be executed. Results of REASSIGN.COM program execution are expressed via errorlevel code and via returned value of some environmental variable. In order to catch the returned errorlevel the program under test may be executed inside a shell of command interpreter COMMAND.COM, launched with undocumented /Z parameter (6.04). However, this shell doesn't convey values of environmental variables. Therefore, lines 5–16 of the proposed test file contain a procedure of errorlevel determination. Then commands in lines 17–18 display returned values of both errorlevel and of that variable, which was to be changed by REASSIGN.COM utility. The last line with PAUSE command prevents the displayed results from being hidden under file manager's panels. In order to check some other function of the program under test, you have to change parameters in preparation part of test batch file. Sometimes command line parameters in main test execution part need to be changed too. Replacement of parameter's values may be performed via dummy parameters of test batch file. But in presented examples dummy parameters are not employed for simplicity reasons. It is assumed that all necessary changes can be made just in batch file text by means of an editor program. When batch file is saved with all necessary changes, it is again ready to be used for testing the next function.

Generally, every program must be subjected to three types of test series. Test series of the first type check all functions of the program in normal conditions. Test series of the second type check program's response to probable abnormal conditions: invalid specifications, absence of required parameters, inadmissible data values, etc. Test series of the third type are for those programs, which perform direct manipulations with hardware : such programs must prove their functionality in all relevant hardware configurations. Naturally, particular composition of tests depends on program's mission and on scope of challenges, but principle is the same for all programs, even as small as proposed REASSIGN.COM utility. When your program passes all tests successfully, then your mission concerning this program may be considered completed.

9.08 Let's try to assemble a driver edit

When DOS is used to explore some computer for the first time, it is desirable to have a fixed letter-name assigned to RAM-disk, prepared for DOS relocation. This can be achieved by means of a driver, which declares a necessary number of nonexistent (dummy) disks so that DOS is forced to present the desired letter-name to RAM-disk.

As far as I know, three attempts to create such driver have been a success. However, freeware versions of such drivers don't hide dummy disks, and the one version which does hide is not a freeware. Therefore, one more (4th) attempt of my own is presented below. It doesn't follow known solutions. It is undertaken especially for making driver's peculiarities more clear and easy to understand. The proposed driver is tiny: 503 bytes total. Let it be named SKIPDSK.SYS.

SKIPDSK.SYS driver is produced by debugger DEBUG.EXE as a result of command sequence execution. This command sequence should be written into command file SKIPDSK.SCR and should be sent to debugger via input redirection :

DEBUG.EXE < SKIPDSK.SCR

The SKIPDSK.SCR file should be prepared by means of editor program (as described in introduction article to chapter 9) according to the text presented below. Verbal comments may be omitted, but proper offset values in comments should be preserved in order to make further debugging and testing easier.

a 0000
      ;************* Section 1: driver's header
         ; 000 Place for next driver's address
dw           FFFF,FFFF
         ; 004 Driver's attributes (A.05-2)
dw           2202
         ; 006 Strategy routine entrance point
dw           0031
         ; 008 Interrupt routine entrance point
dw           006E
         ; 00A Number of disks, accessed from 058, 113
db           00
         ; 00B An identifier (7 following bytes)
db           53,6B,69,70,44,73,6B
      ;************* Section 2: data
         ; 012 Request's offset, accessed from 032, 073
         ; 014 Request's segment, accessed from 037
dw           0000,0000
         ; 016 BPB data (A.03-4), referred at 093 - 0C1
db           00,02,FF,01,00,01,40,00,00,22,F0,01,00
db           12,00,01,00,01,00,00,00,00,00,00,00
         ; 02F CDS, from lines 03D, 04D, 052, 087, 0D8
dw           FEFE
      ;******* Section 3: TSR part of strategy routine
         ; 031 - specified at offset 006
CS:                        ;=031 Strategy routine entrance
mov          [0012],BX     ;*032 Store offset of the request
CS:                        ; 036         data block (A.05-3)
mov          [0014],ES     ;*037 Store its segment
retf                       ; 03B Return far to DOS
      ;*********** Section 4: response to media check
         ; 03C - target for jump from line 084
         ; 05C - target for jump from line 06C
CS:                        ;=03C Is the shift for dummy CDS
cmp byte ptr [002F],FE     ; 03D      record ready in CS:002Fh?
jnb          008E          ;*042 Return back, if no
mov          AH,52         ; 044 Call for List-of-Lists address
int          21            ; 046 Now the address is in ES:BX
ES:                        ; 048 Load address of the 1st CDS
les          BX,[BX+16]    ; 049     record into the ES:BX pair
CS:                        ; 04C Calculate offset of
add          BX,[002F]     ;*04D     the first dummy CDS record
CS:                        ; 051 Mark FFh at 002Fh means that
mov byte ptr [002F],FF     ;*052     media check has been done
CS:                        ; 057 Read the number of
mov          AH,[000A]     ; 058     dummy CDS records (units)
cmp          AH,01         ;=05C Compare the number with 01h
jb           008E          ;*05F Exit, if there are no units
ES:                        ; 061 Else write "disabled disk"
mov word ptr [BX+43],0000  ; 062      attributes into the CDS
add          BX,0058       ; 067 Calculate offset for next CDS
dec          AH            ; 06A Get number of remaining cycles
jmp          005C          ;*06C Repeat the cycle for next CDS
      ;*********** Section 5: TSR part of interrupt routine
         ; 06E - specified at offset 008
         ; 08E - target for jumps from 042, 05F, 082, 13C
pushf                      ;=06E Interrupt routine entrance
push         ES            ; 06F Save states
push         BX            ; 070      of registers and flags
push         AX            ; 071
CS:                        ; 072 Load request block address
les          BX,[0012]     ;*073       into ES:BX pair
ES:                        ; 077 Set "unknown media" status
mov word ptr [BX+03],8007  ; 078        to be returned
ES:                        ; 07D Check operation code
cmp byte ptr [BX+02],01    ; 07E       in request data block
ja           008E          ;*082 Jump to exit, if above 01h
jz           003C          ;*084 Go to media check, if 01h
CS:                        ; 086 If below 01h, then is it
cmp byte ptr [002F],FE     ;*087     the 1st initialization?
jz           00C3          ;*08C Go to initialize, if yes
pop          AX            ;=08E Else restore states
pop          BX            ; 08F        of registers
pop          ES            ; 090
popf                       ; 091 Restore flags
retf                       ; 092 Return far to DOS
      ;********** Section 6: array of BPB offsets for disks
         ; 093 - mentioned in lines 11E, 12A
dw           0016,0016,0016,0016,0016,0016,0016,0016
dw           0016,0016,0016,0016,0016,0016,0016,0016
dw           0016,0016,0016,0016,0016,0016,0016,0016
      ;********* Section 7: interrupt routine, non-TSR part
         ; 0C3 - target for jump from line 08C
push         DX            ;=0C3 Save states of
push         DS            ; 0C4       registers in stack
push         SI            ; 0C5
cld                        ; 0C6 Clear direction flag
ES:                        ; 0C7 Get in AH the disk number,
mov          AH,[BX+16]    ; 0C8        suggested by DOS
mov          AL,41         ; 0CB Convert the disk number into
add          AL,AH         ; 0CD       disk's letter-name in AL
CS:                        ; 0CF Replace with this letter-name
mov          [01E0],AL     ;*0D0       the former one in message
mov          AL,58         ; 0D3 Calculate in AX the shift for
mul          AH            ; 0D5    the first dummy CDS record
CS:                        ; 0D7 Now shift for the first dummy
mov          [002F],AX     ;*0D8    CDS record is in CS:[002F]
      ;********* Section 8: reading of disk's letter-name
         ; 0E2 - target for jump from line 0E7
         ; 0E9 - target for jump from line 0EE
ES:                        ; 0DB Load in DS:SI a pointer to
lds          SI,[BX+12]    ; 0DC       command-line arguments
mov          DX,013F       ;*0DF Offset of 1st error message
lodsb                      ;=0E2 Load a character into AL and
cmp          AL,20         ; 0E3    arrange a cycle searching
jb           00F8          ;*0E5        for first space after
ja           00E2          ;*0E7                driver's name
lodsb                      ;=0E9 Load a character into AL and
cmp          AL,20         ; 0EA    arrange a cycle searching
jb           00F8          ;*0EC       for valid letter after
jz           00E9          ;*0EE              the first space
cmp          AL,43         ; 0F0 Is the letter less than C: ?
jb           00F8          ;*0F2 If yes, jump to section 9
cmp          AL,59         ; 0F4 Is the letter less than Y: ?
jbe 0102                   ;*0F6 If yes, jump to section 10
      ;*********** Section 9: message display part
         ; 0F8 - target for jumps from 0E5, 0EC, 0F2, 10C
push         CS            ;=0F8 Prepare segment address in DS
pop          DS            ; 0F9          for display function
mov          AH,09         ; 0FA Call for a string
int          21            ; 0FC          display function
mov          AL,00         ; 0FE 0 disks to be set after error
jmp          010E          ;*100 Go to prepare return to DOS
      ;*********** Section 10: calculation of TSR part size
         ; 102 - target for jump from line 0F6
         ; 10E - target for jump from line 100
inc          AL            ;=102 Get RAM-disk's letter-name
mov          DX,01C3       ; 104 Offset of 2nd error message
CS:                        ; 107 Calculate number of dummy
sub          AL,[01E0]     ;*108     disks to be established
jb           00F8          ;*10C If below 0, display a message
ES:                        ;=10E Write number of disks into
mov          [BX+0D],AL    ; 10F       the request data block
CS:                        ; 112 Duplicate the number
mov          [000A],AL     ; 113        into driver's header
mov          AH,00         ; 116 Calculate offset for a
cmp          AL,00         ; 118    pointer to 1st byte past
jz           0121          ;*11A       driver's TSR part
shl          AX,1          ; 11C         according to formula
add          AX,0093       ;*11E                (2*AX + 093h)
      ;********* Section 11: filling the request data block
         ; 121 - target for jump from line 11A
ES:                        ;=121 Write the pointer's offset
mov          [BX+0E],AX    ; 122      into request data block
ES:                        ; 125 Write segment address for
mov          [BX+10],CS    ; 126        pointer offset at 0Eh
ES:                        ; 129 Offset of array of BPB offsets
mov word ptr [BX+12],0093  ;*12A       for each installed disk
ES:                        ; 12F Write segment address for
mov          [BX+14],CS    ; 130       the array of BPB offsets
ES:                        ; 133 Write "happy end"
mov word ptr [BX+03],0100  ; 134        status word for return
pop          SI            ; 139 Restore states
pop          DS            ; 13A          of registers
pop          DX            ; 13B
jmp          008E          ;*13C Go to return to DOS
      ;*********** Section 12: error messages
         ; 13F - 1st error message, referred at 0DF
db           0D 0A "SkipDsk: diskletter isn" 27 "t found"
db           20 "or out of range" 0D 0A
db           "Example:" 0A "device=A:\SkipDsk.sys Q:"
db           0D 0A 09 09 09 "Q: - diskletter (C: - Y:)"
db           20 "to be skipped" 0D 0A 0A 24
         ; 1C3 - 2nd error message, referred at 104
db           0D 0A "SkipDsk: diskletters below" 20
         ; 1E0 - letter-name, accessed from 0D0, 108
db           "A: are assigned yet" 0D 0A 0A 24
         ; 1F7 - checkpoint, end of driver's code

m 0000 L01F7 0100
n SkipDsk.sys
rBX
0000
rCX
01F7
w
q

Unlike ordinary executable files, drivers are loaded starting from offset 0000h, i.e. without reserved space for PSP (A.07-1). Because of this reason start command for assembling drivers must be not "A 100", as for ordinary programs, but "A 0000". If start address is specified otherwise, a muddle occurs in displayed target offsets.

One more specific feature is that DOS drivers have two entrance points, and neither of these coincides with driver's code start address. The first entrance point, related to strategy routine, is used to accept a request and to initiate its execution by the corresponding device (a printer, disk drive, etc.). The second entrance point, related to interrupt routine, is used to gather the result and to form a response to the accepted request. Time in between is spent in parallel by DOS and by physical devices, each performing its own job. Entrance points are announced in driver's header: for strategy routine at offset 0006h, for interrupt routine at offset 0008h. These and other elements of driver's header are shown in table A.05-1.

Resident part of strategy routine in SKIPDSK.SYS driver is represented by section 3 of assembler text. It is simple and just writes segment address and offset of request data block (A.05-3) into a prepared memory cell. Strategy routine of SKIPDSK.SYS driver has no non-resident part.

All request-specific operations are performed by interrupt routine, which starts in section 5 of assembler text. Section 5 begins with saving states of flags and registers — this is also a specific responsibility of any driver. Then a code of requested operation is checked. As far as SKIPDSK.SYS driver "serves" dummy disks only, requests with operation codes greater than 01h must always cause a return to DOS with "invalid media" status byte. But requests for operations 00h and 01h invoke execution of commands, presented in sections 7–11 or in section 4.0.

The first request to driver is always for initialization with operation code 00h. Since initialization is requested only once, the corresponding sections 7–11 are beyond driver's resident part. Commands in section 7 prepare data for further use, commands in section 8 read letter-name of the disk to be skipped from command line, commands in section 9 are always ready to display an error message. Templates for error messages are prepared yet in section 12. Commands in section 10 calculate number of dummy disks and actual length of driver's resident part. Commands in section 11 fill request block with data, which should be returned to DOS. According to these data DOS corrects its DPB (A.03-1) and CDS (A.03-3) tables so that disk's letter-names up to the one specified appear busy. When later a RAM-disk driver will be initiated, it will be given the next free letter-name, which is thus predetermined by SKIPDSK.SYS driver.

While assigning a letter-name to RAM-disk, DOS must consider the declared dummy disks valid. But afterwards their valid status confines further letter-names assignment. A mixture of actual disks and dummies causes confusion. Therefore, the next task for SKIPDSK.SYS driver is to disable dummy disks. For this purpose SKIPDSK.SYS must be activated once more.

For the second time SKIPDSK.SYS is activated by a request for "media check" operation (code 01h), because it precedes all other requests, addressed to removable disks. Besides that, DOS automatically requests media check operation, when interpretation of CONFIG.SYS file completes. In response to media check request the SKIPDSK.SYS driver executes commands in its resident section 4.0. These commands determine address of the first dummy CDS record, and then perform a cycle of writing an invalid attribute word 0000h into all dummy CDS records. After that all "dummy" disks become hidden and are considered invalid. Their former letter-names may be assigned to CD-ROMs and to network drives.

When former letter-name of a dummy disk is given yet to another driver, then writing of attribute word 0000h into the same CDS record must be prohibited. Therefore, one of commands in 4th section writes a mark FFh into a memory cell at offset 02Fh. Presence of this mark is checked each time when interrupt routine of SKIPDSK.SYS driver is called for. Owing to this check repeated requests to SKIPDSK.SYS driver can't impair proper access to other disks, which have got former letter-names of dummy disks.

More detailed explanation of particular operations can be found in comments to lines of assembler text file SKIPDSK.SCR.

As in most assembler texts, it is important to note an empty line, the 9th from the end. It forces DEBUG.EXE to exit assembler mode of operation and therefore must be present. The next line with "M" command (6.05-11) moves the whole assembled code 100h bytes further, thus making PSP area free. This is necessary in order to specify driver's name, which is to be written just into PSP area and is announced in the following line by "N" command (6.05-12). Concluding lines of SKIPDSK.SCR file prepare length of driver's file in BX:CX registers and write the SKIPDSK.SYS driver to current disk.

Produced file SKIPDSK.SYS shouldn't be considered valid until its listing is thoroughly checked, as it is described in article 9.07-01. The last actual offset in listing is given in response to the mentioned empty line — the 9th from the end of text. As far as assembling started from address CS:0000h, this last offset must be 01F7h, exactly the same as total driver's length, specified in the 8th and 3rd lines from last. If listing reveals no errors, there is a chance for SKIPDSK.SYS to pass tests successfully.

While arranging tests it should be taken into account that for reasons of simplicity and compact size the capabilities of SKIPDSK.SYS driver are limited. It can't affect allotment of disk's letter-names, which are already assigned. It can't accept letter-name specification, preceded with a slash. It can't tolerate tabulation code (09h) instead of a space (20h) in its command line. Finally, it can't work under MS-DOS versions earlier than 4.0.

In practice a necessity may arise to reassign "dummy" disk's letter-names before interpretation of CONFIG.SYS file is finished. For this purpose a media check request to SKIPDSK.SYS driver should be provoked earlier, for example, by the following commands :

devicehigh=\DOS\DRV\SkipDsk.sys Q:
devicehigh=\DOS\DRV\Ramdrive.sys 2400 /E
install=\Command.com nul /low /f /c vol Q:

In this example the letter-name Q: will be assigned to the last dummy disk, and RAMdisk will be given the next letter-name R:. In the last line an explicit appeal to "dummy" disk Q: provokes media check request, and since that moment all "dummy" disk's letternames become free for reassignment by software, which may be loaded afterwards by INSTALL= or INSTALLHIGH= commands. One more similar example of SKIPDSK.SYS driver usage is presented in article 9.09-01.

9.09 Exploratory configuration files edit

Versions of configuration files CONFIG.SYS and AUTOEXEC.BAT suggested in this article implement several loading modes. Besides MS-DOS 7 loading in an ordinary way, it can be relocated to RAM-disk or to a HDD, and also can be loaded without drivers, as it should be done for RAM testing and for reprogramming BIOS memory chips. Main advantage of proposed configuration files is that a choice of the most suitable loading mode is not necessarily "blind", it can be based on results of preliminary tests. If you prefer MS-DOS 7 relocation onto a RAM-disk, its size choice will be based on results of XMS-memory investigation. If you prefer MS-DOS 7 relocation onto a HDD, you will be informed in advance about which disks are available, about total size and usage percent for all writable disks.

When operating system loading procedures are not completed, then hardware tests can't be performed just as though operating system were loaded yet. Therefore, here disk's tests can't be arranged exactly as in file DISK.BAT (9.03-02). Because of the same reason standard DOS's means are not enough, some other drivers and utilities have to be employed. On the other hand, during OS loading some simplifying assumptions can be adopted. It is assumed that the current disk is the one used to load MS-DOS 7, that the directory structure on this disk is known, and it is known also, which loading operations are not performed yet at each stage. Due to the last assumption, in particular, there is no need to identify RAM-disks and CD/DVD-ROM drives.

Suggested configuration files are most suitable for loading MS-DOS 7 on AT-compatible computers with unknown hardware configuration. Loading adaptation capabilities are especially essential for repairing services.

9.09-01 Multi-alternative CONFIG.SYS file edit

This version of CONFIG.SYS file presents 5 loading alternatives listed in [menu] section. Several of these alternatives include operations, which can't be performed by standard DOS's means. In particular, an opportunity to adapt RAM-disk's size is implemented by a freeware version 2.42 of TDSK.EXE driver (5.05-02). Suitable replacements for this driver are not known. Besides that, the SKIPDSK.SYS driver (9.08) is used, which you may make yourself. Potentially JDRIVE.SYS driver from JAMSOFT can be used instead, but it is not free.

Data and parameters for other employed drivers can be found in chapter 5. These drivers either are included in WINDOWS-95/98 release or have close equivalents, therefore their choice is not critical. Naturally, each replacement implies command line parameters correction according to driver's requirements. Text of proposed CONFIG.SYS file is presented below.

[menu]
numlock off
menuitem=L047, User-configurable real mode
menuitem=L048, User-configurable V86 mode
menuitem=L029, Quick boot in real mode
menuitem=L031, Quick boot in V86 mode
menuitem=L104, Real mode without drivers
menudefault=L029,20

[L047]
device=\DOS\DRV\Himem.sys /v
device=\DOS\DRV\Umbpci.sys
include=L104
country=007,866,\DOS\DRV\Country.sys
devicehigh=\DOS\DRV\Dblbuff.sys
devicehigh=\DOS\DRV\Ifshlp.sys
devicehigh=\DOS\DRV\Setver.exe
devicehigh=\DOS\DRV\Dvs.sys /D:CD001
devicehigh=\DOS\DRV\Skipdsk.sys Q:
device=\DOS\DRV\Tdsk.exe 0
install=\Command.com nul /low /f /c vol Q:
installhigh=\DOS\DRV\Shsucdx.com /D:?CD001 /L:N /~+ /R /Q
installhigh=\DOS\DRV\Ctmouse.exe
installhigh=\DOS\DRV\Keyrus.exe

[L048]
device=\DOS\DRV\Himem.sys /v
device=\DOS\DRV\Jemm386.exe X=TEST noems verbose
include=L104
country=007,866,\DOS\DRV\Country.sys
devicehigh=\DOS\DRV\Dblbuff.sys
devicehigh=\DOS\DRV\Ifshlp.sys
devicehigh=\DOS\DRV\Setver.exe
devicehigh=\DOS\DRV\Dvs.sys /D:CD001
devicehigh=\DOS\DRV\Skipdsk.sys Q:
device=\DOS\DRV\Tdsk.exe 0
install=\Command.com nul /low /f /c vol Q:
installhigh=\DOS\DRV\Shsucdx.com /D:?CD001 /L:N /~+ /R /Q
installhigh=\DOS\DRV\Ctmouse.exe
installhigh=\DOS\DRV\Keyrus.exe

[L029]
include=L047

[L031]
include=L048

[L104]
accdate C- D- E- Fdos=
high,umb,noauto
buffershigh=30,0
fileshigh=30
lastdrivehigh=Z
fcbshigh=1,0
stackshigh=9,256

[common]
shell=\Command.com \ /E:2016 /L:511 /U:255 /p

Text of CONFIG.SYS file starts with [menu] section. There are 5 items, each having an explicit header and a name code (L029 – L104). Choice of an item with some name code initiates interpretation of commands in synonymous section of CONFIG.SYS file.

Besides this, name code of selected item is automatically assigned as a value to CONFIG environmental variable; this value is used later in order to select corresponding part of other configuration file — AUTOEXEC.BAT. For this purpose items in menu are named after label marks in AUTOEXEC.BAT file (9.09-02).

Choice of menu items L029, L031, L047, L048 causes loading of almost the same sets of drivers, defined in sections [L047] and [L048]. The only difference between these sections is in their line 2: driver UMBPCI.SYS (5.04-04) gives access to upper memory in CPU's real mode, whereas driver JEMM386.SYS (note 4 to 5.04-02) gives similar access in V86 mode. Both sections L047 and L048 include loading of XMS-memory driver, driver for access to CD/DVD-ROMs, "mouse" driver and some others. Note that TDSK.EXE driver for arranging a RAM-disk, unlike most others, is loaded into conventional memory, and that RAM-disk's size is not specified here. If arranging of a RAM-disk will be considered expedient, its size should be specified later, during interpretation of AUTOEXEC.BAT file (9.09-02).

Sufficient differences between configurations L029–L048 also evince themselves later, during interpretation of AUTOEXEC.BAT file, except one configuration, defined by menu item L104. Corresponding section [L104] includes DOS settings only, without drivers and TSRs. The last section in CONFIG.SYS file is section [common], which is executed in any case. It loads command interpreter COMMAND.COM.

All paths in lines of CONFIG.SYS file don't include disk's letter-name and therefore are suitable for loading from any disk. A care should be taken about presence of all mentioned drivers and other files in prescribed directories. Path changes are not prohibited, but affect conditions of AUTOEXEC.BAT file execution (9.09-02) and therefore must be made consistent.

Proposed CONFIG.SYS file should be typed as non-formatted text and saved in the root directory of bootable removable media — either a diskette or a flash card, dedicated for loading MS-DOS 7 onto AT-compatible computers with unknown hardware configuration.

9.09-02 AUTOEXEC.BAT file for adaptive loading edit

This version of AUTOEXEC.BAT file implements exploratory and adaptive capabilities of MS-DOS 7. For this purpose the REASSIGN.COM utility is used, which may be assembled by yourself (9.06). Full-functional replacement for REASSIGN.COM utility is not known.

In order to make orientation in AUTOEXEC.BAT file more easy, label marks include corresponding line numbers; for example, label L029 denotes line 29. Besides that, each tenth line is a comment announcing the line number.

Local variables in AUTOEXEC.BAT are named V0–V8. One of them, V8, has no permanent mission. Dedicated missions of the rest local variables are :

V0 – list of disks, selected for being tested
V1 – size of the largest free XMS-memory block
V2 – recommended size of RAM-disk
V3 – candidate target disk for relocation
V4 – current disk, used to boot the PC
V5 – writeability status of the current disk
V6 – list of inaccessible or non-writable disks
V7 – list of writable disks

Text of AUTOEXEC.BAT file version for adaptive loading is presented below.

@echo off
if %1"==J" if not %2"==" goto L%2
prompt $p$g
path ;
path=\DOS\MS7;\DOS\OTH
set V0=C D E F
set V4=33
Reassign.com V4
if errorlevel 2 set V0=%V0% B
rem ============ Line 10 ============
if errorlevel 1 set V0=%V0% A
set comspec=%V4%\Command.com
set V1=11111111
set V2=300
Reassign.com V1
if errorlevel 1 if not errorlevel 2 set V2=5600
if errorlevel 2 if not errorlevel 3 set V2=16000
if errorlevel 3 if not errorlevel 100 set V2=32000
if errorlevel 100 for %%Z in (1 2) do set V%%Z=
rem ============ Line 20 ============
if errorlevel 100 if not %config%"==L104" set config=L047
if not %config%"==L104" goto %config%
for %%Z in (3 5 6 7) do set V%%Z=
for %%Z in (%V0%) do call \Autoexec.bat J 117 %%Z: Q
if %V5%"==F" echo Warning: current disk %V4% is not writable!
if not %V7%"==" echo Writable disk(s): %V7%
if %V7%"==" echo Warning: no writable disks have been found!
goto L104
:L029
rem ============ Line 30 ============
:L031
set V3=
set ramdrive=?:
%V4%\DOS\DRV\Tdsk.exe %V2% /E /M /F:2
if not %ramdrive%"==?:" if not %ramdrive%"==::" set V3=%ramdrive%
if not %V3%"==" if not %V2%==300 goto L086
if not %V3%"==" if %V2%"==300" goto L104
set V1=
echo RAM-disk arranging procedure has failed!
rem ============ Line 40 ============
goto L047
:L042
for %%Z in (1 1 1 1 1 1 1) do echo=
ctty nul
call %V4%\Autoexec.bat J 117 %V3% S
ctty con
:L047
:L048
for %%Z in (1 1 1 1 1 1 1) do echo=
rem ============ Line 50 ============
for %%Z in (5 6 7) do set V%%Z=
ctty nul
for %%Z in (%V0%) do call %V4%\Autoexec.bat J 117 %%Z: V
ctty con
if %ramdrive%"==" set ramdrive=R:
if not %V7%"==" echo Writable disk(s): %V7%
if %V7%"==" echo Writable disks have not been found!
if not %V1%"==" echo Available XMS-memory is %V1% kb
if %V1%"==" echo XMS-memory is unavailable
rem ============ Line 60 ============
echo Select one of the shown keys and then press ENTER:
if not %V7%"==" echo %V7% - set DOS onto the chosen disk
if not %V6%"==" echo %V6% - retry inaccessible disk(s)
if not %V1%"==" echo %ramdrive% - set a RAM-disk for DOS
echo ESC - leave DOS on %V4%, no relocation
echo=
:L067
set V3=2
Reassign.com V3
rem ============ Line 70 ============
if not errorlevel 128 if %V3%"==" goto L067
if errorlevel 128 set V3=
if errorlevel 128 goto L104
set V8=%path%
path %V3%
set V3=%path%:
path=%V8%
if %V3%"==%ramdrive%" if not %V1%"==" goto L031
set V8=N
rem ============ Line 80 ============
for %%Z in (%V6%) do if %V3%==%%Z set V8=F
if %V8%==F goto L042
for %%Z in (%V7%) do if %V3%==%%Z set V8=T
if %V8%==T if %V3%"==%V4%" goto L104
if not %V8%==T goto L067
:L086
ctty nul
for %%Z in (. OTH MS7 VC4) do Attrib -h -r -s %V3%\DOS\%%Z\*.*
for %%Z in (. ..\TEMP OTH MS7 VC4) do md %V3%\DOS\%%Z
rem ============ Line 90 ============
for %%Z in (Autoexec.bat Command.com) do copy /B \%%Z %V3%\DOS /Y
ctty con
if not exist %V3%\DOS\Command.com set V3=
if %V3%"==" echo Relocation attempt has failed!
if %V3%"==" goto L104
echo Copying the following files to disk %V3%
for %%Z in (OTH MS7 VC4) do copy /B \DOS\%%Z\*.* %V3%\DOS\%%Z /Y
set comspec=%V3%\DOS\Command.com
%V3%
rem ============ Line 100 ============
for %%Z in (OTH MS7 VC4) do \DOS\MS7\Attrib +r \DOS\%%Z\*.ini > nul
set V4=%V3%
%V3%\DOS\Autoexec.bat J 104
:L104
if %V3%"==" for %%Z in (%V7%) do set V3=%%Z
if not %V3%"==" if not exist %V3%\Temp\nul md %V3%\Temp
if not %V3%"==" if exist %V3%\Temp\nul set Temp=%V3%\TEMP
if %Temp%"==" echo Warning: the TEMP variable is not defined!
set dircmd= /A /O:GNE /P
rem ============ Line 110 ============
path=%V4%\DOS\OTH;%V4%\DOS\MS7;%V4%\DOS\VC4;%V4%\;%V4%\DOS
%V4%\DOS\OTH\Blue.com
if not %config%==L104 set VC=%V4%\DOS\VC4
for %%Z in (0 1 2 3 4 5 6 7 8) do set V%%Z=
if not %config%==L104 %VC%\Vc.com /TSR /no2E /noswap
goto END
:L117
%comspec% nul /f /c if exist %3\nul cd \DOS
if not exist ..\NUL if %4"==Q" goto END
rem ============ Line 120 ============
if not exist ..\NUL goto L152
%comspec% nul /f /c call %0 J 141 %3 %4
%V4%
if not exist ..\NUL if %3"==%V4%" set V5=
if not exist ..\NUL if %4"==S" goto END
if not exist ..\NUL if not %V7%"==" set V7=%3 %V7%
if not exist ..\NUL if %V7%"==" set V7=%3
if not exist ..\NUL goto END
cd \
rem ============ Line 130 ============
if not %4"==Q" echo Disk %3 is not writable! > con
if %3"==%V4%" set V5=F
if not %4"==S" if not %V6%"==" set V6=%V6% %3
if not %4"==S" if %V6%"==" set V6=%3
if not %4"==S" goto END
echo If disk %3 is write-protected, close protection > con
echo hole in its cartridge. Press any key to continue > con
pause < con > nul
goto END
rem ============ Line 140 ============
:L141
set TEMP=
%3
ver | shift
if not %4"==" goto END
cd %V4%\
if not %3"==V" goto END
echo Disk %2 is writable: > con
dir /a:ARD /-p /v %2\ | %V4%\DOS\MS7\Find.exe "otal d" > con
rem ============ Line 150 ============
goto END
:L152
cd \DOS
set V8=if errorlevel 15 if not errorlevel 16 cd \
if %4"==S" set V8=if errorlevel 20 cd \
%comspec% /f /c for %%Z in ("Label.exe %3trial" "%V8%") do %%Z
if not exist ..\nul if not %4"==S" goto END
if not %4"==S" if not %V6%"==" set V6=%V6% %3
if not %4"==S" if %V6%"==" set V6=%3
rem ============ Line 160 ============
set V8=Disk %3 either is unformatted or has an improper format
if exist ..\nul set V8=Drive %3 probably has no media inside
if %4"==V" set V8=Disk %3 is either unformatted or not inserted
echo %V8% > con
cd \
if not %4"==S" goto END
echo Insert proper media and press any key to continue > con
pause < con > nul
:END

Specific features of this AUTOEXEC.BAT file version begin with a conditional jump in line 2, which enables to execute recursive calls for internal subroutines. But jump conditions are not met, when AUTOEXEC.BAT is interpreted for the first time. Then in lines 3–5 values are assigned to environmental variables PROMPT and PATH. Value of PATH variable is not final; it will be replaced by final value after DOS relocation. The REASSIGN.COM utility is called for the first time in line 8 in order to determine letter-name of the current disk. Errorlevel value, returned by REASSIGN.COM utility, helps to compile a list of disks, selected for being tested. The next time REASSIGN.COM utility is called for in line 15 in order to ascertain availability of XMS-memory and determine size of the largest XMS-block. This time the returned errorlevel value prompts recommended size of RAM-disk, which may be arranged later. If XMS-memory is found unavailable, then in 21st line a value of CONFIG variable will be altered so that certainly unsuccessful attempts to arrange a RAM-disk will be prevented.

An important jump is performed by GOTO command in line 22: it makes further processing consistent with menu item user's choice, defined by value of CONFIG environmental variable. If quick loading has been chosen, then jump leads to labels L029 or L031. There in line 34 the TDSK.EXE driver arranges a RAM-disk of prescribed size and assigns the letter-name of RAM-disk to environmental variable RAMDRIVE. This is just the time to remind that RAMDRIVE variable is defined by the only version 2.42 of TDSK.EXE driver. If earlier versions of that driver or other similar RAM-disk drivers are used (BITDISK.EXE, SRDISK.EXE, XMSDSK.EXE), then fixed value R: should be assigned to RAMDRIVE variable by SET command (3.26). Naturally, opportunities of RAM-disk letter-name adaptation will be lost.

When the prescribed size of RAM-disk is not large enough, then a jump in line 37 leads to termination part without DOS relocation, and RAM-disk will be used exclusively as a place for temporary files. But normally size of RAM-disk is enough to relocate DOS, and then a jump in line 36 leads to label L086 – to DOS relocation part of AUTOEXEC.BAT file.

Relocation part of AUTOEXEC.BAT file is composed so that it can be used for DOS relocation to RAM-disk and to any non-empty physical disk as well. Therefore, relocation procedure begins with removing file's attributes in target directories, because otherwise standard DOS' utilities can't overwrite synonymous files there and even can't determine their presence. Operation in line 89 creates a typical structure of target directories, if it didn't exist before. Then files AUTOEXEC.BAT and COMMAND.COM are copied into directory %V3%\DOS. If copying is a success, then in line 97 all the rest files are copied to their target directories, and new values are assigned to COMSPEC and to V4 environmental variables. Command in line 103 transfers control to a copy of AUTOEXEC.BAT file in target directory %V3%\DOS so that a return to original AUTOEXEC.BAT file wouldn't happen, and execution of the copy starts from its 105th line.

If the user originally has chosen a user-configurable loading alternative, then a jump from line 22 leads to labels L047 or L048, where disks exploration procedure starts. Disks exploration is performed by subroutine L117, which is a part of the same AUTOEXEC.BAT file. It is called recursively from cycle FOR in line 53, separately for each disk under test. Subroutine L117 undertakes first attempt to access the submitted disk in line 118 in order to check whether it is readable. If disk is not readable, then a jump to label L152 follows, where the next test in line 156 is to discriminate between nonexistent drives and those having no formatted media inside. Nonexistent drives are ignored, but all others are added to list of inaccessible disks, represented by value of V6 environmental variable. As far as status of these disks may be changed, their testing may be repeated.

If disk proves to be readable, then the next examination is writeability test, performed by subroutine L141. It is called from line 122 by a separate module of command interpreter COMMAND.COM. The decisive operation is in line 144: there intermediate redirection implies creation of a temporary file on the submitted disk. If this temporary file can't be created, then the SHIFT command in the same line wouldn't be executed, dummy parameters wouldn't be shifted, subroutine L141 will exit in line 145, and commands in lines 133–134 will add letter-name of this non-writable disk to list of inaccessible disks. If redirection in line 144 can create a temporary file, it will be deleted automatically just afterwards, but the SHIFT command will be executed, the ECHO command in line 148 will display a confirming message, and the DIR command in line 149 will display usage of disk's space. Then in line 151 subroutine L141 terminates, and letter-name of writable disk in lines 126–127 is added to list of accessible disks, represented by value of environmental variable V7.

Disk exploration subroutine L117 terminates either at line 128 or at line 135 – it depends on test result. In both cases control is returned to cycle FOR in line 53. Then subroutine L117 will be called again and again until a list of disk's letter-names, represented by value of variable V0, comes to end. When all disks are tested, the results together with suggested alternatives are displayed to the user by commands in lines 56–65. The user is offered to choose the most suitable alternative. User's answer is accepted by REASSIGN.COM utility in line 69. As far as user's response may be expressed by both upper-case and lower-case letters, commands in lines 74–77 turn any accepted letter to upper case, and then conditional commands in lines 78–85 fulfill user's will.

A check in line 78 is to clear up whether the returned letter is the letter-name of RAMdisk. If yes, then a jump to label L031 is performed, and all the following events repeat the scenario of quick loading onto a RAM-disk just as it was described above. In case of any other choice cycle FOR in line 81 searches for the returned letter through list of inaccessible disks. If the letter is found, then a jump to L042 follows. There in line 45 the L117 exploratory subroutine is called in order to test the selected disk more thoroughly. Thorough mode of investigation, defined by the fourth parameter "S", implies other test criteria and more detailed prompts about the ways to regain disk's accessibility. Subroutine makes a pause, allowing to change removable disk or to close write-protection hole in its cartridge. After that in line 53 a normal call follows for the same subroutine L117 in order to update lists of inaccessible and writable disks, and then the user is offered to make his choice once more.

User's choice of DOS relocation onto a writable disk is registered by cycle FOR in 83rd line. In this case execution proceeds through label L086 to DOS relocation part of AUTOEXEC.BAT file. Relocation procedure is performed just as it is was described above for RAM-disk. It is important to note that DOS relocation procedure doesn't make the selected disk bootable, it only allows to continue current DOS session after removing bootable media from that storage device, which was used to boot the PC. As far as DOS relocation procedure doesn't write files in the root directory, original bootability of the selected disk also can't be affected.

The disk used to boot the PC also may be writable, and its letter-name can be included in the list, represented by value of V7 variable. But DOS relocation onto this disk is senseless and should be avoided. This is why a check in line 84 intercepts such user's choice and directs further execution to label L104. Thus DOS relocation is bypassed. As far as bootable disk is writable, it also will be used as a place for temporary files. However, ordinary 1.44 Mb diskettes have no place enough for temporary files; any other writable disk should be preferred. Therefore, the user is given one more chance to bypass DOS relocation: the ESC keystroke forces a jump from line 73 to the same target label L104, but in this case an attempt will be undertaken to find a place for temporary files on some other suitable disk.

The last item in boot menu (9.09-01) is loading without drivers and TSRs. User's choice of this alternative also leads to the same label L104 via a jump from line 28. But before that in line 24 the exploratory subroutine L117 is called in quiet mode, defined by its fourth parameter Q. Detailed exploration is skipped, intermediate messages are not displayed. In this case the only purpose of exploration is data preparation for warning messages, displayed in lines 25–27.

Label L104 denotes final part of AUTOEXEC.BAT file, where all loading alternatives converge. Cycle FOR in line 105 appoints a writable disk for temporary files, if this appointment was not made yet. Operations in lines 106–114 prepare final values for TEMP, DIRCMD and PATH environmental variables, and delete local variables V0–V8. Unless special loading alternative L104 is chosen, the Volkov Commander file manager is launched. The last operation in AUTOEXEC.BAT file is an unconditional jump to final label END.

This version of AUTOEXEC.BAT is to be stored in the root directory of a removable bootable media. Those files, which are called from lines of AUTOEXEC.BAT file, must be present in specified directories of the same media. It is implied that command interpreter COMMAND.COM is present in the root directory, files ATTRIB.EXE, FIND.EXE and LABEL.EXE are present in \DOS\MS7 directory, utilities REASSIGN.COM and BLUE.COM in \DOS\OTH directory, driver TDSK.EXE (version 2.42) in \DOS\DRV directory, files VC.COM and VC.OVL in \DOS\VC4 directory. Naturally, presence of other files in these directories is allowed. If you intend to implement other file's allocation or other directory structure, then all affected path specifications should be corrected accordingly.

9.10 Experiments with linear addressing edit

It is often thought that modern 32-bit CPUs in real mode can't address to memory space beyond 1088 kb. Though this opinion is not denied explicitly in official data, nevertheless it is wrong. Since early 1990s several reports have proclaimed usage of undocumented CPUs features for access to extended memory. Tomas Roden is believed to be author of the idea.

Analysis of HIMEM.SYS (5.04-01) driver's code evinces presence of all necessary elements for providing access to extended memory in real mode. May be, this is just what HIMEM.SYS does, but no official confirmation for that has been published. Despite considerable age of this topic, idea of linear 32-bit addressing in real mode is still a matter of rumors.

Now there is no need to prove the possibility of linear addressing in real mode, but there is a need to demonstrate effective implementation of the idea. Therefore, part 9.10 of this book presents texts of two tiny utilities : GS_LIMIT.COM utility takes off segment boundary protection from GS register, and GS_DUMP.COM displays a dump of memory area, pointed at by a given 32-bit linear address. Effect of presented utilities is shown in fig.7: that memory area, which has just been inaccessible beyond segment limits, becomes accessible after a call for GS_LIMIT.COM utility.

 
Fig. 7

Those who dare to implement the presented examples will get a unique chance to look into all the corners and foldovers of 32-bit address space.

9.10-01 Segment protection switching on/off edit

As calculated linear address goes beyond segment boundaries, CPU's segment protection becomes actuated and ruins all hopes to get any benefit from address size override prefix (7.02-07) in real mode. However, the problem is not so hopeless as it seems at a first glance. All modern processors, starting from 80386 model and on, are able to change segment size. When segment size is made equal to highest address space limit, then any calculated segment address will be found allowable, and segment protection in fact becomes turned off.

Is it good or bad to turn off segment protection ? On one hand, this will cause total loss of stability in multi-tasking operating systems. On the other hand, this will open new opportunities for service and diagnostic programs in DOS operating environment. As any effective instrument, turning off segment protection may be both beneficial and dangerous. Therefore, CPU designers have been very cautious about control over segment protection. An opportunity to set arbitrary segment size has been given to protected mode software at the highest (zero) privilege level only.

When in protected mode at the highest privilege level the operating system core is active yet, then all other programs have no chances to get access to critical CPU's settings. But chances are, while CPU operates in real mode: any real mode program may switch CPU into protected mode, set maximum segment size in a "shadow" register, and then switch CPU back into real mode. After that linear 32-bit addressing with respect to affected segment register will be available without a risk to cause actuation of segment protection. This is, in short, the main idea, implemented by the proposed utility.

For the role of affected segment register the GS segment register is chosen, because its usage by real mode programs is not common. Besides that, properly composed programs don't violate segment limits intentionally. The author has tested many ordinary DOS programs, not aimed at GS segment status determination. All such programs have behaved identically both with and without GS segment limit protection.

Because of GS segment register choice the suggested utility has been named GS_LIMIT.COM. It differs from its known counterparts in that it is able to perform its mission not under "bare" DOS only, but also in cooperation with HIMEM.SYS driver (5.04-01). Second difference is that GS_LIMIT.COM is able to turn segment protection both OFF and ON. In order to turn protection OFF you have to specify a command

GS_limit off

Restoration of standard 64-kb limit for GS segment is initiated by command

GS_limit on

If neither of the shown parameters (OFF or ON) is specified, the GS_LIMIT.COM utility displays a short help.

GS_LIMIT.COM utility (662 bytes long) is produced by debugger DEBUG.EXE as a result of command sequence execution. This command sequence should be written into command file GS_LIMIT.SCR by means of editor program (as described in introduction article to chapter 9). Verbal comments may be omitted. Special attention should be paid to empty line – 8th from the end. It must be there, because empty line forces DEBUG.EXE to exit assembler mode (7.01-04). Then command file GS_LIMIT.SCR should be sent to debugger via input redirection : Debug.exe < GS_limit.scr Command file GS_LIMIT.SCR has to contain the following lines:

a 100
      ;***************** GS_limit.com ******************
      ;********** Section 1: memory and parameters check
         ; 110 - target for jump from line 104
cmp          SP,2010       ; 100 Less than 8 kb allocated?
jbe          0110          ;*104 If yes, leave it as it is
mov          SP,1FFE       ; 106 Set stack's top at 8 kb
mov          BX,0200       ; 109 Request for 8 kb space
mov          AH,4A         ; 10C A call for free MCB
int          21            ; 10E         creation function
cmp byte ptr [005E],46     ;=110 Is there the "F" parameter?
jz           0148          ;*115 If yes, let's go ahead
cmp byte ptr [005E],4E     ; 117 Is there the "N" parameter?
jz           0148          ;*11C If yes, let's go ahead
mov          DX,0317       ;*11E As required parameters
mov          AL,01         ; 121      are not present, go to
jmp          020A          ;*123      display help and exit
      ;********** Section 2: data, pointers, descriptors
         ; 126 - filled from 17D, called from 187, 1F7
         ; 128 - filled from 181, accessed at 190, 1E5
         ; 12A - GDT pseudo descriptor, accessed at 1CA
         ; 12C - GDT linear address, calculated at 1B2
                           ; 126 HIMEM.SYS entrance point
dw           0000,0000
                           ; 12A GDT size (3 descriptors)
db           18 00
                           ; 12C GDT address (offset = 130)
db           30 01 00 00
                           ; 130 "Empty" descriptor 0000
db           00 00 00 00 00 00 00 00
                           ; 138 4-Gb size descriptor 0008
db           FF FF 00 00 00 93 8F 00
                           ; 140 64-kb size descriptor 0010
db           FF FF 00 00 00 93 00 00
      ;******** Section 3: CPU and protection mode checks
         ; 148 - target for jumps from lines 115, 11C
         ; 162 - target for jump from line 158
pushf                      ;=148 Push 2 copies of original
pushf                      ; 149    flag's states into stack
pop          CX            ; 14A Load a copy into CX
xor          CH,70         ; 14B Invert bits 0C, 0D, 0E
push         CX            ; 14E Send inverted states via
popf                       ; 14F         stack into flags
pushf                      ; 150         register, and then
pop          AX            ; 151         via stack into AX
popf                       ; 152 Restore initial flag states
xor          AH,CH         ; 153 Set non-coincident bits
test         AH,40         ; 155 Is it a 16-bit CPU?
jz           0162          ;*158 If no, check protection
mov          AL,04         ; 15A If yes, go to display
mov          DX,024F       ;*15C         error message
jmp          020A          ;*15F         024F and exit
test         AH,30         ;=162 Is protected mode set?
jz           016F          ;*165 If no, go ahead
mov          AL,08         ; 167 If yes, go to display
mov          DX,0271       ;*169         error message
jmp          020A          ;*16C         0271 and exit
      ;********** Section 4: address bus A20 preparation
         ; 16F - target for jump from line 165
         ; 18B - target for jump from line 176
mov          AX,4300       ;=16F Check whether HIMEM.SYS
int          2F            ; 172         driver is installed
cmp          AL,80         ; 174 If no, go for direct
jnz          018B          ;*176             access to A20
mov          AX,4310       ; 178 If yes, query for entrance
int          2F            ; 17B              point address
mov          [0126],BX     ;*17D Store entrance point
mov          [0128],ES     ;*181     address in memory cells
mov          AH,05         ; 185 Call for A20 bus
call far     [0126]        ;*187      activation function
call         021C          ;=18B Check A20 state and jump
jnz          01A7          ;*18E      if A20 bus is active
mov word ptr [0128],0001   ;*190 Mark: bus A20 is not active
mov          AL,FF         ; 196 Attempt to activate A20 bus
call         022F          ;*198          by subroutine 022F
call         021C          ;*19B Check A20 state and jump
jnz          01A7          ;*19E        if A20 bus is active
mov          AL,02         ; 1A0 If A20 is not active, go
mov          DX,029C       ;*1A2        to display error
jmp          020A          ;*1A5        message and exit
      ;*********** Section 5: preparation of GDT table
         ; 1A7 - target for jumps from lines 18E, 19E
                           ;=1A7 Data size override prefix
db           66
xor          AX,AX         ; 1A8 Write zero into EAX
mov          AX,CS         ; 1AA Copy CS segment into AX
mov          CL,04         ; 1AC Shift 4 bits leftwards
db           66
shl          AX,CL         ; 1AF Obtaining linear address
db           66
add          [012C],AX     ;*1B2 GDT's linear address
      ;******** Section 6: selectors, ban on interrupts
         ; 1C3 - target for jump from line 1BE
mov          CX,0008       ; 1B6 0008 - 4-Gb size selector
cmp byte ptr [005E],46     ; 1B9 Is there "F" parameter ?
jz           01C3          ;*1BE If yes, leave CX=0008
mov          CX,0010       ; 1C0 If no, let it be CX=0010
cli                        ;=1C3 Prohibit interrupts
mov          AL,80         ; 1C4 Prohibit NMI by
out          70,AL         ; 1C6       sending byte 70h
in           AL,71         ; 1C8       into port 70h
      ;********* Section 7: transitions to PM and back
                           ; 1CA = Lgdt fword ptr [012A]
db           0F 01 16 2A 01
                           ; 1CF = mov EAX,CR0
db           0F 20 C0
or           AL,01         ; 1D2 Set protected mode
                           ; 1D4      bit (= mov CR0,EAX)
db           0F 22 C0
                           ; 1D7 Load GS (= mov GS,CX)
db           8E E9
and          AL,FE         ; 1D9 Clear protected mode
                           ; 1DB      bit (= mov CR0,EAX)
db           0F 22 C0
      ;********** Section 8: return to former state
         ; 1F5 - target for jump from line 1EC
mov          AL,7F         ; 1DE Enable NMI by
out          70,AL         ; 1E0      sending byte 7Fh
in           AL,71         ; 1E2      into port 70h
sti                        ; 1E4 Enable interrupts
cmp word ptr [0128],0001   ;*1E5 Check A20 state mark
jb           01FB          ;*1EA If mark=0000, do nothing
ja           01F5          ;*1EC If mark>0001 - to HIMEM.SYS
mov          AL,FD         ; 1EE If mark=0001, restore
call         022F          ;*1F0      A20 state by means
jmp          01FB          ;*1F3      of subroutine 022F
mov          AH,06         ;=1F5 Call for HIMEM.SYS function
call far     [0126]        ;*1F7 switching A20 bus OFF
      ;********** Section 9: message display and exit
         ; 1FB - target for jumps from lines 1EA, 1F3
         ; 208 - target for jump from line 203
         ; 20A - jump target from lines 123, 15F, 16C, 1A5
mov          DX,02E9       ;=1FB "Limit set" message offset
cmp byte ptr [005E],46     ; 1FE Is there "F" parameter?
jnz          0208          ;*203 If yes, specify offset for
mov          DX,02B9       ; 205      message "limit is OFF"
mov          AL,00         ;=208 Specify zero errorlevel
push         AX            ;=20A Jumps target point
mov          AH,40         ; 20B      to display messages
mov          BX,DX         ; 20D Read into CX a number
mov          CX,[BX-02]    ; 20F      characters to display
mov          BX,0001       ; 212 0001 = STDOUT's handle
int          21            ; 215 Send message to STDOUT
pop          AX            ; 217 Return errorlevel into AL
mov          AH,4C         ; 218 Call for DOS's program
int          21            ; 21A       termination function
      ;******** Section 10: A20 state check subroutine
         ; 21C - target for calls from lines 18B, 19B
push         DS            ;=21C Save DS segment in stack
xor          SI,SI         ; 21D Write zero into SI
mov          DS,SI         ; 21F now DS:SI=0000:0000
mov          DI,F0F1       ; 221 Set ES:DI=F0F1:F0F0
mov          ES,DI         ; 224    in order to obtain
dec          DI            ; 226    F0F10h + F0F0h = 100000h
cld                        ; 227 Set count UP
mov          CX,0010       ; 228 Set repeat limit 16 bytes
repz                       ; 22B      and repeat while equal
cmpsb                      ; 22C Is there a foldover?
pop          DS            ; 22D Restore DS, return result
ret                        ; 22E       via state of ZF flag
      ;******** Section 11: A20 state change subroutine
         ; 22F - target for calls from lines 198, 1F0
push         AX            ;=22F Save command code in stack
call         0241          ;*230 Wait controller's readiness
mov          AL,D1         ; 233 D1 - first byte of command,
out          64,AL         ; 235       sent into port 64h
call         0241          ;*237 Wait controller's readiness
pop          AX            ; 23A Restore command code in AX
out          60,AL         ; 23B     and send it to port 60h
call         0241          ;*23D Wait controller's actuation
ret                        ; 240            and then return
      ;******** Section 12: controller waiting subroutine
         ; 241 - target for calls from lines 230, 237, 23D
         ; 245 - cycle return point from line 249
push         CX            ;=241 Save CX state in stack
mov          CX,FFFF       ; 242 Write cycle's limit into CX
in           AL,64         ;=245 Read a byte from port 64h
test         AL,02         ; 247 Check state of the 2nd bit
loopnz       0245          ;*249 Repeat, if port is busy
pop          CX            ; 24B Restore CX state from stack
ret                        ; 24C Return from subroutine
      ;*********** Section 13: messages
db           20 00
         ; 24F - 1st message, mentioned at 15C
db           0D 0A 09 "16-bit processor"
db           20 "can" 27 "t suit" 0D 0A
db           29 00
         ; 271 - 2nd message, mentioned at 169
db           0D 0A 09 "GS_limit can" 27 "t run"
db           20 "in protected mode" 0D 0A
db           1B 00
         ; 29C - 3rd message, mentioned at 1A2
db           0D 0A 09 "Line A20 control error" 0D 0A
db           2E 00
         ; 2B9 - 4th message, mentioned at 205
db           0D 0A 09 "GS segment limit"
db           20 "protection is turned OFF" 0D 0A
db           2C 00
         ; 2E9 - 5th message, mentioned at 1FB
db           0D 0A 09 "GS segment limit"
db           20 "protection is restored" 0D 0A
db           7F 00
         ; 317 - 6th message (help), mentioned at 11E
db           0D 0A "GS_limit.com removes the GS segment"
db           20 "limit protection or can restore it back"
db           0D 0A "Usage examples:" 0A 0D 09 09 "GS_lim"
db           "it off" 0A 0D 09 09 "GS_limit on" 0D 0A
         ; 396 End of assembler text

n GS_limit.com
rBX
0000
rCX
0296
w
q

Assembler text starts in section 1 with ordinary release of allocated memory excess (note 5 to A.12-7). Then parameter's presence check is performed. Form of parameter's presentation is chosen so that parameters are automatically uppercased and written into first FCB (note 4 to A.07-1) inside program's PSP. If required parameters are not specified, then in line 123 a jump occurs to final section, where help message is displayed. After that utility comes to end, leaving errorlevel code 01.

When required command line parameters are specified, execution continues from line 148 in section 3, where two conditions are checked: CPU's suitability and its operation in real mode. The essence of these checks is described in notes 2 and 3 to appendix A.11-4. If either of conditions is not met, then jumps occur to final section, where corresponding error message is displayed, and execution comes to end, leaving errorlevel codes either 04 or 08.

When CPU checks are passed successfully, execution continues from line 16F in section 4, where state of address bus line A20 is prepared. Necessity of such preparation is not obvious, but has been confirmed. Address bus line A20 must be switched ON. If HIMEM.SYS driver is installed yet, its function AH=05 (A.12-3) should be called for in order to switch ON the address bus line A20. If HIMEM.SYS driver is not installed, then a subroutine 022F from section 11 should be called, which attempts to switch ON the address bus line A20 by means of keyboard controller (details in note 1 to A.11-3).

Initial and final states of address bus line A20 are checked in lines 18B and 19B by calls for subroutine 021C in section 10. If attempts to switch ON address bus line A20 fail, then in line 1A5 a jump will be performed to final part of program. There an error message is displayed, and program's execution comes to end, leaving errorlevel code 02. Normally at least one attempt to switch ON address bus line A20 is a success, and then execution continues from line 1A7 in section 5.

Commands in section 5 prepare a pseudo descriptor for GDT table. Address of GDT table is copied from this pseudo descriptor by CPU into its GDTR register. In line 1AC segment address CS is transformed into linear address by shift 4 bits leftwards. Summation in line 1B2 forms linear address of GDT table inside pseudo descriptor template (in lines 12A–12F of section 2).

Detailed structure of GDT table's descriptors is shown in appendix A.12-2. Second and third descriptors are prepared for data segments. In line 138 the second descriptor (selector 0008) defines 4 Gb segment size. It should be used to switch off GS segment protection. In line 140 the third descriptor (selector 0010) defines 64 kb segment size. This descriptor should be used in order to restore back normal protection for GS segment. Before CPU is switched to protected mode, in register CX that selector should be prepared, which corresponds to requested task. A choice between those two selectors — 0008 and 0010 — is made by commands in lines 1B6–1C0 of section 6. Following commands in section 6 prohibit interrupts, including NMI (details in note 1 to article 8.01-03).

The first command in 7th section is LGDT (= Load Global Descriptor Table), which loads data from GDT pseudo descriptor into CPU's GDTR register. DEBUG.EXE doesn't "know" the LGDT command, therefore its code (0F 01 16) is introduced as data by DB instruction. The last two bytes in LGDT command — 2A 01 — represent pseudo descriptor's offset counted from segment address in DS register, i.e. just number 012A of a line, where pseudo descriptor's template is prepared in section 2 of assembler text.

CPU transition to protected mode can be performed by INT 15\AH=89h function (8.01-78), which requires a larger GDT table and reprograms both interrupt controllers. As far as proclaimed task implies a return to real mode, later a backward reprogramming will be needed for interrupt controllers. In order to avoid excess complexity, transition to protected mode is performed here by setting the PE bit (PE = Protection Enable) in CPU's CR0 register (A.11-4). Therefore, a command in line 1CF copies contents of CR0 register into AX, in this copy the PE bit is set, and then in line 1D4 the altered copy is written back into CR0 register. Commands concerning CR0 register (note 1 to 7.03-58) are not "known" to DEBUG.EXE and hence are introduced in lines 1CF and 1D4 as data following DB instruction.

Of course, PE bit setting is not sufficient for obtaining a full-functional protected mode, but in this case full functionality isn't required. In protected mode the GS_LIMIT.COM utility must perform a single operation: write into GS segment register that selector (0008 or 0010), which is prepared yet in CX register. Command copying CX contents into GS (note 2 to 7.03-58) is not "known" to DEBUG.EXE, and therefore its code is introduced in line 1D7 as data after DB instruction. Copying of a prepared selector into GS register brings about that hidden event, which is the main goal of GS_LIMIT.COM utility: new contents, including new segment limit, is written into CPU's GS "shadow" register from the GDT descriptor, pointed at by the copied selector.

When summit is reached yet, it's just the time to begin descent back. First of all, the PE bit must be cleared in that code, which is still preserved in EAX register since it has been copied from CPU's control register CR0. Command in line 1DB writes this twice altered code back into CR0 register. Thus CPU is returned into real mode. The commands in section 8 cancel interrupt prohibition and restore initial state of address bus line A20 by just those facilities, which previously have altered this state. Commands in lines 1FB–215 of section 9 select appropriate final message and display it. In lines 218–21A a call for DOS's INT 21\AH=4Ch function terminates execution of GS_LIMIT.COM utility.

After its termination the GS_LIMIT.COM utility leaves errorlevel code, which may present a separate interest in order to determine CPU's operating mode:

GS_limit on > nul
if errorlevel 7 echo Processor runs in V86 mode
if not errorlevel 7 echo Processor runs in real mode

The last peculiarity of GS_LIMIT.COM utility is that the value, left behind in GS segment register, retains status of a selector. This enables to experiment with improper linear addressing in CPU. After any operation, writing a new value into GS register, its contents will acquire proper status of a segment address.

9.10-02 Display of a linearly addressed dump edit

The GS_DUMP.COM utility, presented in this article, shows a 128-byte memory dump on the screen, almost as DEBUG.EXE does in response to its "D" command (6.05-04). The main difference is that GS_DUMP.COM utility instead of ordinary addresses (segment: offset) accepts 32-bit linear addresses, which enable to define just any access point within 4 Gb address space. While segment protection is active, GS_DUMP.COM utility responds with an error message to all requests for access beyond GS segment. But when GS segment protection is switched OFF by GS_LIMIT.COM utility (9.10-01), then dump of any memory region within 4 Gb address space can be displayed.

Besides its obvious demonstration effect, GS_DUMP.COM utility provides answers to many questions, concerning practical implementation of linear addressing in real mode. In command line the name GS_DUMP.COM must be followed by linear address, composed of up to 8 hexadecimal digits, for example :

GS_dump.com FFFE0

Specified linear address is interpreted as absolute address, counted from the start of address space irrespective to actual contents of GS register. After linear address there may be one of two optional parameters :

A – don't alter initial state of address bus line A20 ;
R – write zero into GS segment register.

Command line with an optional parameter may look, for example, as

GS_dump.com FFFE0 A

When address bus line A20 initially is not active, GS_DUMP.COM utility with "A" optional parameter shows address space foldover at address 100000h. By default GS_DUMP.COM utility activates address bus line A20 and always shows address space opened irrespective to its actual initial state.

GS_DUMP.COM utility (1291 bytes long) is produced by debugger DEBUG.EXE as a result of command sequence execution. This sequence should be written into command file GS_DUMP.SCR by means of editor program (as described in introduction to chapter 9). Comments may be omitted. Special attention should be paid to empty line – 8th from the end. It must be there, because it forces DEBUG.EXE to exit assembler mode (7.01-04). Then command file GS_DUMP.SCR should be sent to debugger via input redirection :

DEBUG.EXE < GS_DUMP.SCR

Command file GS_DUMP.SCR must contain the following lines :

a 100
      ;**************** GS_dump.com **************
      ;********* Section 1: preliminary preparations
         ; 110 - target for jump from line 104
cmp          SP,2010       ; 100 Less than 8 kb allocated?
jbe          0110          ;*104 If yes, leave it as it is
mov          SP,1FFE       ; 106 Set stack's top at 8 kb
mov          BX,0200       ; 109 Request for 8 kb space
mov          AH,4A         ; 10C A call for free MCB
int          21            ; 10E          creation function
push         CS            ;=110 Prepare
pop          DS            ; 111           DS = CS
db           66
xor          BX,BX         ; 113 Write zero in EBX register
      ;********* Section 2: CPU checks
         ; 12F - target for jump from line 125
pushf                      ; 115 Push 2 copies of original
pushf                      ; 116    flag's states into stack
pop          CX            ; 117 Load a copy into CX
xor          CH,70         ; 118 Invert bits 0C, 0D, 0E
push         CX            ; 11B Send altered bits via
popf                       ; 11C    stack to flags register,
pushf                      ; 11D        and then again
pop          AX            ; 11E        via stack into AX
popf                       ; 11F Restore initial flag states
xor          AX,CX         ; 120 Set non-coincident bits
test         AH,40         ; 122 Is it a 16-bit CPU?
jz           012F          ;*125 If no, go to next check
mov          AL,02         ; 127 If yes, go to display
mov          DX,03D6       ;*129        error message
jmp          02BF          ;*12C            03D6 and exit
test         AH,30         ;=12F Is protected mode set?
jz           0163          ;*132 If no, bypass section 3
      ;********* Section 3: protected mode checks
         ; 14B - target for jump from line 141
mov word ptr [03D4],0483   ;*134 Prepare message address
mov          AX,1687       ; 13A Send trial request
int          2F            ; 13D           for DPMI server
or           AX,AX         ; 13F Is DPMI server installed?
jnz          014B          ;*141 If not, go further
mov          AL,08         ; 143 If yes, go to display
mov          DX,03FB       ;*145       error message
jmp          02BF          ;*148            03FB and exit
push         DS            ;=14B Save DS segment in stack
mov          DS,BX         ; 14C 0 = interrupt table segment
mov          DS,[019E]     ; 14E Load EMM's segment into DS
cmp word ptr [0014],4249   ; 152 Is it IBM's driver?
pop          DS            ; 158 Restore DS from stack
jz           0163          ;*159 Continue, if IBM's driver
mov          AL,02         ; 15B If not, go to display
mov          DX,041E       ;*15D      error message
jmp          02BF          ;*160            041E and exit
      ;********* Section 4: linear address reading
         ; 163 - target for jumps from lines 132, 159
         ; 16A - cycle return target from line 19C
         ; 17B - target for jump from line 175
         ; 188 - target for jump from line 17E
         ; 194 - target for jumps from lines 16F, 179
mov          CX,0004       ;=163 Preset 4 bit shift
cld                        ; 166 Set SI count upwards
mov          SI,005D       ; 167 Set start address
lodsb                      ;=16A Load one ASCII code into AL
sub          AL,30         ; 16B Translate ASCII code
cmp          AL,09         ; 16D If it is decimal digit,
jbe          0194          ;*16F        append it to EBX
sub          AL,07         ; 171 Translate A-F letter codes
cmp          AL,0A         ; 173 Is it character below "A"?
jb           017B          ;*175 If yes, check the reason
cmp          AL,0F         ; 177 Is it one of letters A-F?
jbe          0194          ;*179 If yes, go append it to EBX
cmp          SI,005E       ;=17B Is it the first iteration?
jnz          0188          ;*17E If not, let's check further
mov          AL,01         ; 180 If yes, go to display
mov          DX,04F1       ;*182        help message 04F1
jmp          02BF          ;*185          and then exit
cmp          AL,E9         ;=188 Is last character a space?
jz           019E          ;*18A If yes, no more characters
mov          AL,01         ; 18C If no, go to display
mov          DX,04CB       ;*18E         error message
jmp          02BF          ;*191             04CB and exit
                           ;=194 Data size override prefix
db           66
shl          BX,CL         ; 195 Shift EBX 4 bits leftwards
or           BL,AL         ; 197 Insert next character in BL
cmp          SI,0064       ; 199 Is 8th iteration reached?
jbe          016A          ;*19C If not, repeat the cycle
      ;******** Section 5: address bus line A20 activation
         ; 19E - target for jump from line 18A
         ; 1C3 - target for jump from line 1AC
         ; 1D3 - target for jump from line 1A3
         ; 1D8 - target for jump from line 1C6
cmp byte ptr [006D],41     ;=19E Is "A" parameter present?
jz           01D3          ;*1A3 If yes, bypass section 5
mov          AX,4300       ; 1A5 Is the HIMEM.SYS
int          2F            ; 1A8         driver installed?
cmp          AL,80         ; 1AA If no, bypass appeals
jnz          01C3          ;*1AC         to HIMEM.SYS driver
push         BX            ; 1AE Save BX contents in stack
mov          AX,4310       ; 1AF Call for HIMEM.SYS
int          2F            ; 1B2      entrance point address
mov          [03C0],BX     ;*1B4 Store entrance point
mov          [03C2],ES     ;*1B8     address in memory cells
mov          AH,05         ; 1BC Call for bus line A20
call far     [03C0]        ;*1BE         activation function
pop          BX            ; 1C2 Restore former BX contents
call         02D3          ;=1C3 Is bus line A20 active?
jnz          01D8          ;*1C6 If yes, bypass next attempt
mov word ptr [03C2],0001   ;*1C8 If no, set a mark
mov          AL,FF         ; 1CE Activate bus line A20
call         02EB          ;*1D0          by subroutine 02EB
call         02D3          ;=1D3 Is bus line A20 active?
jz           01DD          ;*1D6 If yes, message 0445
mov byte ptr [0445],24     ;=1D8        must be made invalid
      ;******* Section 6: indication of GS segment address
         ; 1DD - target for jump from line 1D6
         ; 1E6 - target for jump from line 1E2
cmp byte ptr [006D],52     ;=1DD Is "R" parameter present?
jz           01E6          ;*1E2 If yes, don't read GS
                           ; 1E4 = MOV SI,GS
db           8C EE
                           ;=1E6 = MOV GS,SI
db           8E EE
                           ; 1E8 = PUSH GS
db           0F A8
pop          [03D0]        ;*1EA Store GS contents in 3D0
call         0339          ;*1EE Send 0Dh 20h to STDOUT
call         0349          ;*1F1 Display word stored in 3D0
mov          DX,0445       ;*1F4 Display
call         0330          ;*1F7         message 0445
mov          DX,045B       ;*1FA Display
call         0330          ;*1FD         message 045B
      ;********* Section 7: datum point calculation
db           66
xchg         SI,BX         ; 201 Copy address into ESI
mov          BX,SI         ; 203 Return in BX 2 bytes only
and          BX,000F       ; 205 Select the rightmost digit
neg          BL            ; 209 Get datum point
and          SI,FFF0       ; 20B Now ESI - base address
db           66
mov          DI,SI         ; 210 Copy ESI into EDI
db           66
xchg         [03D0],DI     ;*213 Exchange [03D0] with EDI
db           66
shl          DI,CL         ; 218 In EDI - linear address GS
mov          DX,[03D4]     ;*21A Prepare message number
db           66
sub          SI,DI         ; 21F Calculate offset in ESI
jnb          0226          ;*221 If below zero, change
mov          DX,0460       ;*223      message number to 0460
      ;********* Section 8: flags and pointers replacement
         ; 226 - target for jump from line 221
                           ;=226 Data size override prefix
db           66
pushf                      ; 227 Push EFLAGS into stack
mov          BP,SP         ; 228 Copy stack pointer into BP
mov          AL,[BP+02]    ; 22A Read and store the
mov          [03C4],AL     ;*22D          third flag's byte
and byte ptr [BP+02],FE    ; 230 Clear alignment flag
db           66
popf                       ; 235 Write EFLAGS back
in           AL,21         ; 236 Read port 21h mask
mov          [03C5],AL     ; 238          byte and store it
or           AL,60         ; 23B Set bits 05 and 06
out          21,AL         ; 23D Disable IRQ5 and IRQ6
mov          [03C8],DS     ;*23F Fill segment cells in
mov          [03CC],DS     ;*243         handler's addresses
push         BX            ; 247 Save BX contents in stack
mov          AL,0D         ; 248 Exchange handlers
call         03A0          ;*24A    for interrupt INT 0D and
call         03A0          ;*24D        for interrupt INT 0E
pop          BX            ; 250 Restore BX contents
      ;********* Section 9: trial reading attempt
         ; 251 - cycle return target from line 289
mov          [03CE],SI     ;=251 Save SI for comparison
db           65 67
lodsb                      ; 257 Attempt to read a byte
cmp          SI,[03CE]     ;*258 Has SI value changed?
jnz          0266          ;*25C If yes, go to main cycle
call         0342          ;*25E Display linear address
call         0321          ;*261 Calculate next address
jmp          0286          ;*264 Jump to check new address
      ;********* Section 10: main line display cycle
         ; 266 - target for jump from line 25C
         ; 26E - cycle return target from line 273
         ; 278 - cycle return target from line 27D
         ; 286 - target for jump from line 264
                           ;=266 Data size override prefix
db           66
dec          SI            ; 267 Restore index in ESI
call         0349          ;*268 Display linear address
call         0318          ;*26B Intermediate correction
call         0362          ;=26E Display a byte in AL
inc          BL            ; 271 Increment bytes count by 1
loop         026E          ;*273 1st line display cycle
call         0309          ;*275 Intermediate correction
call         0379          ;=278 Display a byte as ASCII
inc          BL            ; 27B Increment bytes count by 1
loop         0278          ;*27D 2nd line display cycle
call         0339          ;*27F Send 0Dh 20h to STDOUT
mov          DX,[03D4]     ;*282 Update message number
cmp          BL,80         ;=286 Is display line the last?
jb           0251          ;*289 If not, display next line
      ;********* Section 11: return to initial states
         ; 2B4 - target for jump from line 2AB
mov          AL,0D         ; 28B Restore handlers for
call         03A0          ;*28D        interrupt INT 0D and
call         03A0          ;*290        for interrupt INT 0E
mov          AL,[03C5]     ;*293 Read former mask byte and
out          21,AL         ; 296         send it to port 21h
db           66
pushf                      ; 299 Read EFLAGS into stack
mov          BP,SP         ; 29A Copy stack pointer into BP
mov          AL,[03C4]     ;*29C Read and restore former
mov          [BP+02],AL    ; 29F            3rd flag's byte
db           66
popf                       ; 2A3 Restore EFLAGS states
cmp word ptr [03C2],0001   ;*2A4 Check mark of A20 state
jb           02BA          ;*2A9 If 0000, don't touch A20
ja           02B4          ;*2AB IF >0001 - go use HIMEM.SYS
mov          AL,FD         ; 2AD Restore A20 states with
call         02EB          ;*2AF             subroutine 02EB
jmp          02BA          ;*2B2 Jump to final operations
mov          AL,06         ;=2B4 Call HIMEM.SYS function
call far     [03C0]        ;*2B6       in order to close A20
      ;******** Section 12: program's termination
         ; 2BA - target for jumps from lines 2A9, 2B2
         ; 2BF - jumps from lines 12C, 148, 160, 185, 191
mov          DX,0442       ;=2BA Offset for line's end bytes
mov          AL,00         ; 2BD Happy end errorlevel
push         AX            ;=2BF Save errorlevel in stack
mov          AH,09         ; 2C0 Display final
int          21            ; 2C2               message
pop          AX            ; 2C4 Restore errorlevel
mov          AH,4C         ; 2C5 Call for termination
int          21            ; 2C7     function, return to DOS
      ;******* Section 13: interrupt's 0D and 0E handlers
         ; 2C9 - must be specified in cell 3CA
         ; 2CC - must be specified in cell 3C6
mov          DX,04A5       ;=2C9 Call target for Int 0E
mov          BP,SP         ;=2CC Call target for Int 0D
add word ptr [BP+00],+03   ; 2CE Correct return address
iret                       ; 2D2         in stack and return
      ;******** Section 14: state check for bus line A20
         ; 2D3 - target for calls from lines 1C3, 1D3
push         DS            ;=2D3 Save states of
push         CX            ; 2D4       DS and CX registers
db           66
xor          SI,SI         ; 2D6 Write zero into ESI
push         SI            ; 2D8 Save SI=0 in stack
mov          DS,SI         ; 2D9 Now DS:SI=0000:0000
mov          DI,F0F1       ; 2DB Prepare ES:DI=F0F1:F0F0
mov          ES,DI         ; 2DE    in order to get address
dec          DI            ; 2E0    F0F10h + F0F0h = 100000h
cld                        ; 2E1 Set count upwards
mov          CX,0010       ; 2E2 Set count limit 16 bytes
repz                       ; 2E5 Repeat while equal
cmpsb                      ; 2E6 Is there a foldover?
pop          SI            ; 2E7 Restore states of
pop          CX            ; 2E8    registers and return,
pop          DS            ; 2E9        keeping result as
ret                        ; 2EA            state of ZF flag
      ;***** Section 15: bus line A20 control subroutine
         ; 2EB - target for calls from lines 1D0, 2AF
push         AX            ;=2EB Save command in stack
call         02FD          ;*2EC Wait controller readiness
mov          AL,D1         ; 2EF Send 1st command's
out          64,AL         ; 2F1        byte into port 64h
call         02FD          ;*2F3 Wait controller readiness
pop          AX            ; 2F6 Send 2nd command's
out          60,AL         ; 2F7        byte into port 60h
call         02FD          ;*2F9 Wait for controller's
ret                        ; 2FC   actuation and then return
      ;********* Section 16: wait for controller readiness
         ; 2FD - target for calls from lines 2EC, 2F3, 2F9
         ; 301 - cycle return target from line 305
push         CX            ;=2FD Save CX contents in stack
mov          CX,FFFF       ; 2FE Store cycles limit in CX
in           AL,64         ;=301 Read state of port 64h
test         AL,02         ; 303 Check readiness bit
loopnz       0301          ;*305 Repeat, if not ready
pop          CX            ; 307 Restore CX and return
ret                        ; 308      to the caller program
      ;********* Section 17: intermediate correction
         ; 309 - target for call from line 275
         ; 318 - target for call from line 26B
sub          BL,10         ;=309 Return count one line back
push         BX            ; 30C Save BX contents in stack
mov          BL,10         ; 30D 10h = increment value
db           66
add          [03D0],BX     ;*310 Linear address correction
db           66
sub          SI,BX         ; 315 Offset correction
pop          BX            ; 317 Restore former BX contents
call         0393          ;=318 Twice send a space
call         0393          ;*31B        character to STDOUT
mov          CL,10         ; 31E Preset bytes count
ret                        ; 320 Return from subroutine
      ;********* Section 18: transition to next dump line
         ; 321 - target for call from line 261
         ; 330 - target for calls from lines 1F7, 1FD
         ; 339 - target for calls from lines 1EE, 27F
add          BL,10         ;=321 Increment bytes count by 16
push         BX            ; 324 Save BX contents in stack
mov          BL,10         ; 325 Set increment by 16
db           66
add          [03D0],BX     ;*328 Linear address correction
db           66
add          SI,BX         ; 32D Offset correction
pop          BX            ; 32F Restore former BX contents
mov          DI,DX         ;=330 Message address - into DI
mov          AH,09         ; 332 Call for DOS's STDOUT
int          21            ; 334         output function
mov byte ptr [DI],24       ; 336 Disable message
mov          AL,0D         ;=339 Send carriage return
call         0395          ;*33B          command to STDOUT
call         0393          ;*33E Send a space to STDOUT
ret                        ; 341 Return from subroutine
      ;********* Section 19: cell 3D0 contents display
         ; 342 - target for a call from line 25E
         ; 349 - target for calls from lines 1F1, 268
         ; 354 - target for jump from line 35E
         ; 361 - target for jump from line 347
mov          DI,DX         ;=342 Is the requested
cmp byte ptr [DI],24       ; 344          message disabled?
jz           0361          ;*347 If yes, don't display it
mov          AL,0A         ;=349 Send a line feed
call         0395          ;*34B          command to STDOUT
push         BX            ; 34E Save BX contents in stack
xor          BL,BL         ; 34F Turn off limit check
mov          DI,03D3       ; 351 Load 1st byte offset in DI
mov          AL,[DI]       ;=354 Copy that byte into AL
call         0368          ;*356 Call AL byte translation
dec          DI            ; 359 Turn to next byte
cmp          DI,03D0       ;*35A Is it the last byte?
jnb          0354          ;*35E If not, repeat the cycle
pop          BX            ; 360 Restore former BX contents
ret                        ;=361 Return from subroutine
      ;********* Section 20: AL translation subroutine
         ; 362 - target for a call from line 26E
         ; 368 - target for a call from line 356
call         0393          ;=362 Call to display a space
db           67 65
lodsb                      ; 367 Read GS:[linear address]
push         CX            ;=368 Save CX contents in stack
mov          CL,04         ; 369 Preset 4 bits shift in CL
shl          AH,CL         ; 36B Clear 4 bits in AH
shl          AX,CL         ; 36D Separate half-bytes from AL
shr          AL,CL         ; 36F Clear 4 bits in AL
pop          CX            ; 371 Restore former CX contents
call         0384          ;*372 Call for AH display
call         0384          ;*375 Call for AL display
ret                        ; 378 Return from subroutine
      ;********* Section 21: one character display
         ; 379 - target for a call from line 278
         ; 384 - target for calls from lines 372, 375
         ; 38E - target for calls from lines 37E, 382, 38A
         ; 393 - called from lines 318, 31B, 33E, 362
         ; 395 - called from lines 33B, 34B, jump from 391
                           ;=379 Copy one character into AL
db           67 65
lodsb                      ; 37B    from GS:[linear address]
cmp          AL,20         ; 37C Is it 20h value or more?
ja           038E          ;*37E Values AL < 20h replace
mov          AL,2E         ; 380      with dots and jump
jmp          038E          ;*382         to check boundary
xchg         AH,AL         ;=384 Exchange contents AH - AL
add          AL,30         ; 386 Translate 0 - 9 into ASCII
cmp          AL,39         ; 388 Is it 0 - 9 or A - F ?
jbe          038E          ;*38A Leave digits 0 - 9 intact
add          AL,07         ; 38C Translate A - F into ASCII
cmp          BL,80         ;=38E Check boundary
jb           0395          ;*391 If beyond boundary,
mov          AL,20         ;=393     replace it with space
push         AX            ;=395 Save states of AX and
push         DX            ; 396      DX registers in stack
mov          AH,02         ; 397 Call for DOS's function
mov          DL,AL         ; 399      of character output
int          21            ; 39B             into STDOUT
pop          DX            ; 39D Restore former states
pop          AX            ; 39E      of AX and DX registers
ret                        ; 39F Return from subroutine
      ;********* Section 22: handler's exchange subroutine
         ; 3A0 - called from lines 24A, 24D, 28D, 290
mov          AH,35         ;=3A0 Call for handler's address
int          21            ; 3A2   reading function in ES:BX
mov          DI,AX         ; 3A4 Prepare in DI a memory cell
shl          DI,1          ; 3A6        offset: DI = AX * 2
shl          DI,1          ; 3A8 350D*4=D434 350E*4=D438
sub          DI,D06E       ;*3AA D434-D06E=3C6 D438-D06E=3CA
push         DS            ; 3AE Store contents of DS and
push         DX            ; 3AF       DX registers in stack
lds          DX,[DI]       ; 3B0 DS:DX - pointer to a cell
mov          AH,25         ; 3B2 Call for handler's address
int          21            ; 3B4           writing function
pop          DX            ; 3B6 Restore former contents
pop          DS            ; 3B7      of DS and DX registers
mov          [DI],BX       ; 3B8 Store address of the former
mov          [DI+02],ES    ; 3BA     handler in memory cells
inc          AL            ; 3BD Prepare next number in AL
ret                        ; 3BF Return from subroutine
      ;******** Section 23: data and addresses
         ; 3C0 - HIMEM.SYS offset, in lines 1B4, 1BE, 2B6
         ; 3C2 - HIMEM.SYS segment, in lines 1B8, 1C8, 2A4
db           00 00 00 00
         ; 3C4 - 3rd byte of EFLAGS, in lines 22D, 29C
db           00
         ; 3C5 - mask for port 21h, in lines 238, 293
db           00
         ; 3C6 - offset of this cell is calculated in 3AA
         ; 3C8 - segment of 0Dh handler, written from 23F
db           CC 02 00 00
         ; 3CA - offset of this cell is calculated in 3AA
         ; 3CC - segment of 0Eh handler, written from 243
db           C9 02 00 00
         ; 3CE - place to store SI, accessed from 251, 258
db           00 00
         ; 3D0 - linear address - 1EA, 213, 310, 328, 35A
         ; 3D3 - most significant address byte, from 351
db           00 00 00 00
         ; 3D4 - place for message address - 134, 21A, 282
db           71 04
      ;******** Section 24: messages
         ; 3D6 1st message, mentioned at 129
db           0D 0A 'Error: 16-bit machine can' 27
db           't suit' 0D 0A 24
         ; 3FB 2nd message, mentioned at 145
db           0D 0A 'Error: can' 27 't run under WINDOWS'
db           0D 0A 24
         ; 41E 3rd message, mentioned at 15D
db           0D 0A 'Error: incompatible EMM386 version'
         ; 442 4th virtual message, mentioned at 2BA
db           0D 0A 24
         ; 445 5th message, mentioned at 1D8, 1F4
db           09 'Line A20 is disabled' 24
         ; 45B 6th message, mentioned at 1FA
db           'GS=' 20 24
         ; 460 7th message, mentioned at 223
db           09 '- below GS base' 24
         ; 471 8th message, addressed in line 3D4
db           09 '- above GS limit' 24
         ; 483 9th message, mentioned at 134
db           09 'GS range or privilege violation' 20 24
         ; 4A5 10th message, mentioned at 2C9
db           09 'Page isn' 27 't initialized or is'
db           20 'swapped' 24
         ; 4CB 11th message, mentioned at 18E
db           0D 0A 'Address error: invalid character(s)' 0A
         ; 4F1 12th message (help), mentioned at 182
db           0D 0A 09 'GS_dump.com - linear GS address'
db           20 'dump utility' 0D 0A 'Usage examples:'
db           0D 0A 09 09 'GS_dump 002FABCD' 0D 0A 09 09
db           'GS_dump 002FABCD A' 0D 0A 09 09
db           'GS_dump 002FABCD R' 0D 0A 20
db           '002FABCD - linear address example, up to'
db           20 '8 hexadecimal digits long' 0D 0A 09
db           'A - option: don' 27 't try to enable' 20
db           'line A20' 0D 0A 09 'R - option: reset GS'
db           20 'to zero' 0D 0A 24
         ; 60B End of assembler text

n GS_dump.com
rBX
0000
rCX
050B
w
q

Program starts in section 1 with ordinary release of allocated memory excess (note 5 to A.12-7). CPU checks and protected mode checks are performed exactly as in GS_LIMIT.COM program (9.10-01). But in case of protected mode the GS_DUMP.COM utility is not terminated at once; investigation continues in section 3. If protected mode is controlled not by WINDOWS OS, but by compatible IBM's version 4.50 of EMM386.EXE driver (5.04-02), then execution proceeds further.

In the 4th section linear address, specified in command line, is read into EBX register. In course of reading the ASCII code characters are translated into "raw" code and are checked for correctness. If something else is found there except correct hexadecimal digits, then GS_DUMP.COM utility displays error message and terminates.

In the 5th section of GS_DUMP.COM the address bus line A20 is activated just as it is activated in section 4 of GS_LIMIT.COM program (9.10-01).

Important specific operations in GS_DUMP.COM program begin with preparation of GS register in section 6. Preparation is needed, because contents of GS register may retain status of a selector. In order to get rid of uncertainty, command in line 1E6 performs writing into GS register. Even when contents of GS register should be preserved, command in line 1E6 writes in GS register just that value, which has been read from GS register by a command in preceding line 1E4. After writing operation the contents of GS register acquire normal status of a segment address.

Commands in final lines of 6th section display GS segment address. After that commands in lines 201–20B of 7th section calculate datum point byte and base address, i.e. linear address of the first byte in a line of dump. In line 218 a shift 4 bits leftwards transforms GS segment address into linear address. In line 21F a difference between base address and GS linear address is calculated. This difference represents just that offset, which is necessary for reading data at absolute address, specified by the user in command line.

As far as absolute address reading operation is not necessarily directed within allowed segment limits, it may invoke generation of exceptions INT 0D, INT 0E (notes 6 and 7 to 8.01-09) and INT 11 (note 1 to 8.01-42). Each of these exceptions will cause computer's hanging, unless some special precautions are taken in advance. Exception INT 11 can be prevented by clearing misalignment control bit (12h) in EFLAGS register (A.11-4). Therefore, commands in lines 226–235 copy EFLAGS register contents into stack, initial state of the third read byte is stored in a 03C4 memory cell, misalignment control bit is cleared just in stack, and the altered result is written back from stack into EFLAGS register.

Exceptions INT 0D and INT 0E can't be prevented, processor inevitably will generate these exceptions at attempts of access to protected memory regions. Calls for exception handlers can be intercepted, but in real mode it is difficult to discriminate between CPU's calls for exceptions and external interrupt calls, received via IRQ 5 and IRQ 6 lines of interrupt controller (8.01-09). In order to avoid confusion the GS_DUMP.COM program prohibits reception of external interrupts via lines IRQ 5 and IRQ 6 for the time needed to display the dump. For this purpose commands in lines 236–23D read mask of 21h port, save its initial state in memory cell 03C5, set bits 05 and 06 in this mask, and write the altered mask back to port 21h.

Suitable replacements for INT 0D and INT 0E exception handlers are prepared beforehand in section 13 of GS_DUMP.COM program. Commands in 13th section make return address correction in stack and then return control back to interrupted program. Return address correction prevents hanging in a cycle of single command execution and enables resumption of interrupted program execution from the next command. Prepared handler's replacements are made active by commands in lines 23F–24D of section 8. These commands fill memory cells 03C8 and 03CC with actual segment address, and then twice call for subroutine 03A0, which exchanges interrupt handler's addresses in interrupt table with those prepared in memory cells 03C8 and 03CC. Since that moment the GS_DUMP.SYS program eliminates threat of computer's hanging caused by CPU's exceptions INT 0D and INT 0E.

As far as access rights in address space can be changed with discreteness not less than 16 bytes, readability of all bytes inside a 16-byte paragraph can be determined by one trial access attempt, undertaken in line 257 of section 9. When trial byte is accessible, then CPU while performing the LODSB command (7.03-53) in line 257 increments offset in register SI by 1. But when reading request causes segment protection actuation, then LODSB command is not carried out, and SI register doesn't get its increment. Command in line 258 compares current value in SI register with the former one, saved in 03CE memory cell. Equality of the compared values is an evidence of segment protection actuation. If CPU responds to trial access with segment protection actuation, then further attempts of access to any byte in the same paragraph are useless. For such lines of dump the following commands in section 9 display linear address, display a message about protection actuation, and calculate linear address for the next line of a dump. Then a jump follows to line 286, where number of currently displayed line in a dump is checked. If it is not the last line in dump, then a return is performed to start of trial access procedure, which is to be repeated for the next line of dump.

If SI register contents values, compared in line 258, happen to be different, then a jump from line 25C leads to the main dump display procedures in section 10. These procedures include a call for 0349 subroutine in order to display base linear address, a cycle 26E–273, displaying 16 bytes of dump, and then cycle 278–27D, displaying the same 16 bytes as codes ASCII. If current line of dump is not the last one, then a return is performed to start of section 9, where all the same operations will be repeated for the next line of dump. When dump processing is finished, commands in section 11 restore initial states of all affected elements. Subroutine 03A0 is called twice in order to restore former handler's addresses for interrupts INT 0D and INT 0E. Commands in lines 293–2A3 restore former mask in port 21h and former state of EFLAGS register. Commands in lines 2A4–2B6 restore former state of address bus line A20.

Having restored initial states, GS_DUMP.COM utility proceeds to concluding section 12. If dump has been displayed successfully, then final message is not displayed, and errorlevel is set to zero value. But if attempt to display a dump has failed, then the same operations in lines 2BF–2C4 display an error message. In line 2C7 a call for DOS's INT 21\AH=4Ch function terminates execution of GS_DUMP.COM utility.

Note 1: errorlevel code, left behind by GS_DUMP.COM utility, may present a separate interest. In particular, the largest errorlevel code 8 is left by GS_DUMP.COM utility after attempt of its execution inside the "DOS box" of WINDOWS OS. Registration of termination with errorlevel code 8 (3.15-03) enables to prevent execution of those programs, which shouldn't be launched inside "DOS box", for example, of the TURN_OFF.COM utility (9.05-02).

9.11 MS-DOS 7 loading alternatives edit

With respect to preservation of data and of main computer's operating system, any experiments with DOS are most safe when DOS is loaded from an external drive or from a removable media – a diskette, or flash card, or compact disc. This way of loading DOS, described in article 9.11-01, is common for emergency service. But it isn't sufficiently reliable for regular DOS usage, because in this role a lifetime of either removable media is not long enough.

For regular DOS usage a multi-alternative loading is arranged either by special boot managers or by loaders of some operating systems. Articles 9.11-02 and 9.11-03 describe usage for this purpose of WINDOWS-2000 and WINDOWS-XP loaders, and also of MS-DOS 7 itself, as far as initially it was devised for launching WINDOWS-95/98 operating systems. Of course, capabilities of MS-DOS 7 as a boot manager are poor, but it has a useful feature: compatibility with loadable BIOS extensions. May be, for alternative loading you'll have to choose just MS-DOS 7.

9.11-01 MS-DOS 7 loading from removable media edit

Ordinary 1.44 Mb diskette becomes bootable when a valid boot sector and DOS's system files are written onto that diskette. These operations under MS-DOS 7 are performed by SYS.COM utility (6.24). Under Windows you may download a selfextracting file-image of a bootable diskette via Internet, for example, from host http://1gighost.com/ed/jamiephiladelphia/ or from site http://anbcomp.com/files/bootdisk/ . Versions without DOS relocation to RAM-disk (files boot95b.exe, boot98c.exe, boot98sc.exe) are preferable, since standard bootable diskettes employ relocation method which may fail in modern computers. Because of the same reason formation of a bootable diskette shouldn't be ordered to standard formatting procedure. However, the mentioned drawback isn't inherent to bootable diskettes with MS-DOS 8, prepared by formatting procedure under Windows XP.

Having got a standard bootable diskette for DOS, you most probably wouldn't be satisfied with its poor contents. Minimal suitable set of utilities you'll have to prepare yourself. Many utilities for MS-DOS 7 can be got from Windows-95/98 release. Bootable diskette images with better sets of software can be found, for example, in http://www.netbootdisk.com/ and in http://www.multiboot.ru/ . Drivers and utilities, mentioned in configuration files in parts 6.25, 9.01, 9.04, 9.09 of this book, may be regarded as author's recommendations on compiling sets of software for bootable diskettes. Of course, in any case all specifications and paths in configuration files must correspond exactly to actual placement of drivers and utilities in the chosen bootable media. Your attempt to load MS-DOS 7 from diskette may fail because of inadequate parameter's specifications in BIOS Setup (about entering BIOS Setup in article 1.01). Type of your floppy drive must be specified properly in page "Main" of BIOS Setup. Besides that, page "Boot" of BIOS Setup must assign to floppy drive a higher boot device priority, than to fixed drive(s). In obsolete computers boot device priority was defined by order of disk's letter-names; therefore in page "Boot" a list of letter-names must start from letter "A", which denotes first floppy drive. In modern computers first floppy drive must be the first in a list of removable drives, and loading from removable drives must be given higher priority, than loading from fixed drives.

Unfortunately, active operating system on a diskette is too slow and causes intensive wear-out. It diminishes diskette's lifetime to 10–20 hours. Functioning becomes much more fast and reliable, if DOS is relocated from bootable diskette to a RAM-disk (examples in 9.04, 9.09). However, even in the latter case diskette can't be considered a fortunate bootable media. Slow and noisy DOS relocation procedure causes irritation. Besides that, total capacity of a diskette now seems insufficient. Optical discs seem much more attractive because of their greater reading speed, much larger capacity and longer lifetime. However, a long lifetime is inherent only to optical discs that are produced with fixed contents by matrix replication technology.

Writable discs implement quite different storage principle: decay of organic dye. An optically written track loses contrast each time it is read. Therefore, the active lifetime of writable optical discs is similar to that of diskettes. Rumors about reading speed are also half-true : track seek time in optical drives is about 100 mS, while in HDDs it is about 2 mS. Because of a short active lifetime and slow access, bootable optical disks need DOS relocation no less that floppy disks.

Some complexity is due to difference between optical disc's file systems and those "known" to DOS's core. Therefore, DOS can't be loaded immediately from optical disc. An image of other disk – a bootable disk with a "known" file system FAT12 or FAT-16 – must be written onto optical disc. BIOS system of your computer must be able to emulate a logical disk from this image, and only then MS-DOS 7 can be loaded from that emulated logical disk. If prototype for this image was a bootable diskette, then letter-name "A" is assigned to the emulated logical disk, whereas the real floppy drive is given the next letter-name "B". Almost all programs for writing optical disks are able to copy a bootable diskette into an image and to write this image on optical disc, thus making it bootable. For this role, no special preparation of bootable diskettes is needed. Configurations with MS-DOS 7 relocation onto a RAM-disk are quite suitable. Examples of such configurations are shown in parts 9.04 and 9.09. Of course, configurations should include those drivers and TSRs (5.08-04, 5.09-04), which enable access to optical disc's space beyond emulated logical disk and thus eliminate problem of its insufficient capacity.

DOS can't be loaded from optical disc, unless optical disc drive is registered by computer's BIOS system as bootable device. A list of registered bootable devices is shown on page "BOOT" of BIOS Setup program. In this list different BIOS versions specify either a class of device (for example, CD-ROM) or a particular type of device. A place of device in this list defines its priority. If optical disc drive is found there, then you have to specify that priority order, which will force BIOS to address optical disc drive before any of fixed disk drives is addressed. After that all you have to do is to insert a bootable optical disc into the drive in time before BIOS begins its start tests.

In modern computers registration of a drive by BIOS system may fail because of wrong specification of interface parameters. In order to check parameters of IDE interface you have to open page "Main" of BIOS Setup program and press button "IDE Configuration". A new page will be opened, where parameter "Onboard IDE operate Mode" should be given value "Compatible Mode", and parameter "Combined Mode Option" should be given that value, which corresponds to actual type of IDE connection. Nowadays most internal optical disc drives use parallel P-ATA connection. If there is no devices with serial S-ATA connection in your computer, then parameter "Combined Mode option" should be given the "P-ATA only" value, otherwise "P-ATA+S-ATA" value should be preferred. Any changes of interface parameters will have effect on a list of registered devices not at once, but after reboot.

In order to set parameters of USB interface, page "Advanced" of BIOS Setup program should be opened, and there button "USB Configuration" should be pressed. A new page will be opened, where parameter "USB Function" (or else "USB Controller") must have the "Enabled" value. This is enough for access to USB devices by means of drivers (5.07-05). But those BIOS systems, which enable to connect bootable devices via USB bus, provide more optional parameters. In latest versions of AMI BIOS there is parameter "Legacy USB support"; it must be enabled, if you intend to connect bootable devices via USB bus. It should be reminded that enabled state of "Legacy USB support" parameter may cause conflicts between BIOS and USB bus drivers (5.07-05), therefore USB bus drivers shouldn't be loaded in this case. If there are separate parameters for USB 2.0 controller in "USB Configuration" page, these parameters also should be given the "Enabled" value, and data transmission speed should be set to "HiSpeed".

As far as USB bus allows complex connection configurations, registration of bootable devices with USB interface may fail because of restrictions, imposed on BIOS startup tests. In order to remove these restrictions you have to open page "Boot" of BIOS Setup program, and there button "Boot setting configuration" should be pressed. A new page will be opened, where "Quick Boot" option should be disabled. Naturally, BIOS start tests will become about 3 seconds longer.

When BIOS system registers at least one storage device on USB bus, then in modern computers from page "USB Configuration" of BIOS Setup program you'll be able to open the next page "USB Mass Storage Device Configuration". In the latter page there is a list of all registered USB storage devices, and for each device the applied emulation method is shown. Inadequate emulation method specification can be one more reason of booting failure. By default emulation method is determined automatically, but errors may be caused by absence of media in the drive at the moment of test, by presence of unformatted media and even by media insertion just in course of tests.

Naturally, emulation method must correspond to class of device: for magnetic hard disk drives it should be defined as "Hard Disk", for external optical disc drives – as "CDROM", for external floppy drives – as "Floppy". But sometimes it isn't clear, which class of devices should correspond, for example, to a flash card. In such cases decision should be based on that flash cards with capacity 512 Mb and higher are always formatted as hard disks. For flash cards of smaller capacity a special emulation method may be applied – "Forced FDD", which means that the card will be presented by BIOS as "Big Floppy" irrespective to its actual format.

If you intend to subject a storage media to formatting procedure, then emulation method must correspond to the desired format type. Inaccurate emulation method specifications as "Forced FDD" and "Auto" may lead to unpredictable results. New unformatted storage devices are registered by BIOS, but are not given letter-names as logical disks. For their initial formatting modern programs should be preferred, for example, "Partition Magic" version 8.01 or higher. One of primary partitions must be made active. After a reboot the new partitions will be given letter-names, and then the active partition may be made bootable, for example, by SYS.COM utility (6.24). Configurations with relocation of MS-DOS 7 to a RAM-disk are not necessary, if the formatted media is indeed a hard disk.

Most BIOS systems wouldn't take control over those storage devices, which have media not emulated, but really formatted as "Big Floppy", i.e. without MBR. Normally such media are accessed by means of a driver, for example, ASPIDISK.SYS (5.07-03, 5.07-05). MBR can be written onto such media by utilities, mentioned in note 5 to article 6.13. After a reboot, BIOS will accept media with MBR as a HDD. Having been taken under BIOS control, such media can be treated as hard disk, can be formatted and made bootable.

When at least one storage device is recognized by BIOS system as a hard disk, then BIOS Setup program from its page "Boot" enables to open another page "Hard Disk Drives". There a list of all registered hard disk drives is shown. But only the first of these hard disk drives is regarded by BIOS as bootable device. If you want to boot the computer from an external storage device, registered as a hard disk drive, you should set this device in the first place in a list of drives in page "Hard Disk Drives" of BIOS Setup program. Just at once specification of the chosen storage device will appear in list of bootable devices on page "Boot Device Priority". There the chosen storage device must not necessarily be set in the first place, if devices with higher priority at that moment have no removable storage media inside.

Emulation method "Hard Disk" is suitable for storage devices with removable media, but with one required condition: the same removable media must be permanently present in this storage device since computer is switched on. Another removable disk in the same disk drive can't be read. If a storage device is emulated as hard disk, it will be assigned a lettername of hard disk. But if a removable storage media isn't present in this device at the moment when computer is switched on, then this storage device will get no letter-name at all. This may be beneficial, if your USB adapter has several slots for different types of flash cards, and you don't want to spend letter-names for those types of cards, which certainly wouldn't be used.

Storage devices, emulated as "Floppy" or as "Forced FDD", get a letter-name irrespective of presence or absence of their removable media. Those modern BIOS systems, which were tested by the author, allotted to these storage devices letter-names A: and B: only, and didn't allow to exchange initially inserted removable media. If computer has several such devices, then letter-names are assigned to the first two only; the rest such devices are inaccessible. If you intend to boot your computer from a storage device, registered by BIOS as a floppy, then you have to set it the first in a list on page "Removable Drives" of BIOS Setup program.

Now flash card adapters and solid-state storage devices with USB interface have became widespread. Such devices can be used for loading MS-DOS 7 and other operating systems. For this purpose storage devices with USB interface must be taken under BIOS control, just as external hard disk drives. Emulation method "Hard disk" should be attempted first. If storage device becomes accessible, hence it is formatted as hard disk. Otherwise emulation method "Forced FDD" should be preferred. In the latter case you have to check a place of corresponding storage device in a list on page "Removable Drives" of BIOS Setup program. It must be set there either in the first or in the second place, otherwise BIOS wouldn't allot letter-name to this storage device, and then it can't be addressed to.

If storage device is recognized as a hard disk, you have to check whether its primary partition has status of active (bootable) partition. As far as "Partition Magic" program deals with real HDDs only, partition's status in "fake" HDDs, physically represented by solid-state storage devices, should be checked and changed, if necessary, by FDISK.EXE utility, being launched with parameters /fprmt and /actok (6.13). Many BIOS versions don't provide bootability from HDD partitions with FAT-12 file system, but FDISK.EXE inevitably marks with FAT-12 all 16 Mb and smaller partitions. If necessary, partition table may be read (9.02-02) and written back (9.02-03) with file system identifier changed from 01h to 06h (A.13-6). After that the small partition concerned should be formatted by FORMAT.COM utility with /z:1 parameter (6.15), and thus will get the desired FAT-16 file system.

Second stage of solid-state bootable media preparation includes boot sector writing and DOS system files copying by SYS.COM utility (6.24). After that a DOS loading configuration should be prepared. Preferable configurations are those with DOS relocation onto a RAM-disk (9.04, 9.09), because many types of solid-state storage devices are slow, and all types of such devices withstand a limited number of overwriting cycles. According to the desired configuration on solid-state media a directory structure should be formed, and directories should be filled with required files.

When bootable solid-state media is prepared, the corresponding storage device should be set in the first place either in a list on page "Hard Disk Drives" or in a list on page "Removable Drives" – it depends on which emulation method is applied. If necessary, at this stage emulation method "Hard Disk" may be changed to "Forced FDD". When name of corresponding storage device appears on page "Boot Device Priority" of BIOS Setup program, it should be set there in a place with highest priority relative to all other devices, which are ready for booting at that moment. Then you have to close BIOS Setup program, saving the latest settings. After computer's reboot a DOS loading process starts from the prepared solid-state media.

9.11-02 Windows-2000/XP as boot manager for MS-DOS 7 edit

When MS-DOS 7 starts from a hard disk, it requires a primary partition with either FAT-16 or FAT-32 file system. Unlike MS-DOS 7, operating systems Windows-2000/XP can be installed in both primary and non-primary partitions, formatted with either FAT-32 or NTFS file system. Neither combination should be regarded as hopeless. Even if the whole hard disk in your computer is formatted as one NTFS partition, you may release some disk's space for DOS partition with Partition Magic program, and then any appropriate boot manager (for example, System Commander) can arrange alternative loading of either selected operating system. The way described below is a more simple opportunity, using nothing but proprietary loader of Windows-2000/XP operating systems.

Partition structures, comprising partitions with FAT-32 (or FAT-16) file system, may be inherited from former operating system(s). Windows-2000/XP can be installed over Windows-95/98, either losing or preserving an opportunity to load previous operating system. If your computer after being switched on shows a boot menu with item "Previous operating system", and if this previous operating system is just Windows-95/98, hence partition structure on your disk is at least partially inherited. In this case for arranging alternative loading of MS-DOS 7 you have to correct not the loading specifications of Windows-2000/XP, but preserved configuration files of previous Windows-95/98 operating system. Examples of such correction are shown in article 9.11-03.

If boot menu doesn't appear or doesn't contain a line "Previous operating system", then type of file system should be determined on that disk, which is claimed bootable in BIOS specifications. Most often it is disk C:. Having launched the Explorer program, you have to highlight the bootable disk, open context menu with right mouse's button, and choose in context menu the "Properties" item. A window will appear, where type of file system is shown. If this type is "NTFS", then MS-DOS 7 can't be installed in that partition. But if file system type is "FAT", then MS-DOS 7 can be installed. Installation will require BOOT.INI file records correction and copying of MS-DOS 7 files onto that disk.

Officially prescribed path to correction of records in BOOT.INI file starts from menu "Start" and goes via item "Settings", via "Control Panel", via icon "Performance and Maintenance", via item "System", via button "Advanced", via button "Startup and Recovery Settings" to final button "Edit". In the same window a non-zero menu indication time should be set. Corrected version of BOOT.INI should be saved and after that the opened windows must be twice closed with OK button click. Besides that, contents of BOOT.INI file may be corrected by MSCONFIG.EXE utility. It can be launched from command line in a window, opened via item "Run" in menu "Start".

Syntax in BOOT.INI file is the same as that in files MSDOS.SYS (5.01-01) and CONFIG.SYS (9.04-01, 9.09-01). Each line presents a separate specification, which begins with a name, separated from its value with equality sign. Headers of file's sections are enclosed in square brackets. There are two sections in BOOT.INI file: the first specifies loading parameters, the second presents a list of operating systems, which may be loaded. Here is an example of BOOT.INI file, enabling to load any of 3 operating systems :

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(3)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(3)\WINDOWS="Microsoft Windows XP...
multi(0)disk(0)rdisk(0)partition(2)\WINNT="Microsoft Windows 2000...
C:\bootsect.dos="Microsoft DOS 7.10"

In presented example the 5th and the 6th lines are truncated to size of page, in real file these lines are longer. But it doesn't matter: in any case the tails of truncated lines shouldn't be altered. From line's texts it becomes clear that the shown BOOT.INI file enables to load Windows-XP and Windows-2000 operating systems, installed in the 3rd and in the 2nd partitions of a single hard disk, so that the first partition remains free. As far as the first partition is left free, it may be used for a separate installation of MS-DOS 7. The last line in the shown example is just that line, which you have to add yourself in order to load MS-DOS 7, in particular, from disk C:. The words, enclosed in double quotes, represent just a name of menu item and have no effect on loading process.

Lines of BOOT.INI file are interpreted by NTLDR loader. The latter will "understand" the line you have wrote as command to find a file-image of boot sector BOOTSECT.DOS in the root directory of disk C:. If BOOTSECT.DOS file isn't present there, it should be created anew under MS-DOS 7, which may be loaded from diskette or other removable media. First, current boot sector should be saved into a file, as it is described in article 9.02-01. Then boot sector should be overwritten with SYS.COM utility (6.24). New boot sector similarly must be copied in a file, this time in a file named BOOTSECT.DOS. After that the former contents of boot sector must be restored from the previously saved file, also as it is recommended in article 9.02-01. The SYS.COM utility, used to overwrite boot sector, at the same time copies into the root directory system files COMMAND.COM and IO.SYS. Just the IO.SYS loader will be given control after execution of boot sector's code.

The next task is to ensure that all necessary files for MS-DOS 7 can be found along their proper paths. In the root directory of bootable disk the following files must be present :

IO.SYS – hidden system file: loader and the core of MS-DOS 7;
MSDOS.SYS – hidden system file: parameters of loading (5.01-01);
CONFIG.SYS – configuration file (9.01-01, 9.04-01, 9.11-03);
AUTOEXEC.BAT – configuration file (9.01-02, 9.04-02, 9.11-03);
COMMAND.COM – a read-only file: command interpreter (6.04).

Three files from this list – MSDOS.SYS, CONFIG.SYS, AUTOEXEC.BAT – you have to compose yourself. The articles where composition examples can be found are specified inside parentheses for each corresponding line in the list.

Besides root directory files, MS-DOS 7 needs drivers, specified in lines of configuration files, and also various programs. The latter may be selected from those described in chapter 6. Drivers and programs should be stored inside a directories structure, which is to be arranged on the same disk. All examples of configuration files in this book are designed for the same directories structure : drivers are stored in \DOS\DRV directory, original MS-DOS 7 files in \DOS\MS7 directory, file manager in \DOS\VC4 directory, all other files in \DOS\OTH directory. You may arrange other directories structure, but in any case it must be exactly consistent with references in your configuration files.

Which particular configuration example should be followed is a matter of choice. Sometimes the simplest version, shown in article 9.01, is quite sufficient. More often some version with DOS relocation to a RAM-disk should be preferred (9.04, 9.09). For experiments with other operating systems one more version is shown in article 9.11-03. You are free to choose and to compose those MS-DOS 7 loading configurations, which are the most suitable for your own tasks.

Note 1: when over MS-DOS 7 or over Windows-95/98 the Windows-2000/XP operating system is installed, the latter assigns attributes H (hidden) and S (system) to COMMAND.COM interpreter in the root directory. Therefore, calls to command interpreter from batch files are not executed. The mentioned attributes should be taken off with ATTRIB.EXE utility (6.01).

9.11-03 MS-DOS 7 as boot manager edit

Operating systems Windows-95/98 use MS-DOS 7 as primary loader, but don't reveal opportunities of its separate configuration. Meanwhile, it is possible and may be beneficial not only for MS-DOS 7 itself, but also for alternative loading of those other operating systems, which are able to start under DOS.

Below is a CONFIG.SYS file which enables to load Windows-95/98, two configurations of MS-DOS 7, and two other operating systems: QNX and Linux. The author has tested versions of QNX and Linux, requiring bootable disk with file system FAT-16. If you don't need anything more than a choice between Windows-95/98 and MS-DOS 7, then all the lines related to QNX and Linux may be omitted, and the filesystem on the bootable disk may be FAT-32 as well.

This CONFIG.SYS starts with a section [menu]. A choice of an item in menu directs interpretation further to sections [L08]–[L25]: each section corresponds to one of alternatives. Sections are named after corresponding labels in AUTOEXEC.BAT file. Empty lines between sections are inserted for visual clarity only and may be omitted.

[menu]
numlock off
menuitem=L08, Real mode MS-DOS 7
menuitem=L09, Protected mode MS-DOS 7
menuitem=L16, Microsoft's Windows-98
menuitem=L24, QNX v.6.0
menuitem=L25, Linux Slackware v.3.5
menudefault=L16,20

[L08]
device=\DOS\DRV\Himem.sys /v
device=\DOS\DRV\Umbpci.sys
include=S08
include=S09

[S08]
accdate c- d- edos=
high,umb,noauto
buffershigh=30,0
fileshigh=30,0
lastdrivehigh=Z
multitrack=On
fcbshigh=1,0
stackshigh=9,256

[L09]
device=\DOS\DRV\Himem.sys /v
device=\DOS\DRV\Emm386.exe ram v
include=S08
include=S09

[S09]
country=007,866,C:\DOS\DRV\Country.sys
devicehigh=\DOS\DRV\Dblbuff.sys
devicehigh=\DOS\DRV\Ifshlp.sys
devicehigh=\DOS\DRV\Setver.exe
devicehigh=\DOS\DRV\Atapimgr.sys /W:6 /T:5 /LUN
devicehigh=\DOS\DRV\Oakcdrom.sys /D:CD001
installhigh=\DOS\DRV\Shsucdx.com /D:CD001 /L:N /~+ /R /Q

[L16]
device=\WINDOWS\Himem.sys
include=S08
Country=007,866,C:\WINDOWS\COMMAND\Country.sys
devicehigh=\WINDOWS\Dblbuff.sys
devicehigh=\WINDOWS\Ifshlp.sys
devicehigh=\WINDOWS\Setver.exe
devicehigh=\WINDOWS\COMMAND\Display.sys con=(ega,,1)

[L24]
device=\QNX\boot\bin\loadqnx.sys C:\QNX\boot\fs\qnxbas.ifs

[L25]
device=\DOS\DRV\Himem.sys
include=S08
install=\linux\loadlin.exe @\linux\linparam.scr

[common]
installhigh=\DOS\DRV\Mouse.com
shell=C:\COMMAND.COM C:\ /E:2016 /L:511 /U:255 /p

Sections [L08] and [L09] in the shown version of CONFIG.SYS file load MS-DOS 7 as a separate operating system. Section [L09] provides ordinary type of access to UMB memory region by means of EMM386.EXE driver (5.04-02), but section [L08] provides another type of access, by means of UMBPCI.SYS driver (5.04-04) without switching CPU to protected mode. The latter alternative is necessary for experiments with real mode programs, for example, with DUSE.EXE (5.07-05) and with GS_LIMIT.COM (9.10-01) Each operating system should have its own directory tree. Presence of separate directory trees is not a required condition, but nevertheless is desirable: independence of directories contents makes multialternative loading more reliable.

Section [L16] for loading WINDOWS-95/98 includes a number of specifications, which usually are taken by default, but here are shown explicitly for the sake of consistency with separate loading of MS-DOS 7. Paths in [L16] section correspond to ordinary directories structure, created automatically during installation of WINDOWS-95/98 onto a bootable disk. But if in your computer directories structure is different, all references in configuration files must be made corresponding to actual structure.

Choice of menu items [L24] and [L25] transfers control to loaders of UNIX-like operating systems, which don't return control back to MS-DOS 7 loader. Therefore, commands in section [common] will not be performed, so that the only way back is via SHUTDOWN command with following reboot. Paths in sections [L24] and [L25] reflect directory structures, created for each UNIX-like operating system in course of unpacking their release packages.

Choice of menu items [L08], [L09] or [L16] enables to proceed to execution of commands in [common] section. In its last line the SHELL command transfers control to COMMAND.COM interpreter. After that the last stage of MS-DOS 7 loading begins – interpretation of commands in configuration file AUTOEXEC.BAT.

As far as further processing of alternatives [L08] and [L09] is the same, the whole variety of alternatives shrinks to two, and AUTOEXEC.BAT file becomes relatively simple. Particular contents of AUTOEXEC.BAT file may look, for example, like

@echo off
prompt $p$g
set dsk=C:
if not exist %dsk%\Temp\nul md %dsk%\Temp
set Temp=%dsk%\Temp
set dircmd= /A /O:GNE /P
goto %CONFIG%
:L08
:L09
Lh %dsk%\DOS\DRV\Keyrus.com
path ;
set VC=%dsk%\DOS\VC4
path=%VC%;%dsk%\DOS\OTH;%dsk%\;%dsk%\DOS\MS7
Vc.com /TSR /no2E /noswap
goto L25
:L16
path=%dsk%\WINDOWS;%dsk%\WINDOWS\COMMAND
Mode.com con codepage prepare=((866) %dsk%\WINDOWS\COMMAND\Ega3.cpi)
Mode.com con codepage select=866
Lh Keyb.com ru,866,%dsk%\WINDOWS\COMMAND\Keybrd3.sys
echo.
echo Loading Windows-98. Wait...
Win.com
:L24
:L25

In this version of AUTOEXEC.BAT file the lines 2–6 represent common part, assigning values to ordinary environmental variables. It is important not to leave any trailing space in lines 3 and 5, where values are assigned to variables DSK and TEMP. In the 7th line a jump occurs to the label, defined by value of CONFIG environmental variable. This value is just the code of selected menu item, assigned implicitly by IO.SYS loader during interpretation of [menu] section of CONFIG.SYS file.

As far as execution of AUTOEXEC.BAT file is reached after alternatives L08, L09, L16 only, a jump in the 7th line can be directed to labels :L08, :L09 and :L16.

Labels :L08 and :L09 are followed by a group of final commands for loading MS-DOS 7. Commands of this group define paths, specific for MS-DOS 7, and launch the Volkov Commander file manager.

Label :L16 is followed by another group of commands, presenting final operations for loading Windows-95/98 system. Here the PATH variable gets another paths, specific for Windows-95/98. Attention should be paid to usage of trivial national adaptation method, which enables proper switching of national codepages inside "DOS box" of Windows OS. In final lines the Windows GUI loader — file WIN.COM — is called for. Windows logotype will not be displayed, a textual message is displayed instead. When GUI loading is completed, this message becomes hidden under customary Windows desktop.

Note 1: though Windows XP OS is not devised for being started under MS-DOS 7, the required initial start conditions can be prepared by free utility Dostowxp.com. Its recent version, modified by V.Ashumov, can initiate loading of Windows Vista and Windows-7. Both original and modified versions of this utility are available at http://www.multiboot.ru/files.htm . Thus MS-DOS 7 is enabled to initiate launching of pre-installed modern Windows OS versions similarly to other OS launching examples, shown in article 9.11-03.

Note 2: installation of Linux OS is performed by recompiling its core with a specific set of drivers, required by hardware of a particular computer. Internet server ftp://ftp.wolfmountaingroup.org/pub/linuxware/ presents software package linuxware-09072008.tar.gz enabling to recompile the core 2.4 of Linux OS into a form of DOS's ordinary application. This recompiled core starts Linux under DOS, preserves DOS's structure intact and returns to DOS after shutdown. Core 2.4 is used in Mandrake Linux versions 8–10 and in many other modern clones of Linux OS.