Shell track

Shell

How the LainDOS prompt works after boot: built-ins, current directories, AUTOEXEC.BAT, PATH search, environment variables, and the small boundary where it intentionally stops short of COMMAND.COM compatibility.

From boot to A:\>

The shell is just another DOS program, but it is the user-facing control loop for the live image. It receives the initial PSP and environment, runs optional startup commands, then keeps handing child programs to EXEC until EXIT terminates it.

01
Startup
The kernel boots SHELL.COM as the first program, then the shell shrinks its PSPPSPThe DOS data block placed before each program, holding terminate vectors, the job file table, command tail, and environment pointer. block and prints the banner.
02
AUTOEXEC
If AUTOEXEC.BAT exists in the root, it runs before the first interactive prompt.
03
Prompt
The prompt is built from the current drive plus AH=47h current-directory state.
04
Dispatch
A line is uppercased, checked for built-ins or drive switches, then treated as COM, EXE, or BAT.
05
Return
Child programs return through AH=4Dh so the shell can recover state and print the next prompt.

Startup and dispatch

SHELL.COM owns the prompt loop after boot.

The kernel boots SHELL.COM like any other COM program. The shell immediately shrinks its allocation with AH=4Ah, prints LainDOS Shell, tries AUTOEXEC.BAT, and then enters the prompt loop.

Every interactive line is read with AH=0Ah, uppercased in-place, checked for a standalone drive switch, matched against the built-in command table, and finally passed to external command lookup.

programs/shell.asmNASM · 16-bit
16
17
18
25
 
27
28
29
30
31
32
 
41
43
55
56
57
mov bx, shell_resident_paras
mov ah, 0x4A
int 0x21
call run_autoexec
; ...
prompt:
call print_prompt
call read_line
call uppercase_line
call execute_line
jmp prompt
; ...
call change_drive_command
mov bx, command_table
.external:
call prepare_command
call run_command

Built-ins and current directory

Built-ins are small DOS API wrappers.

The built-ins intentionally stay narrow. DIR lists the current directory with FindFirst/FindNext, formats entries like MS-DOS with fixed 8.3 columns, and supports /P pagination. CD, MD, and RD call the matching directory APIs. TYPE opens a file and copies it to handle 1.

The prompt and drive switches are DOS state, not private shell variables. The prompt asks AH=19h for the current drive and AH=47h for the current directory; C: uses AH=0Eh to select the drive and relies on the kernel to reject missing drives.

programs/shell.asmNASM · 16-bit
78
79
88
92
93
94
98
103
533
536
537
 
680
702
704
724
728
736
 
1356
1357
1359
1360
1363
1365
1366
do_dir:
call parse_dir_args
call print_dir_header
mov dx, dir_pattern
mov cx, ATTR_DIR
mov ah, 0x4E
call print_dir_entry
call print_dir_summary
do_cd:
mov dx, si
mov ah, 0x3B
; ...
change_drive_command:
mov dl, al
mov ah, 0x0E
print_prompt:
mov ah, 0x47
mov dx, prompt_end
; ...
command_table:
dw exit_cmd, exit_shell
dw dir_cmd, do_dir
dw cd_cmd, do_cd
dw type_cmd, do_type
dw echo_cmd, do_echo
dw rem_cmd, do_rem

External command lookup

COM, EXE, and BAT are tried locally before PATH.

If the command has no extension, the shell first tries .COM, then .EXE, then .BAT in the current directory. Only after those fail does it walk PATH for COM, EXE, and BAT candidates.

Arguments are copied to a PSPPSPThe DOS data block placed before each program, holding terminate vectors, the job file table, command tail, and environment pointer.-compatible command tail. Successful child programs return through AH=4Dh so the shell consumes the exit status and restores DS before printing the next prompt.

programs/shell.asmNASM · 16-bit
785
788
826
828
831
833
835
840
 
872
875
883
891
897
909
932
933
964
979
980
prepare_command:
mov byte [command_has_ext], 0
cmp byte [command_has_ext], 0
mov al, '.'
mov al, 'C'
mov al, 'O'
mov al, 'M'
call build_cmd_tail
; ...
run_command:
call run_current_command
call run_current_command
call run_batch
call run_path_exec
call run_path_batch
mov ah, 0x4D
int 0x21
run_current_command:
mov ax, 0x4B00
int 0x21

AUTOEXEC.BAT and batch lines

Startup batch is a simple straight-line script.

