home *** CD-ROM | disk | FTP | other *** search
File List | 1992-03-01 | 36.0 KB | 501 lines |
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 1
- QBREDIR.ASM
-
- 1
- 2 ; Quick Basic Redirected File Display
- 3 ;
- 4 ;
- 5 ; ---- A modified version of the original PD program REDVIEW.ASM ----
- 6 ; by Alexandr Novy and Peter Horak of Prague, Czechoslovakia
- 7 ;
- 8 ;
- 9 ; ---- This Public Domain Adaptation for QuickBasic Written By:
- 10 ;
- 11 ; Peter R. Barnes
- 12 ; Brentwood, TN
- 13 ; March, 1992
- 14 ;
- 15 ;
- 16 ; ---- To assemble the object module with TASM, use the command:
- 17 ;
- 18 ; TASM QBREDIR ;I don't have MASM,
- 19 ; ;but I'm sure the
- 20 ; ;syntax is the same
- 21 ;
- 22 ;
- 23 ; ---- To use in a QuickBasic program, compile your program
- 24 ; and link the module to your code, for example:
- 25 ;
- 26 ; BC /options YOURPROG; ;any options you want
- 27 ;
- 28 ; LINK /linkoptions YOURPROG + QBREDIR;
- 29 ;
- 30 ;
- 31 ;
- 32 ; ---- To call the routines in your program, use the following:
- 33 ;
- 34 ; CALL QFRedSet (FlagIntegerVariable%)
- 35 ;
- 36 ; CALL QFRedOff
- 37 ;
- 38 ;
- 39 ; --- IMPORTANT: READ THE QBREDIR.DOC FILE TO SEE HOW TO
- 40 ; PROPERLY USE THESE ROUTINES!! THE ROUTINES
- 41 ; INSTALL A CUSTOM INTERRUPT HANDLER WHICH
- 42 ; MUST BE TREATED WITH CARE!!
- 43 ;
- 44 ; ---- NOTE: CANNOT BE USED IN THE QB ENVIRONMENT (SEE
- 45 ; QBREDIR.DOC)
- 46 ;
- 47 ;
- 48 ; ---- I have added some comments to help explain the code as
- 49 ; it applies to QuickBasic, just for general (and my own)
- 50 ; reference. Some of the stuff is pretty elementary, so if
- 51 ; you are an assembly language wiz, just skip it; it's there
- 52 ; for the rest of us.
- 53 ;
- 54 ;
- 55 ;********************************************************************
- 56 ;********************************************************************
- 57 ;
- 58 ; Make our procedure names available to external programs
- 59 ;
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 2
- QBREDIR.ASM
-
- 60 public QFRedSet, QFRedOff
- 61 ;
- 62 ;
- 63 ;********************************************************************
- 64 ;
- 65 ; All Basic modules are assembled using the Medium
- 66 ; memory model, so we must add the proper assembler directives
- 67 ;
- 68 ;
- 69 0000 .MODEL Medium, Basic ;always, for QuickBasic
- 70 ;
- 71 0000 .CODE ;make the code segment our default
- 72 ;
- 73 ;
- 74 ;
- 75 ; We will let QBasic and TASM set the type of procedure, and we will
- 76 ; declare the module using the TASM simplified format
- 77 ;
- 78 ;
- 79 0000 QFRedSet proc uses si di es ds
- 80 = 0002 ARG flag:PTR = ARGLEN
- 81 ;
- 82 ;
- 83 ; That tells TASM to add code for a Basic procedure call, save
- 84 ; si,di,es,ds pointers, and that we are passing to the procedure a
- 85 ; two-byte pointer to the INTEGER (notice that I capitalized that --
- 86 ; three guesses why) variable /flag/, consisting of ARGLEN number of
- 87 ; bytes -- the QuickBasic 'call' automatically pushes that address onto
- 88 ; the stack and TASM calculates the correct offset to those stack
- 89 ; bytes when it assembles the code. All parameter addresses passed by
- 90 ; QuickBasic are near references (offset only, 1 word or 2 bytes)
- 91 ; unless you use the keyword SEG for the passed variable, which tells
- 92 ; QuickBasic to pass the address as a far reference (segment:offset,
- 93 ; 2 words or 4 bytes).
- 94 ; ---------
- 95 ; TASM knows from our 'Basic' directive that the variable we pass will
- 96 ; be a 2-byte address, and that Basic always makes far calls, leaving a full,
- 97 ; 2-word return address as the last 4 bytes on the stack; the variable
- 98 ; address, next on the stack, will be at [B(ase)P(ointer)+6] from the
- 99 ; present BP value (+ because the stack grows downward in memory).
- 100 ; That's also how TASM knows that we need a far return, not a near
- 101 ; return, and also how many bytes to discard, after it pops that QB
- 102 ; return address, to restore the stack after the procedure ends --
- 103 ; i.e. 2 bytes for the variable near address...
- 104 ;
- 105 ; push bp ;TASM automatically
- 106 ; mov bp,sp ;adds the code to save the B(ase)
- 107 ; ;P(ointer) to the stack in the stack
- 108 ; ;segment, and set up a new stack
- 109 ; ;that starts at the end of the old
- 110 ; ;one pointed to by the current
- 111 ; ;S(tack)P(ointer) -- i.e.
- 112 ; New bp = Old bp + sp
- 113 ; ;Actually, our new stack will begin
- 114 ; ;at New bp + sp, but this will keep
- 115 ; ;us away from the old stack for
- 116 ; ;all values of sp, in case sp gets
- 117 ; ;set to a lower value -- we will
- 118 ; ;never get lower than the new bp.
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 3
- QBREDIR.ASM
-
- 119 ; ;Of course, we have to be careful
- 120 ; ;to preserve that sp value in our
- 121 ; ;code by always 'popping' the same
- 122 ; ;number of bytes that we 'push'
- 123 ; ;onto the stack.
- 124 ;
- 125 ;
- 126 ; ;This is the code that is added
- 127 ; ;by TASM when it sees the 'Basic'
- 128 ; push si ;and the 'uses' directives, so we
- 129 ; push di ;don't need the explicit statements.
- 130 ; push es ;The address of /flag/ is placed on
- 131 ; push ds ;the stack first, and ARGLEN tells
- 132 ; ;TASM how many bytes to discard, when
- 133 ; ;we return from the call, to get
- 134 ; ;back to the original stack before
- 135 ; ;the call.
- 136 ;
- 137 ;
- 138 ;********************************************************************
- 139 ;
- 140 ;
- 141 ;
- 142 ; The first thing we do is grab and save the address of the QB variable
- 143 ; that was passed to us, on the stack, by QuickBasic when it made the
- 144 ; call; remember that TASM knows that [flag] means [BP+6]. In this
- 145 ; call, we passed the 2-byte offset address of the variable in the
- 146 ; ds data segment, so the 'word ptr [flag]' will have the 2-byte word
- 147 ; for the offset moved into ax; then the 2-byte word for the ds data
- 148 ; segment, whatever it is, will be stored. Both of those values are
- 149 ; copied into /flagadr/ by the following 3 mov instructions:
- 150 ;
- 151 ;
- 152 0000 55 8B EC 56 57 06 1E + mov ax,word ptr [flag] ;save the offset of passed
- 153 8B 46 06
- 154 000A 2E: A3 00EDr mov cs:flagadr,ax ;integer variable /flag/ in
- 155 ; ;memory location /flagadr/
- 156 ;
- 157 ;
- 158 ; That saves the offset that we passed, now we save the data segment
- 159 ; pointer that is in ds at the time of the call. Since QuickBasic
- 160 ; only has one data segment for integer variables, ds will already be
- 161 ; pointing to the segment containing our /flag/ variable.
- 162 ;
- 163 ; We could pass the segment pointer when we made the call from
- 164 ; QuickBasic by preceding the variable name with the SEG keyword; in
- 165 ; that situation, QuickBasic would push the full 4-byte address onto
- 166 ; the stack. We could also use the CALLS statement to call this
- 167 ; routine, instead of CALL, because that statement always passes the
- 168 ; full address of parameters. But neither of those actions are
- 169 ; required, because QuickBasic only has one data segment
- 170 ;
- 171 ; The following mov instruction saves our data segment for DOS to
- 172 ; use to find our variable when it executes the interrupt routine:
- 173 ;
- 174 ;
- 175 000E 2E: 8C 1E 00EFr mov cs:flagadr+2,ds ;save the segment pointer
- 176 ; ;of passed integer variable
- 177 ; ;/flag/ in second word
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 4
- QBREDIR.ASM
-
- 178 ; ;of /flagadr/ --
- 179 ; ;that's what DOS needs when
- 180 ; ;the interrupt is called
- 181 ;
- 182 ;
- 183 ; Next step is flagcheck to prevent multiple calls to this routine
- 184 ; if it has not been reset. Without this check, a second call would
- 185 ; save our new handler address as the original.
- 186 ;
- 187 ; Check to see if we have already installed the vector to the
- 188 ; interrupt handler routine. If we have, we don't want to do it
- 189 ; again, because we will lose the original vector
- 190 ;
- 191 0013 2E: A0 00ECr mov al,cs:[IntSetFlag] ;get flag to see if handler
- 192 0017 3C 00 cmp al,0 ;is already installed
- 193 0019 75 1F jne EndSet ;if set, we have installed
- 194 ; ;so go to exit
- 195 ;
- 196 001B B0 01 mov al, 1 ;else set the flag
- 197 001D 2E: A2 00ECr mov cs:[IntSetFlag],al
- 198 ;
- 199 ;
- 200 ; ;pick up original vector contents
- 201 ;
- 202 0021 B8 3521 mov ax,3521H ;for interrupt 21H (MS-DOS Services
- 203 0024 CD 21 int 21H ;Function Request handler)
- 204 ;
- 205 0026 2E: 89 1E 00F1r mov cs:Old21Offset,bx ;save the original vector
- 206 002B 2E: 8C 06 00F3r mov cs:Old21Segment,es ;in our data area
- 207 ;
- 208 ;
- 209 ; ;a call to DOS services INT21H for
- 210 ; ;the SET INTERRUPT function 25xxH
- 211 ; ;requires ds:dx to be set to the
- 212 ; ;interrupt address, so the next two
- 213 ; ;statements set the ds segment
- 214 ; ;equal to the cs segment, where
- 215 ; ;our new handler procedure is
- 216 ; ;located
- 217 ;
- 218 ;
- 219 0030 0E push cs ;push our code segment onto stack
- 220 0031 1F pop ds ;pop it back off to set ds
- 221 ; ;i.e. sets ds=cs
- 222 ;
- 223 0032 BA 0042r mov dx,offset QDualDisp ;load offset of our Int 21 handler
- 224 0035 B8 2521 mov ax,02521H ;and reset vector to point to
- 225 0038 CD 21 int 21H ;our procedure
- 226 ;
- 227 ;
- 228 ; pop ds ;restore registers and
- 229 ; pop es ;return to our QB program
- 230 ; pop di
- 231 ; pop si ;NOTE--TASM automatically adds
- 232 ; pop bp ;the code to restore registers
- 233 ;
- 234 003A 1F 07 5F 5E 5D CA + EndSet: ret
- 235 0002
- 236 ;
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 5
- QBREDIR.ASM
-
- 237 ; actually, it's 'ret [ARGLEN]' ;TASM automatically makes the
- 238 ; ;return discard the /flag/ address
- 239 ; ;bytes from the stack--the number
- 240 ; ;of bytes to discard was set by
- 241 ; ;the ARGLEN directive
- 242 ;
- 243
- 244 ;
- 245 ;
- 246 ; Following is our new interrupt handler which is called by
- 247 ; MS-DOS whenever an Interrupt 21 MS-DOS Services Request
- 248 ; is detected. The procedure first checks our enable flag
- 249 ; to determine if we want the dual output activated; the address of
- 250 ; this flag was passed by the QB program during the call to
- 251 ; the QFRedSet function. If the flag is not set, we just skip our
- 252 ; handler and proceed to the regular interrupt 21 routine.
- 253 ;
- 254 ; The handler then checks to see what type of service is being requested.
- 255 ; If it is a call to the DOS routines to output a character or a string
- 256 ; to STDOUT, the standard DOS output device, which is redirectible and
- 257 ; is always designated with file handle 1, then our procedure essentially
- 258 ; duplicates the routine by sending the output to STDERR, the standard
- 259 ; DOS error output device, which is always the display, file handle 2,
- 260 ; and whose output cannot be redirected. Then the handler exits to the
- 261 ; normal Interrupt 21 procedure to let it process the original call.
- 262 ;
- 263 ; Note that the routine is part of the QFRedSet procedure, but it is never
- 264 ; executed when that procedure is called by the QB program, because that
- 265 ; procedure ends at the 'ret' opcode at EndSet, above.
- 266 ;
- 267 QDualDisp:
- 268
- 269 0042 9C pushf ;save everything we can --
- 270 0043 50 push ax ;i.e. all registers and flags
- 271 0044 53 push bx
- 272 0045 51 push cx ;note that we save the flags
- 273 0046 52 push dx ;even though DOS did the same
- 274 0047 55 push bp ;thing when it called the
- 275 0048 56 push si ;interrupt routine
- 276 0049 57 push di
- 277
- 278
- 279 004A 53 push bx ;save entering register values
- 280 004B 1E push ds
- 281 004C 2E: 8B 1E 00EDr mov bx,cs:flagadr ;get address of flag value passed
- 282 0051 2E: 8E 1E 00EFr mov ds,cs:flagadr+2 ;from our QB program
- 283 0056 8A 07 mov al,ds:bx ;get flag value into al
- 284 0058 1F pop ds ;retrieve register values
- 285 0059 5B pop bx
- 286
- 287 005A 0A C0 or al, al ;see if value was zero
- 288 005C 74 57 je EndOur21 ;yes, don't do anything, go
- 289 ;to the regular interrupt 21 routine
- 290
- 291 005E 80 FC 02 cmp ah,02 ;was it a call to write character to STDOUT
- 292 0061 75 1C jne Int9Chk ;no, jump to next check
- 293
- 294 0063 1E push ds ;yes, save entering data segment value
- 295 0064 0E push cs ;set ds=cs
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 6
- QBREDIR.ASM
-
- 296 0065 1F pop ds
- 297 0066 2E: 88 16 00F5r mov cs:character,dl ;save the character to write in character
- 298 006B B4 40 mov ah,40h ;set up for int 21 function 40h
- 299 ;to write character to output device
- 300 006D BB 0002 mov bx,2 ;set file handle=2 for STDERR
- 301 0070 BA 00F5r mov dx,OFFSET character ;get the pointer to the character
- 302 0073 B9 0001 mov cx,1 ;one character to be written
- 303 0076 9C pushf ;save current flag status
- 304 0077 2E: FF 1E 00F1r call dword ptr cs:[Old21];use old interrupt routine to output
- 305 ;the character to the display,
- 306 ;it will pop the flags saved on stack
- 307 ;when it returns, because it ends with
- 308 ;iret, since it is an interrupt routine
- 309 007C 1F pop ds ;retrieve our character
- 310 007D EB 36 jmp short EndOur21 ;exit to old routine to write the character
- 311 ;to redirected output
- 312
- 313 Int9Chk:
- 314 007F 80 FC 09 cmp ah,09 ;was it a call to write string to output
- 315 0082 75 1C jne Int40Chk ;no, go to next check
- 316
- 317 Chrloop:
- 318 0084 8B F2 mov si,dx ;yes, set index
- 319 0086 80 3C 24 cmp byte ptr ds:[si],'$';are we at the end of the string
- 320 0089 74 13 je End2109 ;yes, go to finish
- 321 008B B4 40 mov ah,40h ;no, set up for int 21 function 40h
- 322 008D BB 0002 mov bx,2 ;output to STDERR
- 323 0090 B9 0001 mov cx,1
- 324 0093 52 push dx
- 325 0094 9C pushf
- 326 0095 2E: FF 1E 00F1r call dword ptr cs:[Old21];write the character as above
- 327 009A 5A pop dx
- 328 009B 42 inc dx ;bump index to next character
- 329 009C EB E6 jmp short Chrloop ;loop until done
- 330 End2109:
- 331 009E EB 15 jmp short EndOur21 ;exit to old routine to write the character
- 332 ;to redirected output
- 333
- 334 Int40Chk:
- 335 00A0 80 FC 40 cmp ah,40h ;was it a call to write to file or device
- 336 00A3 75 10 jne EndOur21 ;no, done, exit to regular int 21
- 337
- 338 00A5 83 FB 01 cmp bx,1 ;yes, then was it a call to STDOUT
- 339 00A8 75 0B jne EndOur21 ;no, done, exit
- 340
- 341 00AA 1E push ds ;yes, save character
- 342 00AB BB 0002 mov bx,2 ;set file handle for STDERR
- 343 00AE 9C pushf
- 344 00AF 2E: FF 1E 00F1r call dword ptr cs:[Old21] ;use old interrupt routine to output
- 345 00B4 1F pop ds ;retrieve character
- 346
- 347 EndOur21:
- 348
- 349 ;retrieve registers
- 350 00B5 5F pop di ;this resets registers
- 351 00B6 5E pop si ;and flags to exactly
- 352 00B7 5D pop bp ;where they were when we
- 353 00B8 5A pop dx ;entered our custom handler
- 354 00B9 59 pop cx
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 7
- QBREDIR.ASM
-
- 355 00BA 5B pop bx
- 356 00BB 58 pop ax
- 357 00BC 9D popf
- 358 ;
- 359 ;
- 360 00BD 2E: FF 2E 00F1r jmp dword ptr cs:[Old21] ;go to regular int 21 routine
- 361 ;
- 362 ;
- 363 ;
- 364 ;
- 365 00C2 QFRedSet endp
- 366 ;
- 367 ;
- 368 ;
- 369 ; The function QFRedOff is called by the QB program to
- 370 ; return the MS-DOS interrupt vector 21H to its' original state.
- 371 ; The function is used in the form:
- 372 ;
- 373 ; QFRedOff()
- 374 ;
- 375 ;
- 376 00C2 QFRedOff proc uses si di es ds ;Restore MS-DOS Services
- 377 ; ;interrupt vector 21H
- 378 ; push bp ;to its' original state
- 379 ; mov bp,sp
- 380 ; push ds ;same code added as above
- 381 ; etc.
- 382 ;
- 383 ;
- 384 ; First we must check our IntSetFlag to see if the
- 385 ; vectors to our handler were installed; if not, we
- 386 ; do not want to reset them here, either
- 387 ;
- 388 ;
- 389 00C2 55 8B EC 56 57 06 1E + mov al,cs:[IntSetFlag] ;Get our inhibit flag
- 390 2E: A0 00ECr
- 391 00CD 3C 01 cmp al,1 ;see if it is set
- 392 00CF 75 15 jne EndOff ;no, go to exit
- 393 ;else, reset vector
- 394 ;
- 395 ;
- 396 00D1 2E: 8B 16 00F1r mov dx,cs:Old21Offset ;Set interrupt 21H MS-DOS
- 397 00D6 2E: 8E 1E 00F3r mov ds,cs:Old21Segment ;Services Request
- 398 00DB B8 2521 mov ax,02521H ;back to its' original vector
- 399 00DE CD 21 int 21H
- 400 ;
- 401 ;
- 402 00E0 B0 00 mov al, 0 ;reset the flag to enable
- 403 00E2 2E: A2 00ECr mov cs:[IntSetFlag],al ;the QFRedSet routine
- 404 ;
- 405 ; pop ds ;restore registers and
- 406 ; pop bp ;return to QB program --
- 407 ; etc. ;same exit code added as above
- 408 ;
- 409 00E6 1F 07 5F 5E 5D CB EndOff: ret
- 410 ;
- 411 00EC QFRedOff endp
- 412 ;
- 413 ;
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 8
- QBREDIR.ASM
-
- 414 ;
- 415 00EC 00 IntSetFlag db 0 ;this flag inhibits QFRedSet operation
- 416 ; ;after it has redirected vector
- 417 ; ;until QFRedOff resets it, and does
- 418 ; ;the same for QFRedOff if QFRedSet
- 419 ; ;has not redirected vector
- 420 ;
- 421 00ED 0000 0000 flagadr dw 0,0 ;Long address of QB program's
- 422 ; ;display redirected output flag --
- 423 ; ;note that each '0' is actually 2 bytes
- 424 ;
- 425 Old21 Label Dword
- 426 00F1 ???? Old21Offset dw ? ;Original contents of MS-DOS
- 427 00F3 ???? Old21Segment dw ? ;Interrupt 21H vector
- 428
- 429 ;
- 430 ;
- 431 00F5 ?? character DB ? ;storage for output character
- 432 ;
- 433
- 434 end
- Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 9
- Symbol Table
-
-
- Symbol Name Type Value Cref defined at #
-
- ??DATE Text "03-01-92"
- ??FILENAME Text "QBREDIR "
- ??TIME Text "18:14:09"
- ??VERSION Number 0100
- @CODE Text QBREDIR_TEXT #69 #71
- @CODESIZE Text 1 #69
- @CPU Text 0101H
- @CURSEG Text QBREDIR_TEXT #71
- @DATA Text DGROUP #69
- @DATASIZE Text 0 #69
- @FILENAME Text QBREDIR
- @WORDSIZE Text 2 #71
- ARGLEN Number 0002 #80
- CHARACTER Byte QBREDIR_TEXT:00F5 297 301 #431
- CHRLOOP Near QBREDIR_TEXT:0084 #317 329
- END2109 Near QBREDIR_TEXT:009E 320 #330
- ENDOFF Near QBREDIR_TEXT:00E6 392 #409
- ENDOUR21 Near QBREDIR_TEXT:00B5 288 310 331 336 339 #347
- ENDSET Near QBREDIR_TEXT:003A 193 #234
- FLAG Number [DGROUP:BP+0006] #80 152
- FLAGADR Word QBREDIR_TEXT:00ED 154 175 281 282 #421
- INT40CHK Near QBREDIR_TEXT:00A0 315 #334
- INT9CHK Near QBREDIR_TEXT:007F 292 #313
- INTSETFLAG Byte QBREDIR_TEXT:00EC 191 197 389 403 #415
- OLD21 Dword QBREDIR_TEXT:00F1 304 326 344 360 #425
- OLD21OFFSET Word QBREDIR_TEXT:00F1 205 396 #426
- OLD21SEGMENT Word QBREDIR_TEXT:00F3 206 397 #427
- QDUALDISP Near QBREDIR_TEXT:0042 223 #267
- QFREDOFF Far QBREDIR_TEXT:00C2 60 #376
- QFREDSET Far QBREDIR_TEXT:0000 60 #79
-
- Groups & Segments Bit Size Align Combine Class Cref defined at #
-
- DGROUP Group #69 69
- _DATA 16 0000 Word Public DATA #69
- QBREDIR_TEXT 16 00F6 Word Public CODE #69 69 #71 71
-