Subversion Repositories SvarDOS

Rev

Rev 548 | Rev 983 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 548 Rev 576
1
;
1
;
2
; rmod - resident module of the SvarCOM command interpreter (NASM code)
2
; rmod - resident module of the SvarCOM command interpreter (NASM code)
3
;
3
;
4
; Copyright (C) 2021 Mateusz Viste
4
; Copyright (C) 2021 Mateusz Viste
5
; MIT license
5
; MIT license
6
;
6
;
7
; this is installed in memory by the transient part of SvarCOM. it has only
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
8
; two jobs: providing a resident buffer for command history, environment, etc
9
; and respawning COMMAND.COM whenever necessary.
9
; and respawning COMMAND.COM whenever necessary.
10
 
10
 
11
CPU 8086
11
CPU 8086
12
org 0x100
12
org 0x100
13
 
13
 
14
PSP_ENVSEG equ 0x2C
14
PSP_ENVSEG equ 0x2C
15
 
15
 
16
section .text    ; all goes into code segment
16
section .text    ; all goes into code segment
17
 
17
 
18
                 ; offset
18
                 ; offset
19
SIG1 dw 0x1983   ;  +0
19
SIG1 dw 0x1983   ;  +0
20
SIG2 dw 0x1985   ;  +2
20
SIG2 dw 0x1985   ;  +2
21
SIG3 dw 0x2017   ;  +4
21
SIG3 dw 0x2017   ;  +4
22
SIG4 dw 0x2019   ;  +6  this acts also as a guardval to detect stack overflows
22
SIG4 dw 0x2019   ;  +6  this acts also as a guardval to detect stack overflows
23
 
23
 
24
; DOS int 21h functions that I use require at least 40 bytes of stack under
24
; DOS int 21h functions that I use require at least 40 bytes of stack under
25
; DOS-C (FreeDOS) kernel, so here I reserve 64 bytes juste to be sure
25
; DOS-C (FreeDOS) kernel, so here I reserve 64 bytes juste to be sure
26
STACKBUF db "XXX  SVARCOM RMOD BY MATEUSZ VISTE  XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
26
STACKBUF db "XXX  SVARCOM RMOD BY MATEUSZ VISTE  XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
27
STACKPTR dw 0
27
STACKPTR dw 0
28
 
28
 
29
; offset of the COMSPEC variable in the environment block, 0 means "use
29
; offset of the COMSPEC variable in the environment block, 0 means "use
30
; boot drive". this value is patched by the transient part of COMMAND.COM
30
; boot drive". this value is patched by the transient part of COMMAND.COM
31
COMSPECPTR dw 0  ; +4Ah
31
COMSPECPTR dw 0  ; +4Ah
32
 
32
 
33
; fallback COMSPEC string used if no COMPSEC is present in the environment
33
; fallback COMSPEC string used if no COMPSEC is present in the environment
34
; drive. drive is patched by the transient part of COMMAND.COM
34
; drive. drive is patched by the transient part of COMMAND.COM
35
COMSPECBOOT db "@:\COMMAND.COM", 0 ; +4Ch
35
COMSPECBOOT db "@:\COMMAND.COM", 0 ; +4Ch
36
 
36
 
37
; exit code of last application
37
; exit code of last application
38
LEXCODE  db 0    ; +5Bh
38
LEXCODE  db 0    ; +5Bh
39
 
39
 
40
; ExecParamRec used by INT 21h, AX=4b00 (load and execute program), 14 bytes:
40
; ExecParamRec used by INT 21h, AX=4b00 (load and execute program), 14 bytes:
41
;  offset  size  content
41
;  offset  size  content
42
;     +0     2   segment of environment for child (0 = current)
42
;     +0     2   segment of environment for child (0 = current)
43
;     +2     4   address of command line to place at PSP:0080
43
;     +2     4   address of command line to place at PSP:0080
44
;     +6     4   address of an FCB to be placed at PSP:005c
44
;     +6     4   address of an FCB to be placed at PSP:005c
45
;    +0Ah    4   address of an FCB to be placed at PSP:006c
45
;    +0Ah    4   address of an FCB to be placed at PSP:006c
46
EXEC_PARAM_REC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   ; +5Ch
46
EXEC_PARAM_REC db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   ; +5Ch
47
 
