Subversion Repositories SvarDOS

Rev

Rev 1849 | Rev 1853 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
349 mateuszvis 1
;
537 mateuszvis 2
; rmod - resident module of the SvarCOM command interpreter (NASM code)
349 mateuszvis 3
;
1730 mateusz.vi 4
; Copyright (C) 2021-2024 Mateusz Viste
349 mateuszvis 5
; MIT license
6
;
7
; this is installed in memory by the transient part of SvarCOM. it has only
8
; two jobs: providing a resident buffer for command history, environment, etc
9
; and respawning COMMAND.COM whenever necessary.
10
 
11
CPU 8086
459 mateuszvis 12
org 0x100
349 mateuszvis 13
 
459 mateuszvis 14
PSP_ENVSEG equ 0x2C
15
 
349 mateuszvis 16
section .text    ; all goes into code segment
17
 
350 mateuszvis 18
                 ; offset
19
SIG1 dw 0x1983   ;  +0
20
SIG2 dw 0x1985   ;  +2
21
SIG3 dw 0x2017   ;  +4
983 mateusz.vi 22
SIG4 dw 0x2019   ;  +6  acts also as a guardval to detect severe stack overflows
349 mateuszvis 23
 
983 mateusz.vi 24
; Buffer used to remember previous command, when SvarCOM calls the buffered
25
; input service at INT 21h,AH=0x0A.
26
; This buffer is right before the stack, so in case of a stack overflow event
27
; (for example because of a "too ambitious" TSR) only this buffer is damaged,
987 mateusz.vi 28
; and can be invalidated without much harm. To detect such damage, SvarCOM's
29
; transient part is appending a signature at the end of the buffer.
30
INPUTBUF: times 132 db 0 ; 130 bytes for the input buffer + 2 for signature
983 mateusz.vi 31
 
490 mateuszvis 32
; DOS int 21h functions that I use require at least 40 bytes of stack under
33
; DOS-C (FreeDOS) kernel, so here I reserve 64 bytes juste to be sure
34
STACKBUF db "XXX  SVARCOM RMOD BY MATEUSZ VISTE  XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
35
STACKPTR dw 0
349 mateuszvis 36
 
366 mateuszvis 37
; offset of the COMSPEC variable in the environment block, 0 means "use
38
; boot drive". this value is patched by the transient part of COMMAND.COM
1178 mateusz.vi 39
COMSPECPTR dw 0  ; +CEh
349 mateuszvis 40
 
1849 mateusz.vi 41
; fallback COMSPEC string used if no COMSPEC is present in the environment.
42
; drive is patched by the transient part of COMMAND.COM
1178 mateusz.vi 43
COMSPECBOOT db "@:\COMMAND.COM", 0 ; +D0h
350 mateuszvis 44
 
529 mateuszvis 45
; exit code of last application
1178 mateusz.vi 46
LEXCODE  db 0    ; +DFh
529 mateuszvis 47
 
460 mateuszvis 48
; ExecParamRec used by INT 21h, AX=4b00 (load and execute program), 14 bytes:
49
;  offset  size  content
50
;     +0     2   segment of environment for child (0 = current)
51
;     +2     4   address of command line to place at PSP:0080
52
;     +6     4   address of an FCB to be placed at PSP:005c
53
;    +0Ah    4   address of an FCB to be placed at PSP:006c
1178 mateusz.vi 54
EXEC_PARAM_REC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   ; +E0h
366 mateuszvis 55
 
548 mateuszvis 56
; Program to execute, preset by SvarCOM (128 bytes, ASCIIZ)
1178 mateusz.vi 57
EXECPROG: times 128 db 0                                     ; +EEh
460 mateuszvis 58
 
548 mateuszvis 59
; File where stdin and stdout should be redirected (0 = no redirection)
1178 mateusz.vi 60
REDIR_INFIL:     times 128 db 0     ; +16Eh
61
REDIR_OUTFIL:    times 128 db 0     ; +1EEh
62
REDIR_OUTAPPEND: dw 0               ; +26Eh
63
REDIR_DEL_STDIN: db 0               ; +270h  indicates that the stdin file
576 mateuszvis 64
                                    ;        should be deleted (pipes). This
