# 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

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

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

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

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
### 9.02-01 Boot sector saving and restoration

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

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

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

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

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

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

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

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

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

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

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

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

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 :
* report size of largest free XMS memory block ;
* accept input from keyboard ;
* 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.