47
 
48
; Program to execute, preset by SvarCOM (128 bytes, ASCIIZ)
48
; Program to execute, preset by SvarCOM (128 bytes, ASCIIZ)
49
EXECPROG: times 128 db 0                                     ; +6Ah
49
EXECPROG: times 128 db 0                                     ; +6Ah
50
 
50
 
51
; File where stdin and stdout should be redirected (0 = no redirection)
51
; File where stdin and stdout should be redirected (0 = no redirection)
52
REDIR_INFIL:     times 128 db 0     ; +EAh
52
REDIR_INFIL:     times 128 db 0     ; +EAh
53
REDIR_OUTFIL:    times 128 db 0     ; +16Ah
53
REDIR_OUTFIL:    times 128 db 0     ; +16Ah
54
REDIR_OUTAPPEND: dw 0               ; +1EAh
54
REDIR_OUTAPPEND: dw 0               ; +1EAh
-
 
55
REDIR_DEL_STDIN: db 0               ; +1ECh  indicates that the stdin file
-
 
56
                                    ;        should be deleted (pipes). This
-
 
57
                                    ;        MUST contain the 1st char of
-
 
58
                                    ;        REDIR_INFIL!
55
 
59
 
56
; CTRL+BREAK (int 23h) handler
60
; CTRL+BREAK (int 23h) handler
57
; According to the TechHelp! Manual: "If you want to abort (exit to the parent
61
; According to the TechHelp! Manual: "If you want to abort (exit to the parent
58
; process), then set the carry flag and return via a FAR RET. This causes DOS
62
; process), then set the carry flag and return via a FAR RET. This causes DOS
59
; to perform normal cleanup and exit to the parent." (otherwise use iret)
63
; to perform normal cleanup and exit to the parent." (otherwise use iret)
60
BREAK_HANDLER:            ; +1ECh
64
BREAK_HANDLER:            ; +1EDh
61
stc
65
stc
62
retf
66
retf
63
 
67
 
64
 
68
 
65
skipsig:                  ; +1EEh
69
skipsig:                  ; +1EFh
66
 
70
 
67
; set up CS=DS=SS and point SP to my private stack buffer
71
; set up CS=DS=SS and point SP to my private stack buffer
68
mov ax, cs
72
mov ax, cs
69
mov ds, ax
73
mov ds, ax
70
mov es, ax
74
mov es, ax
71
mov ss, ax
75
mov ss, ax
72
mov sp, STACKPTR
76
mov sp, STACKPTR
73
 
77
 
74
; set up myself as break handler
78
; set up myself as break handler
75
mov ax, 0x2523  ; set int vector 23h
79
mov ax, 0x2523  ; set int vector 23h
76
mov dx, BREAK_HANDLER
80
mov dx, BREAK_HANDLER
77
int 0x21
81
int 0x21
78
 
82
 
79
; revert stdin/stdout redirections (if any) to their initial state
83
; revert stdin/stdout redirections (if any) to their initial state
80
call REVERT_REDIR_IF_ANY
84
call REVERT_REDIR_IF_ANY
81
 
85
 
82
; redirect stdout if required
86
; redirect stdin and/or stdout if required
83
call REDIR_INOUTFILE_IF_REQUIRED
87
call REDIR_INOUTFILE_IF_REQUIRED
84
 
88
 
85
; should I executed command.com or a pre-set application?
89
; should I executed command.com or a pre-set application?
86
or [EXECPROG], byte 0
90
or [EXECPROG], byte 0
87
jz EXEC_COMMAND_COM
91
jz EXEC_COMMAND_COM
88
 
92
 
89
; TODO: perhaps I should call the DOS SetPSP function here? But if I do, the
93
; TODO: perhaps I should call the DOS SetPSP function here? But if I do, the
90
;       int 21h, ah=50h call freezes...
94
;       int 21h, ah=50h call freezes...
91
;mov ah, 0x50           ; DOS 2+ -- Set PSP
95
;mov ah, 0x50           ; DOS 2+ -- Set PSP
92
;mov bx, cs
96
;mov bx, cs
93
;int 0x21
97
;int 0x21
94
 
98
 