65
                                    ;        MUST contain the 1st char of
66
                                    ;        REDIR_INFIL!
461 mateuszvis 67
 
1730 mateusz.vi 68
EXEC_LH: db 0                       ; +271h  EXECPROG to be loaded high?
69
ORIG_UMBLINKSTATE: db 0             ; +272h
70
ORIG_ALLOCSTRAT: db 0               ; +273h
1846 mateusz.vi 71
CTRLC_FLAG: db 0                    ; +274h  flag that says "aborted by CTRL+C"
1730 mateusz.vi 72
 
537 mateuszvis 73
; CTRL+BREAK (int 23h) handler
74
; According to the TechHelp! Manual: "If you want to abort (exit to the parent
75
; process), then set the carry flag and return via a FAR RET. This causes DOS
76
; to perform normal cleanup and exit to the parent." (otherwise use iret)
1846 mateusz.vi 77
BREAK_HANDLER:            ; +275h
78
mov [CTRLC_FLAG], byte 1  ; checked by SvarCOM to abort BAT files
537 mateuszvis 79
stc
80
retf
517 mateuszvis 81
 
1178 mateusz.vi 82
; INT 0x2E handler
83
INT2E:
84
xor ax, ax
85
iret
537 mateuszvis 86
 
1846 mateusz.vi 87
skipsig:                  ; +27Fh
537 mateuszvis 88
 
349 mateuszvis 89
; set up CS=DS=SS and point SP to my private stack buffer
90
mov ax, cs
91
mov ds, ax
92
mov es, ax
93
mov ss, ax
94
mov sp, STACKPTR
95
 
1178 mateusz.vi 96
; set up myself as break handler (int 0x23)
537 mateuszvis 97
mov ax, 0x2523  ; set int vector 23h
98
mov dx, BREAK_HANDLER
99
int 0x21
100
 
1178 mateusz.vi 101
; set up myself as int 0x2E handler ("pass command to shell")
102
mov ax, 0x252E
103
mov dx, INT2E ; TODO do something meaningful instead of a no-op
104
int 0x21
105
 
517 mateuszvis 106
; revert stdin/stdout redirections (if any) to their initial state
107
call REVERT_REDIR_IF_ANY
108
 
576 mateuszvis 109
; redirect stdin and/or stdout if required
543 mateuszvis 110
call REDIR_INOUTFILE_IF_REQUIRED
517 mateuszvis 111
 
1593 mateusz.vi 112
; should I execute command.com or a pre-set application?
113
cmp [EXECPROG], byte 0
460 mateuszvis 114
jz EXEC_COMMAND_COM
115
 
461 mateuszvis 116
; TODO: perhaps I should call the DOS SetPSP function here? But if I do, the
117
;       int 21h, ah=50h call freezes...
118
;mov ah, 0x50           ; DOS 2+ -- Set PSP
119
;mov bx, cs
120
;int 0x21
460 mateuszvis 121
 
1730 mateusz.vi 122
 
123
; LOADHIGH?
124
cmp [EXEC_LH], byte 0
125
je NO_LOADHIGH
126
; SAVE CURRENT UMB LINK STATE
127
mov ax, 0x5802  ; GET UMB LINK STATE
128
int 0x21
129
mov [ORIG_UMBLINKSTATE], al
130
; SAVE CURRENT ALLOCATION STRATEGY
131
mov ax, 0x5800
132
int 0x21
133
mov [ORIG_ALLOCSTRAT], al
134
 
135
; LOADHIGH: link in the UMB memory chain for enabling high-memory allocation
136
;           (and save initial status on stack)
137
mov ax, 0x5803  ; SET UMB LINK STATE */
138
mov bx, 1
139
int 0x21
140
; set strategy to 'last fit, try high then low memory'
141
mov ax, 0x5801
142
mov bx, 0x0082
143
int 0x21
144
NO_LOADHIGH:
145
 
