-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCOMMAND.ASM
502 lines (440 loc) · 13.5 KB
/
COMMAND.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
;% .MODEL MEMMOD,C
include asmgloba.h
LOCALS
%MACS
.LALL
extrn pwait: far
.DATA
db 4096 dup(?)
cstack label byte
.CODE
dbase dw @Data
segoff struc
offs dw ?
segm dw ?
segoff ends
main_subr dw ?
their_8h dd ?
their_10h dd ?
their_13h dd ?
their_1Bh dd ?
their_21h dd ?
their_23h dd ?
their_24h dd ?
their_28h dd ?
dos_segment dw ? ;segment of internal DOS flags
indos_offset dw ? ;offset of INDOS flag
errflag_offset dw ? ;offset of critical error flag
program_status db 0 ;popup status
flag_10h db 0 ;status of interrupt 10h
flag_13h db 0 ;status of interrupt 13h
zflag db ? ;save and restore critical error.
dos_version db ? ;dos major version.
main_countdown db 20
ss_register dw ? ;SS register storage
sp_register dw ? ;SP register storage
my_psp dw ? ;our PSP.
their_psp dw ? ;PSP segment storage
tick_counter dw ?
indos_counter dw ?
errflag_counter dw ?
status_counter dw ?
bp_counter dw ?
main_counter dw ?
their_dta dd ?
;
;------------------------------------------------------------------------------
;Interrupt 8 handling routine.
;------------------------------------------------------------------------------
timer:
pushf ;call BIOS routine
call their_8h
inc tick_counter
cmp program_status,0 ;are we already running?
jne timer_status ;yes, then suspend ticking.
cmp flag_10h,0 ;video flag set?
jne timer_exit ;yes, then exit
cmp flag_13h,0 ;disk flag set?
jne timer_exit ;yes, then exit
push es ;save ES and DI
push di
mov es,dos_segment ;check INDOS flag
mov di,indos_offset
cmp byte ptr es:[di],0
jne timer_indos ;exit if it's set
mov di,errflag_offset ;check critical error flag
cmp byte ptr es:[di],0
jne timer_errflag ;exit if it's set
mov main_subr,offset cycle
call main ;call body of program
pop di
pop es
timer_exit:
iret
timer_indos:
inc indos_counter
pop di ;restore registers
pop es
iret
timer_errflag:
inc errflag_counter
pop di ;restore registers
pop es
iret
timer_status:
inc status_counter
iret
;
;------------------------------------------------------------------------------
;Interrupt 10h handling routine.
;------------------------------------------------------------------------------
video:
pushf ;push flags onto stack
inc flag_10h ;increment flag
call their_10h ;call BIOS routine
dec flag_10h ;decrement flag
iret
;
;------------------------------------------------------------------------------
;Interrupt 13h handling routine.
;------------------------------------------------------------------------------
my_13:
pushf ;push flags onto stack
inc flag_13h ;set 'busy' flag
call their_13h ;call BIOS routine
pushf ;save output flags
dec flag_13h ;clear flag
popf ;restore output flags
retf 2 ;exit without destroying flags
;
;------------------------------------------------------------------------------
;Interrupt 28h handling routine.
;------------------------------------------------------------------------------
my_28:
pushf ;call original routine
call their_28h
inc bp_counter
cmp program_status,0 ;are we already running?
jne bp_exit ;yes, don't enter it again.
cmp flag_10h,0 ;video flag set?
jne bp_exit ;yes, then exit
cmp flag_13h,0 ;disk flag set?
jne bp_exit ;yes, then exit
push es ;save ES and DI
push di
mov es,dos_segment ;check critical error flag
mov di,errflag_offset
cmp byte ptr es:[di],0
pop di ;clean up the stack
pop es
jne bp_errflag
mov main_subr,offset cycle
call main ;call main routine
bp_exit:
iret ;done - exit
bp_errflag:
inc errflag_counter
iret ;done - exit
;
;------------------------------------------------------------------------------
;Interrupt 21h handling routine.
;------------------------------------------------------------------------------
my_21:
pushf ;save the flags
or ah,ah ;Doing function zero?
je jump_to_dos ;If yes, take the jump
cmp ah,4bh ;Doing EXEC function?
je jump_to_dos ;If yes, take the jump
popf
pushf
call cs:their_21h ;Do the DOS function
pushf ;Save the result flags
cmp cs:program_status,0 ;are we already running?
jne no_recursion ;yes, don't recurse.
dec cs:main_countdown
jne no_recursion
mov cs:main_countdown,20
mov main_subr,offset cycle
call main ;Safe to access disk now
no_recursion:
popf ;Recover DOS result flags
sti ;Must return with interrupts on
retf 2 ;Return with DOS result flags
jump_to_dos:
popf
jmp cs:their_21h
;
;------------------------------------------------------------------------------
;Interrupt 24h handling routine (DOS 3.X only).
;------------------------------------------------------------------------------
my_24:
mov al,3 ;fail the call in progress
ioexit:
iret ;give control back to DOS
cycle:
xor ax,ax ;push a null pointer.
push ax
push ax
call pwait
add sp,4
ret
;
;------------------------------------------------------------------------------
;MAIN is the routine called periodically.
;------------------------------------------------------------------------------
main:
inc main_counter
mov program_status,1 ;set program active flag
cli ;make sure interrupts are off
mov ss_register,ss ;save stack registers
mov sp_register,sp
mov ss,cs:dbase ; establish interrupt data segment
mov sp,offset cstack
sti ;enable interrupts
PUSHALL
; push ax
; push bx
; push cx
; push dx
; push si
; push di
push ds
push es
; push bp
;
;Set DS and ES segment registers.
;
push cs ;set DS to code segment
pop ds
assume ds:seg dos_version
;
;Save the current active PSP address and activate this PSP.
;
mov zflag,0 ;clear flag
cmp dos_version,2 ;DOS version 2.X?
jne main5
mov es,dos_segment ;point ES:DI to INDOS
mov di,indos_offset
cmp byte ptr es:[di],0 ;INDOS clear?
je main5 ;yes, then branch
mov di,errflag_offset ;point ES:DI to error flag
cmp byte ptr es:[di],0 ;critical error flag clear?
jne main5 ;no, then branch
mov byte ptr es:[di],1 ;set critical error flag manually
mov zflag,1 ;set change flag
main5:
mov ah,51h ;get current PSP segment
int 21h
mov their_psp,bx ;save it
mov ah,50h ;make this the active PSP
mov bx,my_psp
int 21h
cmp zflag,0 ;ZFLAG clear?
je main6 ;yes, then branch
mov di,errflag_offset ;point ES:DI to error flag
mov byte ptr es:[di],0 ;restore error flag value
main6:
;
;Reset the interrupt 1Bh, 23h, and 24h vectors.
;
call ioset ;reset interrupt vectors
;
;Save the current dta and subdirectory
;
mov ah,2fh ;get disk transfer address
int 21h
mov their_dta.segm,es
mov their_dta.offs,bx
;
;Call the commutator loop of net until nothing gets queued up.
;
mov ds,cs:dbase ; establish interrupt data segment
assume ds:nothing
call main_subr
;
;Restore the current dta and subdirectory
;
lds dx,their_dta
mov ah,1ah
int 21h
;
;Restore interrupt vectors and former active PSP.
;
mov ah,50h ;restore active PSP label
mov bx,their_psp
int 21h
call ioreset ;restore interrupt vectors
;
;Restore registers and stack before exit.
;
; pop bp ;restore registers and exit
pop es
pop ds
POPALL
; pop di
; pop si
; pop dx
; pop cx
; pop bx
; pop ax
cli ;interrupts off
mov ss,ss_register ;switch to original stack
mov sp,sp_register
sti ;interrupts on
mov program_status,0 ;clear status flag
ret
;
intset:
;enter with al = interrupt number, cs:dx = offset of new interrupt,
; cs:di -> place to store old interrupt.
push es ;get the old interrupt into es:bx
push ds ;now set the new interrupt to ds:dx.
mov bx,cs
mov ds,bx
mov ah,35h
int 21h
mov [di].segm,es ;and store it into ds:di.
mov [di].offs,bx
mov ah,25h
int 21h
pop ds
pop es
ret
intreset:
;enter with al = interrupt number, di -> old interrupt.
push ds
mov ah,25h
lds dx,cs:[di]
int 21h
pop ds
ret
;------------------------------------------------------------------------------
;IOSET vectors interrupts 1Bh, 23h and 24h to internal handlers. IORESET
;restores the original vector values.
;------------------------------------------------------------------------------
ioset:
mov al,1bh
mov di,offset their_1Bh
mov dx,offset ioexit
call intset
mov al,23h
mov di,offset their_23h
mov dx,offset ioexit
call intset
mov al,24h
mov di,offset their_24h
mov dx,offset my_24
call intset
ret
;
ioreset:
mov al,24h
mov di,offset their_24h
call intreset
mov al,23h
mov di,offset their_23h
call intreset
mov al,1Bh
mov di,offset their_1Bh
call intreset
ret
public start_back
start_back proc
;
;Remember our psp.
;
mov ah,51h ;get current PSP segment
int 21h
mov my_psp,bx ;save it
;
;Determine which version of DOS is running.
;
init3:
mov ah,30h ;DOS function 30h
int 21h
mov dos_version,al ;major version number
;
;Get and save the address of the INDOS flag.
;
mov ah,34h ;function 34h
int 21h ;get address
mov dos_segment,es ;save segment
mov indos_offset,bx ;save offset
;
;Get and save the address of the critical error flag.
;
mov ax,3E80h ;CMP opcode
mov cx,2000h ;max search length
mov di,bx ;start at INDOS address
init4:
repne scasw ;do the search
jcxz init5 ;branch if search failed
cmp byte ptr es:[di+5],0BCh ;verify this is it
je found ;branch if it is
jmp init4 ;resume loop if it's not
init5:
mov cx,2000h ;search again
inc bx ;search odd addresses this time
mov di,bx
init6:
repne scasw ;look for the opcode
jcxz notfound ;not found if loop expires
cmp byte ptr es:[di+5],0BCh ;verify this is it
je found
jmp init6
notfound:
xor ax,ax
ret
found:
mov ax,es:[di] ;get flag offset address
mov errflag_offset,ax ;save it
;
;Save and replace all required interrupt vectors.
;
mov al,08h
mov dx,offset timer
mov di,offset their_8h
call intset
mov al,10h
mov dx,offset video
mov di,offset their_10h
call intset
mov al,13h
mov dx,offset my_13
mov di,offset their_13h
call intset
mov al,28h
mov dx,offset my_28
mov di,offset their_28h
call intset
mov al,21h
mov dx,offset my_21
mov di,offset their_21h
call intset
mov ax,1
ret
start_back endp
public stop_back
stop_back proc
mov cs:program_status,1
mov al,08h
mov di,offset their_8h
call intreset
mov al,10h
mov di,offset their_10h
call intreset
mov al,13h
mov di,offset their_13h
call intreset
mov al,28h
mov di,offset their_28h
call intreset
mov al,21h
mov di,offset their_21h
call intreset
mov cs:program_status,0
mov dx,ds
mov ax,offset tick_counter
ret
stop_back endp
end