95
; exec an application preset (by SvarCOM) in the ExecParamRec
99
; exec an application preset (by SvarCOM) in the ExecParamRec
96
mov ax, 0x4B00         ; DOS 2+ - load & execute program
100
mov ax, 0x4B00         ; DOS 2+ - load & execute program
97
mov dx, EXECPROG       ; DS:DX  - ASCIZ program name (preset at PSP[already)
101
mov dx, EXECPROG       ; DS:DX  - ASCIZ program name (preset at PSP[already)
98
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
102
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
99
int 0x21
103
int 0x21
100
mov [cs:EXECPROG], byte 0 ; do not run app again (+DS might have been changed)
104
mov [cs:EXECPROG], byte 0 ; do not run app again (+DS might have been changed)
101
 
105
 
102
jmp short skipsig      ; enforce valid ds/ss/etc (can be lost after int 21,4b)
106
jmp short skipsig      ; enforce valid ds/ss/etc (can be lost after int 21,4b)
103
 
107
 
104
EXEC_COMMAND_COM:
108
EXEC_COMMAND_COM:
105
 
109
 
106
; collect the exit code of previous application
110
; collect the exit code of previous application
107
mov ah, 0x4D
111
mov ah, 0x4D
108
int 0x21
112
int 0x21
109
mov [LEXCODE], al
113
mov [LEXCODE], al
110
 
114
 
111
; zero out the exec param block (14 bytes)
115
; zero out the exec param block (14 bytes)
112
mov al, 0              ; byte to write
116
mov al, 0              ; byte to write
113
mov cx, 14             ; how many times
117
mov cx, 14             ; how many times
114
mov di, EXEC_PARAM_REC ; ES:DI = destination
118
mov di, EXEC_PARAM_REC ; ES:DI = destination
115
cld                    ; stosb must move forward
119
cld                    ; stosb must move forward
116
rep stosb              ; repeat cx times
120
rep stosb              ; repeat cx times
117
 
121
 
118
; preset the default COMSPEC pointer to ES:DX (ES is already set to DS)
122
; preset the default COMSPEC pointer to ES:DX (ES is already set to DS)
119
mov dx, COMSPECBOOT
123
mov dx, COMSPECBOOT
120
 
124
 
121
; do I have a valid COMSPEC?
125
; do I have a valid COMSPEC?
122
or [COMSPECPTR], word 0
126
or [COMSPECPTR], word 0
123
jz USEDEFAULTCOMSPEC
127
jz USEDEFAULTCOMSPEC
124
; set ES:DX to actual COMSPEC (in env segment)
128
; set ES:DX to actual COMSPEC (in env segment)
125
mov es, [PSP_ENVSEG]
129
mov es, [PSP_ENVSEG]
126
mov dx, [COMSPECPTR]
130
mov dx, [COMSPECPTR]
127
USEDEFAULTCOMSPEC:
131
USEDEFAULTCOMSPEC:
128
 
132
 
129
; prepare the exec param block
133
; prepare the exec param block
130
mov ax, [PSP_ENVSEG]
134
mov ax, [PSP_ENVSEG]
131
mov [EXEC_PARAM_REC], ax
135
mov [EXEC_PARAM_REC], ax
132
mov [EXEC_PARAM_REC+2], word CMDTAIL
136
mov [EXEC_PARAM_REC+2], word CMDTAIL
133
mov [EXEC_PARAM_REC+4], cs
137
mov [EXEC_PARAM_REC+4], cs
134
 
138
 
135
; execute command.com
139
; execute command.com
136
mov ax, 0x4B00         ; DOS 2+ - load & execute program
140
mov ax, 0x4B00         ; DOS 2+ - load & execute program
137
push es                ;
141
push es                ;
138
pop ds                 ;
142
pop ds                 ;
139
;mov dx, COMSPEC       ; DS:DX  - ASCIZ program name (preset already)
143
;mov dx, COMSPEC       ; DS:DX  - ASCIZ program name (preset already)
140
push cs
144
push cs
141
pop es
145
pop es
142
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
146
mov bx, EXEC_PARAM_REC ; ES:BX  - parameter block pointer
143
int 0x21
147
int 0x21
144
 
148
 
145
; if all went well, jump back to start
149
; if all went well, jump back to start
146
jnc skipsig
150
jnc skipsig
147
 
151
 
148
; restore DS=CS
152
; restore DS=CS
149
mov bx, cs
153
mov bx, cs
150
mov ds, bx
154
mov ds, bx
151
 
155
 
152
; update error string so it contains the error number
156
; update error string so it contains the error number
153
add al, '0'
157
add al, '0'
154
mov [ERRLOAD + 4], al
158
mov [ERRLOAD + 4], al
155
 
159
 
156
; display error message
160
; display error message
157
mov ah, 0x09
161
mov ah, 0x09
158
mov dx, ERRLOAD
162
mov dx, ERRLOAD
159
int 0x21
163
int 0x21
160
 
164
 
161
; wait for keypress
165
; wait for keypress
162
mov ah, 0x08
166
mov ah, 0x08
163
int 0x21
167
int 0x21
164
 
168
 
165
; back to program start
169
; back to program start
166
jmp skipsig
170
jmp skipsig
167
 
171
 
168
; command.com tail arguments, in PSP format: length byte followed by args and
172
; command.com tail arguments, in PSP format: length byte followed by args and
169
; terminated with \r) - a single 0x0A byte is passed so SvarCOM knows it is
173
; terminated with \r) - a single 0x0A byte is passed so SvarCOM knows it is
170
; called as respawn (as opposed to being invoked as a normal application)
174
; called as respawn (as opposed to being invoked as a normal application)
171
; this allows multiple copies of SvarCOM to stack upon each other.
175
; this allows multiple copies of SvarCOM to stack upon each other.
172
CMDTAIL db 0x01, 0x0A, 0x0D
176
CMDTAIL db 0x01, 0x0A, 0x0D
173
 