460 mateuszvis 146
; exec an application preset (by SvarCOM) in the ExecParamRec
147
mov ax, 0x4B00         ; DOS 2+ - load & execute program
461 mateuszvis 148
mov dx, EXECPROG       ; DS:DX  - ASCIZ program name (preset at PSP[already)
460 mateuszvis 149
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
150
int 0x21
465 mateuszvis 151
mov [cs:EXECPROG], byte 0 ; do not run app again (+DS might have been changed)
460 mateuszvis 152
 
1730 mateusz.vi 153
; go to start if nothing else to do (this will enforce valid ds/ss/etc)
154
cmp [cs:EXEC_LH], byte 0
155
je skipsig
463 mateuszvis 156
 
1730 mateusz.vi 157
; restore UMB link state and alloc strategy to original values (but make sure
158
; to base it on CS since DS might have been trashed by the program)
159
mov ax, 0x5803
160
xor bx, bx
161
mov bl, [cs:ORIG_UMBLINKSTATE]
162
int 0x21
163
; restore original memory allocation strategy
164
mov ax, 0x5801
165
mov bl, [cs:ORIG_ALLOCSTRAT]
166
int 0x21
167
; turn off the LH flag
168
mov [cs:EXEC_LH], byte 0
169
 
170
 
171
jmp skipsig      ; enforce valid ds/ss/etc (can be lost after int 21,4b)
172
 
460 mateuszvis 173
EXEC_COMMAND_COM:
174
 
353 mateuszvis 175
; collect the exit code of previous application
176
mov ah, 0x4D
177
int 0x21
529 mateuszvis 178
mov [LEXCODE], al
353 mateuszvis 179
 
460 mateuszvis 180
; zero out the exec param block (14 bytes)
181
mov al, 0              ; byte to write
182
mov cx, 14             ; how many times
183
mov di, EXEC_PARAM_REC ; ES:DI = destination
184
cld                    ; stosb must move forward
185
rep stosb              ; repeat cx times
186
 
1730 mateusz.vi 187
; zero out the LH flag
188
mov [EXEC_LH], byte 0
189
 
366 mateuszvis 190
; preset the default COMSPEC pointer to ES:DX (ES is already set to DS)
191
mov dx, COMSPECBOOT
192
 
193
; do I have a valid COMSPEC?
194
or [COMSPECPTR], word 0
195
jz USEDEFAULTCOMSPEC
459 mateuszvis 196
; set ES:DX to actual COMSPEC (in env segment)
197
mov es, [PSP_ENVSEG]
366 mateuszvis 198
mov dx, [COMSPECPTR]
199
USEDEFAULTCOMSPEC:
200
 
349 mateuszvis 201
; prepare the exec param block
459 mateuszvis 202
mov ax, [PSP_ENVSEG]
350 mateuszvis 203
mov [EXEC_PARAM_REC], ax
465 mateuszvis 204
mov [EXEC_PARAM_REC+2], word CMDTAIL
442 mateuszvis 205
mov [EXEC_PARAM_REC+4], cs
349 mateuszvis 206
 
207
; execute command.com
208
mov ax, 0x4B00         ; DOS 2+ - load & execute program
366 mateuszvis 209
push es                ;
210
pop ds                 ;
211
;mov dx, COMSPEC       ; DS:DX  - ASCIZ program name (preset already)
212
push cs
213
pop es
349 mateuszvis 214
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
215
int 0x21
216
 
217
; if all went well, jump back to start
218
jnc skipsig
219
 
1851 mateusz.vi 220
; save error code into cl
221
mov cl, al
222
 
223
; display program name (in DS:DX), but first replace its nul terminator by a $
224
mov si, dx
225
PARSENEXTBYTE:
226
lodsb ; load byte at DS:SI into AL and inc SI (direction flag is clear already)
227
test al, al  ; is zero yet?
228
jnz PARSENEXTBYTE
229
mov [si], byte '$'   ; replace the nul terminator by a $ and display it
230
mov ah, 0x09
231
int 0x21
232
mov [si], byte 0     ; revert the nul terminator back to its place
233
 
366 mateuszvis 234
; restore DS=CS
235
mov bx, cs
236
mov ds, bx
237
 
349 mateuszvis 238
; update error string so it contains the error number
1851 mateusz.vi 239
mov al, '0'
240
add al, cl    ; the exec error code
241
mov [ERRLOAD + 6], al
349 mateuszvis 242
 
366 mateuszvis 243
; display error message
349 mateuszvis 244
mov ah, 0x09
245
mov dx, ERRLOAD
246
int 0x21
247
 
248
; wait for keypress
249
mov ah, 0x08
250
int 0x21
251
 
252
; back to program start
253
jmp skipsig
254
 
460 mateuszvis 255
; command.com tail arguments, in PSP format: length byte followed by args and
465 mateuszvis 256
; terminated with \r) - a single 0x0A byte is passed so SvarCOM knows it is
257
; called as respawn (as opposed to being invoked as a normal application)
258
; this allows multiple copies of SvarCOM to stack upon each other.
259
CMDTAIL db 0x01, 0x0A, 0x0D
442 mateuszvis 260
 
