
This implies that there is possibly no operating system present, rather, the software contains all the routines necessary for correct operation of the system.
In designing software for these types of applications, it is important to design the software in such a way as to perform the necessary hardware device initializations, and to handle shutdowns and error situations gracefully.
The software stored in ROM must contain this vector. Other tasks the software must do is,
When the target system is turned on, it computes its own ROM checksum then compares it with that stored in ROM. If these checksums are the same, the program can then continue.
checkrom: xor ax, ax ; clear ax mov ds, 0f000h ; start at location 0 in EPROM mov si, 0 ; offset 0 mov cx, 0ffffh ; number of characters in checksum chklp1: add ax, ds:[si] ; add character to total inc si ; point to next character loop chklp1 mov si, chksum ; now compare with rom chksum cmp ax, ds:[si] jne rombad next: org 0ffffh ;top of 32k eprom 27C256 chksum: db 04h ; calculated checksumTesting RAM can be done by writing values to the RAM areas and then verifying that the read-back value is the same.
It is vital that these routines are written in such a way that they do not rely upon the use of a STACK, ie, using CALLS, as a RAM error will cause the system to crash upon return from the test module.
The best patterns to use are AA and 55 as these test for flawed bits within each byte.
In most systems it will be necessary to initialize the dynamic RAM refresh subsystem before testing the RAM. The IBM-PC uses a timer counter chip (8239, channel 0), tied to a direct memory access chip (8253) to refresh the dynamic RAM by pseudo-reads.
RAM chips normally go bit faulty, in that one or more bits fail to assume the correct logic state. Using the patterns AA and 55 test for these faulty bits. There are more comprehensive memory tests available,
Which test is used is dependent upon how critical the application is. More comprehensive tests can take long periods to complete.
ramtest: mov ds, 0h ; RAM start 00000h mov cx, 4000h ; test 16k RAM mov si, 0 mov dx, 0AA55h ramlp1: mov al, dh mov byte ptr ds:[si], al ; write value cmp al, byte ptr ds:[si] ; read back and compare jne ramerr mov al, dl mov byte ptr ds:[si], al ; write value cmp al, byte ptr ds:[si] ; read back and compare jne ramerr inc si loop ramlp1Testing the CPU can be performed by loading the registers with values and doing known calculations, then testing the register result against the expected result (using the condition code register).
cputest: cli ; disable interrupts mov ah, 0d5h ; set sf, cf, zf, and af sahf jnc cpuerr ; test carry jnz cpuerr ; test zero jnp cpuerr ; test parity jns cpuerr ; test sign lahf ; transfer flags to ah mov cl, 5 ; test auxillary carry shr ah, cl ; rotate ah jnc cpuerr mov al, 40h ; test overflow flag shl al, 1 jno cpuerr xor ah, ah sahf ; clear sf, cf, zf, pf jc cpuerr jz cpuerr js cpuerr jp cpuerr lahf mov cl, 5 shr ah, cl jc cpuerr shl al, 1 jo cpuerr regtest: mov ax, 0ffffh mov ds, ax mov bx, ds mov es, bx mov cx, es mov ss, cx mov dx, ss mov sp, dx mov bp, sp mov si, bp mov di, si or ax, di jnz cpuerrModern processors such as the iAPX386 perform self tests at power on, with EAX holding 00000000h to indicate success. Register DX holds the Component and ID Revision numbers of the 386 chip.
The preferred order of testing is CPU, ROM then RAM.
In large memory systems, a parity generator is probably used. This requires either 0's or 1's to be written to the memory in order for the parity comparator system to work properly (the PC requires 0's).
The parity system in the PC is connected to the NMI interrupt line, so this may require a dummy service routine whilst the parity system is being initialized.
Normally, the NMI is disabled by writing to port A0h.
nmioff: mov al, 0 out 0a0h, al
The first step is obtaining data sheets for each of the chips then writing initialization routines as HLL algorithms. These can then be compiled or converted to assembler.
Determine the order in which the devices must be initialized, system chips like PIC's may need to done first. PROBABLY, the PIC and timers would be disabled till all the other hardware is initialized, at which time they would be re-activated.
You can't have interrupts being generated whilst setting up the system! The hardware should also be tested using dummy interrupts. Serial systems can be tested using LOOP BACK MODE, parallel systems for handshake signals.
Spare bits on parallel ports could be used for system checkout.
; irq vectors are located in RAM and initialised at power-up org 0004h ; irq 1 dw dummyint CSEG dummyint: mov ax, 0h iret testint: mov ax, 0ffh swi 1 cmp ax, 0 jne interr retUnexpected interrupts can also be tested for by initialising all interrupt vectors to the dummy interrupt handler, and waiting for a small delay.
testall: mov ax, 0ffh sti ; enable interrupts sub cx, cx tstlp1: loop tstlp1 ; wait for 1 second tstlp2: loop tstlp2 cmp ax, 0ffh ; any interrupts occur? jne interrException handling deals with the occurrence of unexpected hardware/software errors. These could be interrupts, overflows/wrap around, divide by zero, power down etc. All possible foreseeable exceptions should be catered for by software, or disabled by the use of hardware. This could be simple IRET statements.
Some systems use watchdog timers to prevent software runaway. A watchdog timer is programmed to generate an interrupt at regular intervals (say every 20milliseconds). The timer is loaded with a count value and decrements by one for each system clock period (or a divisor thereof). The main software periodically resets the timer (every 15ms) to prevent the interrupt occurring.
main()
{
reset_timer(1000);
for( ; ; ) {
.......
.......
reset_timer(1000);
.......
.......
reset_timer(1000);
}
}
When the software hangs, the timer will not be reset, thus generating an
interrupt. The service routine will then either recover, restart, or
shut the system down.
Error recovery means handling exceptions in such a way as to continue processing with little or no degrading of performance. It is best to incorporate several layers of error recovery so that a system is said to 'degrade gracefully'.
It should not fail instantly. Certain techniques used could be switching to an alternate RAM area, using timeouts and retrying (for parallel and serial ports), performing calculations etc.
retry re-initialize then retry abort
It is easy to add simple warm restart capability to the target system by using a variable located in RAM. At power-up, this variable is tested for a specific value, and if set, this indicates a warm-start is required.
warmboot: cmp warmbootflag, 1234h je warmstart mov warmbootflag, 1234h warmstart:
The processor stack registers is initialized to the STACK segment (SS and SP), and the processor data registers to the DATA segment (ES and DS).
Some compilers have special pre-processor directives to simplify segment placement, eg, the CC09 supports #DATA, #CODE and #CONST, which specify segment types to the assembler/linker utilities.
_TEXT Code _STACK Stack _BSS Un-initialized data, eg, static int i; _DATA Initialized data, eg, static int y = 3;NOTE: The IC86 compiler does not use underscores or the _BSS segment, whereas both TurboC and MSC do. Standard convention is to prefix underscores to variable and function names (except for the IC86 compiler).
The two data segments are combined forming a single concatenated data segment called DGROUP. The C compiler inserts code to zero out the _BSS segment before jumping to _main. Because the _BSS segment contains zero's, it does not need to be stored in ROM.
The _DATA segment, containing initialized values, will need to be appended at the end of the _CODE segment, and stored in ROM. During startup, the _DATA segment will be copied to RAM, then the DS register will be switched to the RAM area.
In creating rommable code, it is necessary to write your own startup code which replaces that of the C compiler (TurboC uses C0x.obj). The basic structure of the startup code is,
The startup code is normally written in assembler, so that direct control over segment placement can be achieved.
; startup.asm
_STACK segment para stack 'STACK'
db 1024 dup ('STACK');
stackend label word
_STACK ends
_TEXT segment byte public 'CODE'
assume CS:_TEXT, DS:_DATA, ES:_BSS, SS:_STACK
start: cli ; disable interrupts
mov ax, _STACK ; initialize stack
mov ss, ax
mov ax, offset stackend
mov sp, ax
...
...
sti
call _main
jmp start
_TEXT ends
; startup.asm _BSS segment para public 'BSS' _BSS ends _BSSEND segment byte public 'BSSEND' public endbss endbss label byte _BSSEND ends DGROUP group _BSS, _BSSEND _TEXT segment byte public 'CODE' assume CS:_TEXT, DS:DGROUP, ES:DGROUP, SS:_STACK start: cli ; disable interrupts ... ; initialize stack mov ax, seg _BSS mov es, ax mov cx, offset DGROUP:endbss mov di, 0 mov ax, 0 rep stosb ; write to ES:DI ... sti call _main jmp start _TEXT endsThe purpose of the BSSEND segment is to provide a means of determining how long _BSS is. As BSSEND occurs after _BSS, then zeroing everything from _BSS to _BSSEND will ensure that all of _BSS is zeroed out!
_DATA segment para public 'DATA' _DATA ends _DATAEND segment para public 'DATAEND' public enddata enddata label byte _DATAEND ends DGROUP group _DATA, _DATAEND, _BSS, _BSSEND _TEXT segment byte public 'CODE' _TEXT ends _TEXTEND segment para public 'CODEEND' public codeend db 16 dup (?) codeend label byte _TEXTEND ends CGROUP group _TEXT, _TEXTEND _TEXT segment assume CS:_TEXT, DS:DGROUP, ES:DGROUP, SS:_STACK start: cli ; disable interrupts ... ; initialize _STACK ... ; initialise _BSS mov ax, seg DGROUP mov es, ax ; point ES to _DATA mov cx, offset DGROUP:enddata mov si, 0 mov di, 0 assume ds:CGROUP:_TEXTEND mov ax, seg _TEXTEND:codeend inc ax mov ds, ax ; point DS to _CONST rep movsb ; copy _CONST to _DATA push es ; point DS to _DATA pop ds sti ; enable interrupts call _main jmp start _TEXT endsThe option chosen by the LOCATE utility is to duplicate the _DATA segment into a segment called _CONST. This is then attached to the end of the _CODE segment and stored in EPROM.
The startup code then copies this _CONST segment to the address reserved in RAM for _DATA. It figures out the start address of the _CONST segment using the dummy _TEXTEND segment, and the length of the _DATA segment using the dummy segment _DATAEND.
; tcstart.asm, for f600:0000
extrn _main:far
_text segment byte public 'CODE'
_text ends
_textend segment para public 'CODEEND'
_textend ends
_data segment para public 'DATA'
_data ends
_dataend segment para public 'DATAEND'
_dataend ends
_bss segment para public 'BSS'
_bss ends
_bssend segment byte public 'BSSEND'
_bssend ends
_stack segment para stack 'STACK'
_stack ends
DGROUP group _DATA, _DATAEND, _BSS, _BSSEND
CGROUP group _TEXT, _TEXTEND
_TEXT segment
assume CS:CGROUP, DS:DGROUP, ES:DGROUP, SS:_STACK
start: cli ; disable interrupts
mov ax, _STACK ; initialize stack
mov ss, ax
mov ax, offset stackend
mov sp, ax
mov ax, _BSS
mov es, ax
mov cx, offset DGROUP:enddata ; calculate length of _BSS
mov ax, offset DGROUP:endbss
sub ax, cx
mov cx, ax
mov di, 0
mov ax, 0
rep stosb ; write to ES:DI
mov ax, seg DGROUP
mov es, ax ; point ES to _DATA
mov cx, offset DGROUP:enddata
mov si, 0
mov di, 0
assume ds:CGROUP:_TEXTEND
mov ax, seg _TEXTEND:codeend
inc ax
mov ds, ax ; point DS to _CONST
rep movsb ; copy _CONST to _DATA
push es ; point DS to _DATA
pop ds
sti ; enable interrupts
call _main
jmp start
_TEXT ends
_TEXTEND segment
public codeend
db 16 dup (?)
codeend label byte
_TEXTEND ends
_STACK segment
db 1024 dup ('STACK');
stackend label word
_STACK ends
_DATAEND segment
public enddata
enddata label byte
_DATAEND ends
_BSSEND segment
public endbss
endbss label byte
_BSSEND ends
end
The C source is compiled using the command line version of the
compiler (tcc), then linked (using tlink) with the startup code
and any user supplied libraries or object files
(doit.bat).
tasm /mx tcstart tasm /mx tclib tcc -a- -c -f- -G- -K -B -ml -M -N- -O- -r- -v- -y- -Z- -S -O- %1.c tlink /m tcstart %1 tclib, %1, %1 locate %1 hexbin2 %1.hex %1.bin i f600The LOCATE utility (developed by R Naro, Dr Dobbs, Dec 1987) is then used to process the .EXE file and assign physical addresses to each of the segments. This creates a .HEX file which is then used to burn an EPROM. The locate utility uses a special configuration file which specifies where the segments are to be located.
filename_CODE filename_DATA filename_CONST ; if using the RAM directive STACKIt does not prefix variable names, segments or function names with an underscore. If the compiler option ROM is specified, all constants and string literals are placed into the filename_CODE segment.
The startup code declares the segments which are used to arrange them in the correct order in physical memory. Initialized constants are placed in the FILENAME_CONST segment (when using RAM, else they are placed in the filename_CODE if using the ROM directive).
All other data is placed into FILENAME_DATA. This creates a small problem in referencing data, so it is best to local variables and pass them as parameters to functions which access them (ie, do not use GLOBAL variables!).
Initialized global variables should be avoided if designing programs for ROM. The compiler generates an error if it encounters initialized data, eg,
static int y = 7; static int z;compiled with the command line
ic86 file.c LARGE ROMwill generate an error for the initialization of the variable y. As the DATA segment will be placed in RAM, any initial values will be lost.
The compiler also inserts code to initialize the DS register to the FILENAME_DATA segment. The following code will be used with the IC86 compiler.
; c86start.asm
name c86start
extrn main : far
STACK segment para stack 'STACK'
public top
db 1024 dup ('STACK')
top label word
STACK ends
CODE segment byte public 'CODE'
assume cs:CODE, SS:STACK
start proc far
entry: cli
mov ax, STACK
mov ss, ax
mov ax, offset top
mov sp, ax
sti
call main
jmp entry
start endp
CODE ends
end
The command line sequence to generate the file is,
asm86 c86start.asm ic86 test.c LARGE link86 c86start.obj,test.obj,c86clib.obj to test.lnk nobind loc86 test.lnk order(classes(stack,data,code)) & >> order(segments(STACK,TEST_DATA,CODE,TEST_CODE)) & >> ad(sm(stack(10000h),test_data(20000h))),(code(0f6000h))) oh86 test to test.hexThe segment maps look like,
C Program c86start +---------+ +---------+ | |FILENAME_CODE |+++++++++|STACK | | |+++++++++| | | |+++++++++| +---------+ +---------+ | |FILENAME_DATA |/////////|CODE | | |/////////| +---------+ +---------+ |+++++++++|STACK +---------+Combined segments after linking and locating are,
+---------+ |+++++++++|STACK (10000h) |+++++++++| |+++++++++| +---------+ | |FILENAME_DATA (20000h) | | +---------+ | | | | +---------+ |/////////|CODE (0F6000h) |/////////| +---------+ | |FILENAME_CODE | | | | +---------+ +---------+
When the PC is powered up, it first attempts to boot from floppy disk. If this is unsuccessful, it reads the keyboard then generates an int18h instruction. This normally performs the following instruction,
JMP F600:0000However, generating code to fit into this empty space requires special libraries or run time routines. Compilers like TC and MSC have libraries which use DOS in order to perform many functions. Since our turnkey system does not have DOS loaded, any systems we design using these libraries will certainly result in a system crash.
To overcome this, we shall design our own library of routines which emulate functions found in TC. These routines will look like functions in C, but will be written in assembler. First, lets look at creating a header file for these calls.
/* CROM.H, designed by SE2, 1990 for EMBEDDED CODE */
struct eightbit
{
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
struct sixteenbit
{
unsigned int ax, bx, cx, dx, si, di, cflag;
};
union REGS
{
struct sixteenbit x;
struct eightbit h;
};
/* function prototypes follow */
extern void outportb( unsigned int, char);
extern char inportb( unsigned int );
extern void int86( int, union REGS *, union REGS * );
extern void enable( void );
extern void disable( void );
extern void setvect( int, void (*ptr)( void ) );
The _int86 call provides a method of interfacing to the existing
ROM BIOS routines, which are accessed via software interrupt calls.
The library code, stored in TCLIB.ASM, looks like,
; TCLIB.ASM, library routine for int86() calls from EMBEDDED CODE ; designed by SE2, 1990 ; works with CROM.H ; turboc code must be compiled using ; tcc -a- -c -f- -G- -K -B -ml -M -N- -O- -r- -v- -y- -Z- -S -O- %1.c public _enable public _disable public _setvect public _int86 public _inportb public _outportb CODE segment para PUBLIC 'CODE' assume cs:CODE name tclib ; void enable( void ) ; ; enable interrupts _enable proc far cli ret _enable endp ; void disable( void ); ; disable interrupts _disable proc far sti ret _disable endp ; void setvect( int vetcnumber, void (*ptr)( void ) ); ; set interrupt vector to function code _setvect proc far push bp ;acess local vars inside functions mov bp,sp ;use bp to acess passed parameters push ax push bx push dx push es mov al, byte ptr [bp+6] ; int number mov ah, 0 rol ax, 1 rol ax, 1 mov bx, ax xor ax, ax mov es, ax mov dx, [bp+10] ; segment mov es:[bx+2], dx mov dx, [bp+8] ; offset mov es:[bx], dx pop es pop dx pop bx pop ax pop bp ret _setvect endp ; void int86( int intnumber, union REGS *, union REGS *); _int86 proc far push bp ;acess local vars inside functions mov bp,sp ;use bp to acess passed parameters push ax push bx push cx push dx push ds mov ax,[bp+10] ;get segment addr of inregs mov ds,ax mov si,[bp+8] ;copy .h.. into cpu registers mov ax,[si] ;ds:si mov bx,[si+2] mov cx,[si+4] mov dx,[si+6] mov di,[si+10] mov si,[si+8] cmp word ptr [bp+6],0010h je short int10 cmp word ptr [bp+6],0016h je short int16 mov ax,-1 jmp short exit int10: int 10h jmp short exit int16: int 16h jmp short exit exit: push ax mov ax,[bp+14] ;save regs in program model mov ds,ax pop ax mov si,[bp+12] mov [si],ax mov [si+2],bx mov [si+4],cx mov [si+6],dx mov [si+8],si mov [si+10],di jnc short ex2 mov word ptr [si+12],0001h ; carry flag set jmp short ex3 ex2: mov word ptr [si+12],0000h ; clear carry flag ex3: pop ds ;restore registers pop dx pop cx pop bx pop ax pop bp ret _int86 endp ; char inportb( int port ); _inportb proc far push bp ;acess local vars inside functions mov bp,sp ;use bp to acess passed parameters push dx mov dx,[bp+6] ; get port address xor ax,ax ; clear ax in al, dx ; read from port into al register mov ah, 00h ; ensure high byte of ax is cleared pop dx pop bp ret _inportb endp ; void outportb( int port, char value ); _outportb proc far push bp ;acess local vars inside functions mov bp,sp ;use bp to acess passed parameters push dx mov dx,[bp+6] ;get port address mov al, byte ptr [bp+8] out dx, al pop dx pop bp ret _outportb endp CODE ends end
The C program looks like,
/* test.c */
#include "crom.h"
union REGS regs;
void clear()
{
regs.h.ah = 15; /* get video mode */
int86( 0x10, ®s, ®s );
regs.h.ah = 0; /* set video mode */
int86( 0x10, ®s, ®s );
regs.x.dx = 0; /* set cursor */
regs.h.ah = 2;
int86( 0x10, ®s, ®s );
}
void writechar( char ch )
{
regs.h.ah = 14; /* write tty */
regs.h.bh = 0;
regs.h.al = ch;
int86( 0x10, ®s, ®s );
}
char readkey()
{
regs.h.ah = 0;
int86( 0x16, ®s, ®s );
return regs.h.al;
}
main()
{
char ch;
clear();
for( ; ; )
{
ch = readkey();
writechar( ch );
}
}
The sequence of commands necessary to generate the .EXE file is,
tasm /mx tcstart tasm /mx tclib tcc -a- -c -f- -G- -K -B -ml -M -N- -O- -r- -v- -y- -Z- -S -O- test tlink /m tcstart test tclib, test, testThe configuration file TEST.CFG has the following format,
dup DATA CONST class CODE = 0xf600 class STACK = 0x1000 class DATA = 0x2000 order DATA DATAEND BSS BSSEND order CODE CODEEND CONST rom CODE CONSTFinally, the memory image for storage in EPROM is generated using the LOCATE utility as follows,
locate test hexbin2 test.hex test.bin i f600
The library code, stored in CLIB.ASM, is altered slightly by removing the leading underscores from the function names.
The sequence of commands necessary to generate the EPROM image is,
asm86 c86f600.asm asm86 c86clib.asm ic86 test.c LARGE link86 c86f600.obj,test.obj,c86clib.obj to test.lnk nobind loc86 test.lnk order(classes(stack,data,code)) & >> order(segments(STACK,TEST_DATA,CODE,TEST_CODE)) & >> ad(sm(stack(10000h),test_data(20000h),(code(0f6000h))) oh86 test to test.hex
Examples of these are
The PC provides a low cost entry into turnkey systems. By adding a memory board and software in EPROM, it can quickly take the place of a custom developed system.
The TWO basic methods are,
The PC, upon power-on, executes routines in the ROM BIOS chip. The design of the computer allows the execution of programs stored in external EPROM, examples being those stored on EGA and XT hard disk controller cards.
The memory region C0000 - EFFFF was reserved for this purpose. The ROM BIOS routine, upon power on, checks each 2k block for a special signature byte (AA, 55), and, if found, executes the program stored there. In the case of the EGA card, this would patch the existing int 10h vector into its own code space, then return to the ROM BIOS routine. An embedded system would not return.
Lets look at the change required to implement a turnkey system out of our existing rommable code demonstration program we developed earlier, residing at F600:0000. We shall use the ic86 compiler to do this.
Below are the changes required to the c86start.asm file
; c86d000.asm for Intel Compiler
name c86d000
extrn main : far
STACK segment para stack 'STACK'
public top
db 1024 dup ('STACK')
top label word
STACK ends
CODE segment byte public 'CODE'
public entry
assume cs:CODE, SS:STACK
db 55h
db 0aah
db 40h ;length of eprom /512
jmp entry
start proc far
entry: cli
mov ax, STACK
mov ss, ax
mov ax, offset top
mov sp, ax
sti
push ds
mov ax, 0040h ; set warm boot flag
mov ds, ax
mov si, 0072h
mov word ptr [si], 1234h
pop ds
cli
mov al, 80h ; enable NMI
out 0A0h, al
mov al, 0bch ; enable 8259 PIC
out 21h, al
sti
call main
jmp entry
start endp
CODE ends
end
NOTE the extra code required to enable the NMI (used by the parity
system) and enable the default IRQ's on the 8259 PIC.
We shall use a standard PC Prototype board, which has had a 27256 Eprom socket wire wrapped as a 32k block beginning at 0D0000h. This board cost $120 and only took 30 minutes to wire-wrap and configure for use.
The command sequence to generate the HEX file is,
asm86 c86d000.asm asm86 c86clib.asm ic86 test.c LARGE link86 c86d000.obj,test.obj,c86clib.obj to test.lnk nobind loc86 test.lnk order(classes(stack,data,code)) & >> order(segments(STACK,TEST_DATA,CODE,TEST_CODE)) & >> ad(sm(stack(10000h),test_data(20000h),code(0d0000h))) oh86 test to test.hexThe code is generated then stored into EPROM. The memory board is configured to 0d0000h and installed with the EPROM into the target PC/XT. When the PC is turned on, it executes the self tests of CPU, ROM and RAM in the ROM BIOS before checking for external EPROMS. It will then find ours, and execute the code stored there.
In a turnkey system running in external space C8000-F6000, extra code in the startup file is necessary to enable the NMI and the PIC, as well as reset the warm boot flag used by the CTRL_ALT_DEL routine.
;SETUP 8237 DMA CONTROLLER mov al, 0ffh ; dmsa count 64k channel 0 out 1, al ; lsb out 1, al ; msb mov al, 58h ; set dma mode ch0 ready, auto int out 0bh, al mov al, 0 ; enable dma out 8, al out 0Ah, al ; set chn0 for refresh of RAM ;SETUP 8253 TIMER COUNTER CHIP mov ax, 1254h out 43h, al xchg ah, al out 41h, al mov cx, 3 mov al, 41h lp2: out 0bh, al inc al loop lp2 ;SETUP 8259 PRIORITY INTERRUPT CONTROLLER mov al, 13h ; ICW1-Edge, Single ICW4 out 20h, al mov al, 8 ; ICW2 - int type 8-F out 21h, al inc al out 21h, al ; ICW4 - buffered 8086 mode mov al, 0bch ; enable default IRQ's out 21h, al
SHELL=TEST.EXEWhen the PC/XT boots, the file will be run instead of command.com, the standard command-line DOS interface program. The advantages of this system are,
The DOS consists of two files, IO.SYS and MSDOS.SYS, which are loaded when the computer boots from disk. Using standard libraries simplifies design and coding, as well as allowing complicated software to be written (menus, database etc)