177
 
174
ERRLOAD db "ERR x, FAILED TO LOAD COMMAND.COM", 13, 10, '$'
178
ERRLOAD db "ERR x, FAILED TO LOAD COMMAND.COM", 13, 10, '$'
175
 
179
 
176
; variables used to revert stdin/stdout to their initial state
180
; variables used to revert stdin/stdout to their initial state
177
OLD_STDOUT dw 0xffff
181
OLD_STDOUT dw 0xffff
178
OLD_STDIN  dw 0xffff
182
OLD_STDIN  dw 0xffff
179
 
183
 
180
 
184
 
181
; ****************************************************************************
185
; ****************************************************************************
182
; *** ROUTINES ***************************************************************
186
; *** ROUTINES ***************************************************************
183
; ****************************************************************************
187
; ****************************************************************************
184
 
188
 
185
; ----------------------------------------------------------------------------
189
; ----------------------------------------------------------------------------
186
; revert stdin/stdout redirections (if any) to their initial state
190
; revert stdin/stdout redirections (if any) to their initial state
187
; all memory accesses are CS-prefixes because this code may be called at
-
 
188
; times when DS is out of whack.
-
 
189
REVERT_REDIR_IF_ANY:
191
REVERT_REDIR_IF_ANY:
190
; is stdout redirected?
192
; is stdout redirected?
191
mov bx, [OLD_STDOUT]
193
mov bx, [OLD_STDOUT]
192
cmp bx, 0xffff
194
cmp bx, 0xffff
193
je STDOUT_DONE
195
je STDOUT_DONE
194
; revert the stdout handle (dst in BX already)
196
; revert the stdout handle (dst in BX already)
195
mov cx, 1        ; src handle (1=stdout)
197
mov cx, 1        ; src handle (1=stdout)
196
mov ah, 0x46     ; redirect a handle
198
mov ah, 0x46     ; redirect a handle
197
int 0x21
199
int 0x21
198
; close the old handle (still in bx)
200
; close the old handle (still in bx)
199
mov ah, 0x3e
201
mov ah, 0x3e
200
int 0x21
202
int 0x21
201
mov [OLD_STDOUT], word 0xffff ; mark stdout as "not redirected"
203
mov [OLD_STDOUT], word 0xffff ; mark stdout as "not redirected"
202
STDOUT_DONE:
204
STDOUT_DONE:
203
 
205
 