1851 mateusz.vi 261
ERRLOAD db ": ERR x, LOAD FAILED", 0x0A, 0x0D, '$'
517 mateuszvis 262
 
263
; variables used to revert stdin/stdout to their initial state
264
OLD_STDOUT dw 0xffff
265
OLD_STDIN  dw 0xffff
266
 
267
 
520 mateuszvis 268
; ****************************************************************************
517 mateuszvis 269
; *** ROUTINES ***************************************************************
520 mateuszvis 270
; ****************************************************************************
517 mateuszvis 271
 
520 mateuszvis 272
; ----------------------------------------------------------------------------
517 mateuszvis 273
; revert stdin/stdout redirections (if any) to their initial state
274
REVERT_REDIR_IF_ANY:
275
; is stdout redirected?
276
mov bx, [OLD_STDOUT]
277
cmp bx, 0xffff
278
je STDOUT_DONE
519 mateuszvis 279
; revert the stdout handle (dst in BX already)
517 mateuszvis 280
mov cx, 1        ; src handle (1=stdout)
281
mov ah, 0x46     ; redirect a handle
282
int 0x21
519 mateuszvis 283
; close the old handle (still in bx)
284
mov ah, 0x3e
285
int 0x21
517 mateuszvis 286
mov [OLD_STDOUT], word 0xffff ; mark stdout as "not redirected"
287
STDOUT_DONE:
543 mateuszvis 288
 
289
; is stdin redirected?
290
mov bx, [OLD_STDIN]
291
cmp bx, 0xffff
292
je STDIN_DONE
547 mateuszvis 293
; revert the stdin handle (dst in BX already)
543 mateuszvis 294
xor cx, cx       ; src handle (0=stdin)
295
mov ah, 0x46     ; redirect a handle
296
int 0x21
297
; close the old handle (still in bx)
298
mov ah, 0x3e
299
int 0x21
300
mov [OLD_STDIN], word 0xffff ; mark stdin as "not redirected"
576 mateuszvis 301
 
302
; delete stdin file if required
303
cmp [REDIR_DEL_STDIN], byte 0
304
je STDIN_DONE
305
; revert the original file and delete it
306
mov ah, [REDIR_DEL_STDIN]
307
mov [REDIR_INFIL], ah
308
mov ah, 0x41     ; DOS 2+ - delete file pointed at by DS:DX
309
mov dx, REDIR_INFIL
310
int 0x21
311
mov [REDIR_INFIL], byte 0
312
mov [REDIR_DEL_STDIN], byte 0
313
 
543 mateuszvis 314
STDIN_DONE:
315
 
517 mateuszvis 316
ret
520 mateuszvis 317
; ----------------------------------------------------------------------------
517 mateuszvis 318
 
319
 