run_autoexec just asks the batch runner to open AUTOEXEC.BAT. Missing files are ignored, which gives the same shell prompt on images without startup scripts.

Batch files are read into a fixed 512-byte buffer, split on CR/LF, uppercased, and executed through the same command path as interactive input. Empty lines are skipped; errors do not abort the script; nested batch files are rejected by batch_active.

programs/shell.asmNASM · 16-bit
989
992
993
 
1004
1005
1007
1009
1018
1030
1032
1038
1044
 
1235
1257
1259
1267
1268
run_autoexec:
mov dx, autoexec_name
call run_batch_named
; ...
run_batch_named:
cmp byte [batch_active], 0
mov byte [batch_active], 1
mov ah, 0x3D
mov ah, 0x3F
mov si, batch_buf
call batch_read_line
call execute_line
mov byte [batch_active], 0
; ...
batch_read_line:
cmp cx, 63
stosb
.eof:
stc

PATH lookup

The shell reads PATH from its PSP environment.

PATH lookup is deliberately caller-visible. The shell asks AH=62h for its PSPPSPThe DOS data block placed before each program, holding terminate vectors, the job file table, command tail, and environment pointer., follows PSPPSPThe DOS data block placed before each program, holding terminate vectors, the job file table, command tail, and environment pointer.:2Ch to the environment block, finds the PATH= string, and tries each semicolon-separated directory.

Commands that already contain a drive or slash bypass PATH. This keeps explicit paths deterministic while still allowing PATHRUN from A:BIN after the user changes into another directory.

programs/shell.asmNASM · 16-bit
1057
1061
1064
1065
1067
1070
1074
1077
 
1097
1104
1110
1118
1138
1140
 
1160
1180
1207
1214
1222
run_path_exec:
run_path_batch:
run_path_command:
cmp byte [command_has_path], 0
call find_path_value
call build_path_candidate
call run_candidate_exec
call run_batch_path
; ...
find_path_value:
mov ah, 0x62
mov ax, [es:0x2C]
mov si, path_env_name
.found:
mov [path_env_seg], ax
; ...
build_path_candidate:
cmp al, ';'
mov al, '\'
mov si, command_name
clc

Default environment

COMSPEC, PATH, PROMPT, and BLASTER are kernel-provided.

The boot program gets an MCBMCBA 16-byte DOS memory header that describes the allocated or free block immediately after it.-backed environment before it starts. Default variables include COMSPEC=A:SHELL.COM, PATH=A:;A:BIN, PROMPT=$P$G, and the conventional Sound Blaster string used by game setup tools.

When the shell EXECs a child, the loader copies or writes an environment and appends the DOS executable-path tail. The environment tests read PSPPSPThe DOS data block placed before each program, holding terminate vectors, the job file table, command tail, and environment pointer.:2Ch and verify both the variables and the tail visible to child programs.

src/kernel.asmNASM · 16-bit
1302
1309
1316
1317
1318
1325
 
1359
1365
1370
1378
1380
1382
1405
1406
 
2753
2754
2755
2756
2757
2758
init_environment:
call alloc_exec_environment
call write_environment_vars
mov ax, 1
stosw
mov si, fname_exe
; ...
write_environment_vars:
mov si, env_comspec_name
mov si, env_path_name
mov si, env_blaster
mov si, env_prompt
xor al, al
env_copy_drive_root:
mov al, [cs:dos_drive_letter]
; ...
env_comspec_name: db "COMSPEC=", 0
env_path_name: db "PATH=", 0
env_shell_name: db "SHELL.COM", 0
env_bin_dir: db "BIN", 0
env_blaster: db "BLASTER=A220 I5 D1 H5 P330 T6", 0
env_prompt: db "PROMPT=$P$G", 0

What this is not

LainDOS keeps shell behavior target-driven. The current shell is enough for test images, startup scripts, PATH-based game launchers, and manual emulator use, but it is not a full clone of COMMAND.COM.

No full COMMAND.COM compatibility: no SET command, prompt expansion, aliases, pipes, redirection, COPY, DEL, REN, or wildcard argument expansion.
Batch files are straight-line scripts. There is no CALL, GOTO, IF, FOR, labels, variable expansion, or nested batch execution.
AUTOEXEC.BAT is the only startup script. CONFIG.SYS and installable DOS device drivers remain out of scope.
Bad commands print an error and batch execution continues; this matches the current game/test needs rather than full DOS policy.