204
; is stdin redirected?
206
; is stdin redirected?
205
mov bx, [OLD_STDIN]
207
mov bx, [OLD_STDIN]
206
cmp bx, 0xffff
208
cmp bx, 0xffff
207
je STDIN_DONE
209
je STDIN_DONE
208
; revert the stdin handle (dst in BX already)
210
; revert the stdin handle (dst in BX already)
209
xor cx, cx       ; src handle (0=stdin)
211
xor cx, cx       ; src handle (0=stdin)
210
mov ah, 0x46     ; redirect a handle
212
mov ah, 0x46     ; redirect a handle
211
int 0x21
213
int 0x21
212
; close the old handle (still in bx)
214
; close the old handle (still in bx)
213
mov ah, 0x3e
215
mov ah, 0x3e
214
int 0x21
216
int 0x21
215
mov [OLD_STDIN], word 0xffff ; mark stdin as "not redirected"
217
mov [OLD_STDIN], word 0xffff ; mark stdin as "not redirected"
-
 
218
 
-
 
219
; delete stdin file if required
-
 
220
cmp [REDIR_DEL_STDIN], byte 0
-
 
221
je STDIN_DONE
-
 
222
; revert the original file and delete it
-
 
223
mov ah, [REDIR_DEL_STDIN]
-
 
224
mov [REDIR_INFIL], ah
-
 
225
mov ah, 0x41     ; DOS 2+ - delete file pointed at by DS:DX
-
 
226
mov dx, REDIR_INFIL
-
 
227
int 0x21
-
 
228
mov [REDIR_INFIL], byte 0
-
 
229
mov [REDIR_DEL_STDIN], byte 0
-
 
230
 
216
STDIN_DONE:
231
STDIN_DONE:
217
 
232
 
218
ret
233
ret
219
; ----------------------------------------------------------------------------
234
; ----------------------------------------------------------------------------
220
 
235
 
221
 
236
 
222
; ----------------------------------------------------------------------------
237
; ----------------------------------------------------------------------------
223
; redirect stdout if REDIR_OUTFIL points to something
238
; redirect stdout if REDIR_OUTFIL points to something
224
REDIR_INOUTFILE_IF_REQUIRED:
239
REDIR_INOUTFILE_IF_REQUIRED:
225
cmp [REDIR_OUTFIL], byte 0
240
cmp [REDIR_OUTFIL], byte 0
226
je NO_STDOUT_REDIR
241
je NO_STDOUT_REDIR
227
mov si, REDIR_OUTFIL   ; si = output file
242
mov si, REDIR_OUTFIL   ; si = output file
228
mov ax, 0x6c00         ; Extended Open/Create
243
mov ax, 0x6c00         ; Extended Open/Create
229
mov bx, 1              ; access mode (0=read, 1=write, 2=r+w)
244
mov bx, 1              ; access mode (0=read, 1=write, 2=r+w)
230
xor cx, cx             ; file attribs when(if) file is created (0=normal)
245
xor cx, cx             ; file attribs when(if) file is created (0=normal)
231
mov dx, [REDIR_OUTAPPEND] ; action if file exist (0x11=open, 0x12=truncate)
246
mov dx, [REDIR_OUTAPPEND] ; action if file exist (0x11=open, 0x12=truncate)
232
int 0x21               ; ax=handle on success (CF clear)
247
int 0x21               ; ax=handle on success (CF clear)
233
mov [REDIR_OUTFIL], byte 0
248
mov [REDIR_OUTFIL], byte 0
234
jc NO_STDOUT_REDIR     ; TODO: abort with an error message instead
249
jc NO_STDOUT_REDIR     ; TODO: abort with an error message instead
235
 
250
 
236
; jump to end of file if flag was 0x11 (required for >> redirections)
251
; jump to end of file if flag was 0x11 (required for >> redirections)
237
cmp [REDIR_OUTAPPEND], word 0x11
252
cmp [REDIR_OUTAPPEND], word 0x11
238
jne SKIP_JMPEOF
253
jne SKIP_JMPEOF
239
mov bx, ax
254
mov bx, ax
240
mov ax, 0x4202         ; jump to position EOF - CX:DX in handle BX
255
mov ax, 0x4202         ; jump to position EOF - CX:DX in handle BX
241
xor cx, cx
256
xor cx, cx
242
xor dx, dx
257
xor dx, dx
243
int 0x21
258
int 0x21
244
mov ax, bx             ; put my handle back in ax, as expected by later code
259
mov ax, bx             ; put my handle back in ax, as expected by later code
245
SKIP_JMPEOF:
260
SKIP_JMPEOF:
246
 