520 mateuszvis 320
; ----------------------------------------------------------------------------
517 mateuszvis 321
; redirect stdout if REDIR_OUTFIL points to something
543 mateuszvis 322
REDIR_INOUTFILE_IF_REQUIRED:
548 mateuszvis 323
cmp [REDIR_OUTFIL], byte 0
517 mateuszvis 324
je NO_STDOUT_REDIR
548 mateuszvis 325
mov si, REDIR_OUTFIL   ; si = output file
517 mateuszvis 326
mov ax, 0x6c00         ; Extended Open/Create
327
mov bx, 1              ; access mode (0=read, 1=write, 2=r+w)
328
xor cx, cx             ; file attribs when(if) file is created (0=normal)
329
mov dx, [REDIR_OUTAPPEND] ; action if file exist (0x11=open, 0x12=truncate)
330
int 0x21               ; ax=handle on success (CF clear)
548 mateuszvis 331
mov [REDIR_OUTFIL], byte 0
517 mateuszvis 332
jc NO_STDOUT_REDIR     ; TODO: abort with an error message instead
520 mateuszvis 333
 
334
; jump to end of file if flag was 0x11 (required for >> redirections)
335
cmp [REDIR_OUTAPPEND], word 0x11
336
jne SKIP_JMPEOF
337
mov bx, ax
338
mov ax, 0x4202         ; jump to position EOF - CX:DX in handle BX
339
xor cx, cx
340
xor dx, dx
341
int 0x21
342
mov ax, bx             ; put my handle back in ax, as expected by later code
343
SKIP_JMPEOF:
344
 
517 mateuszvis 345
; duplicate current stdout so I can revert it later
346
push ax                ; save my file handle in stack
347
mov ah, 0x45           ; duplicate file handle BX
548 mateuszvis 348
mov bx, 1              ; 1 = stdout
517 mateuszvis 349
int 0x21               ; ax=new (duplicated) file handle
350
mov [OLD_STDOUT], ax   ; save the old handle in memory
520 mateuszvis 351
 
517 mateuszvis 352
; redirect stdout to my file
353
pop bx                 ; dst handle
354
mov cx, 1              ; src handle (1=stdout)
355
mov ah, 0x46           ; "redirect a handle"
356
int 0x21
520 mateuszvis 357
 
517 mateuszvis 358
; close the original file handle, I no longer need it
359
mov ah, 0x3e           ; close a file handle (handle in BX)
519 mateuszvis 360
int 0x21
517 mateuszvis 361
NO_STDOUT_REDIR:
543 mateuszvis 362
 
363
; *** redirect stdin if REDIR_INFIL points to something ***
548 mateuszvis 364
cmp [REDIR_INFIL], byte 0
543 mateuszvis 365
je NO_STDIN_REDIR
548 mateuszvis 366
mov dx, REDIR_INFIL    ; dx:dx = file
543 mateuszvis 367
mov ax, 0x3d00         ; open file for read
368
int 0x21               ; ax=handle on success (CF clear)
548 mateuszvis 369
mov [REDIR_INFIL], byte 0
543 mateuszvis 370
jc NO_STDIN_REDIR      ; TODO: abort with an error message instead
371
 
372
; duplicate current stdin so I can revert it later
373
push ax                ; save my file handle in stack
374
mov ah, 0x45           ; duplicate file handle BX
375
xor bx, bx             ; 0=stdin
376
int 0x21               ; ax=new (duplicated) file handle
377
mov [OLD_STDIN], ax    ; save the old handle in memory
378
 
379
; redirect stdout to my file
380
pop bx                 ; dst handle
381
xor cx, cx             ; src handle (0=stdin)
382
mov ah, 0x46           ; "redirect a handle"
383
int 0x21
384
 
385
; close the original file handle, I no longer need it
386
mov ah, 0x3e           ; close a file handle (handle in BX)
387
int 0x21
388
NO_STDIN_REDIR:
517 mateuszvis 389
ret
520 mateuszvis 390
; ----------------------------------------------------------------------------