261
 
247
; duplicate current stdout so I can revert it later
262
; duplicate current stdout so I can revert it later
248
push ax                ; save my file handle in stack
263
push ax                ; save my file handle in stack
249
mov ah, 0x45           ; duplicate file handle BX
264
mov ah, 0x45           ; duplicate file handle BX
250
mov bx, 1              ; 1 = stdout
265
mov bx, 1              ; 1 = stdout
251
int 0x21               ; ax=new (duplicated) file handle
266
int 0x21               ; ax=new (duplicated) file handle
252
mov [OLD_STDOUT], ax   ; save the old handle in memory
267
mov [OLD_STDOUT], ax   ; save the old handle in memory
253
 
268
 
254
; redirect stdout to my file
269
; redirect stdout to my file
255
pop bx                 ; dst handle
270
pop bx                 ; dst handle
256
mov cx, 1              ; src handle (1=stdout)
271
mov cx, 1              ; src handle (1=stdout)
257
mov ah, 0x46           ; "redirect a handle"
272
mov ah, 0x46           ; "redirect a handle"
258
int 0x21
273
int 0x21
259
 
274
 
260
; close the original file handle, I no longer need it
275
; close the original file handle, I no longer need it
261
mov ah, 0x3e           ; close a file handle (handle in BX)
276
mov ah, 0x3e           ; close a file handle (handle in BX)
262
int 0x21
277
int 0x21
263
NO_STDOUT_REDIR:
278
NO_STDOUT_REDIR:
264
 
279
 
265
; *** redirect stdin if REDIR_INFIL points to something ***
280
; *** redirect stdin if REDIR_INFIL points to something ***
266
cmp [REDIR_INFIL], byte 0
281
cmp [REDIR_INFIL], byte 0
267
je NO_STDIN_REDIR
282
je NO_STDIN_REDIR
268
mov dx, REDIR_INFIL    ; dx:dx = file
283
mov dx, REDIR_INFIL    ; dx:dx = file
269
mov ax, 0x3d00         ; open file for read
284
mov ax, 0x3d00         ; open file for read
270
int 0x21               ; ax=handle on success (CF clear)
285
int 0x21               ; ax=handle on success (CF clear)
271
mov [REDIR_INFIL], byte 0
286
mov [REDIR_INFIL], byte 0
272
jc NO_STDIN_REDIR      ; TODO: abort with an error message instead
287
jc NO_STDIN_REDIR      ; TODO: abort with an error message instead
273
 
288
 
274
; duplicate current stdin so I can revert it later
289
; duplicate current stdin so I can revert it later
275
push ax                ; save my file handle in stack
290
push ax                ; save my file handle in stack
276
mov ah, 0x45           ; duplicate file handle BX
291
mov ah, 0x45           ; duplicate file handle BX
277
xor bx, bx             ; 0=stdin
292
xor bx, bx             ; 0=stdin
278
int 0x21               ; ax=new (duplicated) file handle
293
int 0x21               ; ax=new (duplicated) file handle
279
mov [OLD_STDIN], ax    ; save the old handle in memory
294
mov [OLD_STDIN], ax    ; save the old handle in memory
280
 
295
 
281
; redirect stdout to my file
296
; redirect stdout to my file
282
pop bx                 ; dst handle
297
pop bx                 ; dst handle
283
xor cx, cx             ; src handle (0=stdin)
298
xor cx, cx             ; src handle (0=stdin)
284
mov ah, 0x46           ; "redirect a handle"
299
mov ah, 0x46           ; "redirect a handle"
285
int 0x21
300
int 0x21
286
 
301
 
287
; close the original file handle, I no longer need it
302
; close the original file handle, I no longer need it
288
mov ah, 0x3e           ; close a file handle (handle in BX)
303
mov ah, 0x3e           ; close a file handle (handle in BX)
289
int 0x21
304
int 0x21
290
NO_STDIN_REDIR:
305
NO_STDIN_REDIR:
291
ret
306
ret
292
; ----------------------------------------------------------------------------
307
; ----------------------------------------------------------------------------
293
 
308