home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / masm / masmsmpl.txt < prev    next >
Encoding:
Text File  |  2013-11-08  |  361.4 KB  |  9,665 lines

  1.  Microsoft MASM: Sample Code from v6.0
  2.  
  3.  
  4.  ALARM.ASM
  5.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\TSR\ALARM.ASM
  6.  
  7.  ;* ALARM.ASM - A simple memory-resident program that beeps the speaker
  8.  ;* at a prearranged time.  Can be loaded more than once for multiple
  9.  ;* alarm settings.  During installation, ALARM establishes a handler
  10.  ;* for the timer interrupt (interrupt 08).  It then terminates through
  11.  ;* the Terminate-and-Stay-Resident function (function 31h).  After the
  12.  ;* alarm sounds, the resident portion of the program retires by setting
  13.  ;* a flag that prevents further processing in the handler.
  14.  ;*
  15.  ;* NOTE: You must assemble this program as a .COM file, either as a PWB
  16.  ;*       build option or with the ML /AT switch.
  17.  
  18.          .MODEL tiny, pascal, os_dos
  19.          .STACK
  20.  
  21.          .CODE
  22.  
  23.          ORG     5Dh                     ; Location of time argument in PSP,
  24.  CountDown       LABEL   WORD            ;   converted to number of 5-second
  25.                                          ;   intervals to elapse
  26.          .STARTUP
  27.          jmp     Install                 ; Jump over data and resident code
  28.  
  29.  ; Data must be in code segment so it won't be thrown away with Install code.
  30.  
  31.  OldTimer        DWORD   ?               ; Address of original timer routine
  32.  tick_91         BYTE    91              ; Counts 91 clock ticks (5 seconds)
  33.  TimerActiveFlag BYTE    0               ; Active flag for timer handler
  34.  
  35.  ;* NewTimer - Handler routine for timer interrupt (interrupt 08).
  36.  ;* Decrements CountDown every 5 seconds.  No other action is taken
  37.  ;* until CountDown reaches 0, at which time the speaker sounds.
  38.  
  39.  NewTimer PROC   FAR
  40.  
  41.          .IF     cs:TimerActiveFlag != 0 ; If timer busy or retired:
  42.          jmp     cs:OldTimer             ; Jump to original timer routine
  43.          .ENDIF
  44.          inc     cs:TimerActiveFlag      ; Set active flag
  45.          pushf                           ; Simulate interrupt by pushing flags
  46.          call    cs:OldTimer             ;   then far-calling original routine
  47.          sti                             ; Enable interrupts
  48.          push    ds                      ; Preserve DS register
  49.          push    cs                      ; Point DS to current segment for
  50.          pop     ds                      ;   further memory access
  51.          dec     tick_91                 ; Count down for 91 ticks
  52.          .IF     zero?                   ; If 91 ticks have elapsed:
  53.          mov     tick_91, 91             ; Reset secondary counter and
  54.          dec     CountDown               ;   subtract one 5-second interval
  55.          .IF     zero?                   ; If CountDown drained:
  56.          call    Sound                   ; Sound speaker
  57.          inc     TimerActiveFlag         ; Alarm has sounded, set flag
  58.          .ENDIF
  59.          .ENDIF
  60.  
  61.          dec     TimerActiveFlag         ; Decrement active flag
  62.          pop     ds                      ; Recover DS
  63.          iret                            ; Return from interrupt handler
  64.  
  65.  NewTimer ENDP
  66.  
  67.  
  68.  ;* Sound - Sounds speaker with the following tone and duration:
  69.  
  70.  BEEP_TONE       EQU     440             ; Beep tone in hertz
  71.  BEEP_DURATION   EQU     6               ; Number of clocks during beep,
  72.                                          ;   where 18 clocks = approx 1 second
  73.  
  74.  Sound   PROC    USES ax bx cx dx es     ; Save registers used in this routine
  75.          mov     al, 0B6h                ; Initialize channel 2 of
  76.          out     43h, al                 ;   timer chip
  77.          mov     dx, 12h                 ; Divide 1,193,180 hertz
  78.          mov     ax, 34DCh               ;   (clock frequency) by
  79.          mov     bx, BEEP_TONE           ;   desired frequency
  80.          div     bx                      ; Result is timer clock count
  81.          out     42h, al                 ; Low byte of count to timer
  82.          mov     al, ah
  83.          out     42h, al                 ; High byte of count to timer
  84.          in      al, 61h                 ; Read value from port 61h
  85.          or      al, 3                   ; Set first two bits
  86.          out     61h, al                 ; Turn speaker on
  87.  
  88.  ; Pause for specified number of clock ticks
  89.  
  90.          mov     dx, BEEP_DURATION       ; Beep duration in clock ticks
  91.          sub     cx, cx                  ; CX:DX = tick count for pause
  92.          mov     es, cx                  ; Point ES to low memory data
  93.          add     dx, es:[46Ch]           ; Add current tick count to CX:DX
  94.          adc     cx, es:[46Eh]           ; Result is target count in CX:DX
  95.          .REPEAT
  96.          mov     bx, es:[46Ch]           ; Now repeatedly poll clock
  97.          mov     ax, es:[46Eh]           ;   count until the target
  98.          sub     bx, dx                  ;   time is reached
  99.          sbb     ax, cx
  100.          .UNTIL  !carry?
  101.  
  102.          in      al, 61h                 ; When time elapses, get port value
  103.          xor     al, 3                   ; Kill bits 0-1 to turn
  104.          out     61h, al                 ;   speaker off
  105.          ret
  106.  
  107.  Sound   ENDP
  108.  
  109.  
  110.  
  111.  ;* Install - Converts ASCII argument to valid binary number, replaces
  112.  ;* NewTimer as the interrupt handler for the timer, then makes program
  113.  ;* memory-resident by exiting through function 31h.
  114.  ;*
  115.  ;* This procedure marks the end of the TSR's resident section and the
  116.  ;* beginning of the installation section.  When ALARM terminates through
  117.  ;* function 31h, the above code and data remain resident in memory.  The
  118.  ;* memory occupied by the following code is returned to DOS.
  119.  
  120.  
  121.  Install PROC
  122.  
  123.  ; Time argument is in hhmm military format.  Convert ASCII digits to
  124.  ; number of minutes since midnight, then convert current time to number
  125.  ; of minutes since midnight.  Difference is number of minutes to elapse
  126.  ; until alarm sounds.  Convert to seconds-to-elapse, divide by 5 seconds,
  127.  ; and store result in word CountDown.
  128.  
  129.  DEFAULT_TIME    EQU     3600            ; Default alarm setting = 1 hour
  130.                                          ;   (in seconds) from present time
  131.          mov     ax, DEFAULT_TIME
  132.          cwd                             ; DX:AX = default time in seconds
  133.          .IF     BYTE PTR CountDown != ' ';If not blank argument:
  134.          xor     CountDown[0], '00'      ; Convert 4 bytes of ASCII
  135.          xor     CountDown[2], '00'      ;   argument to binary
  136.  
  137.          mov     al, 10                  ; Multiply 1st hour digit by 10
  138.          mul     BYTE PTR CountDown[0]   ;   and add to 2nd hour digit
  139.          add     al, BYTE PTR CountDown[1]
  140.          mov     bh, al                  ; BH = hour for alarm to go off
  141.          mov     al, 10                  ; Repeat procedure for minutes
  142.          mul     BYTE PTR CountDown[2]   ; Multiply 1st minute digit by 10
  143.          add     al, BYTE PTR CountDown[3] ;   and add to 2nd minute digit
  144.          mov     bl, al                  ; BL = minute for alarm to go off
  145.          mov     ah, 2Ch                 ; Request function 2Ch
  146.          int     21h                     ; Get Time (CX = current hour/min)
  147.          mov     dl, dh
  148.          sub     dh, dh
  149.          push    dx                      ; Save DX = current seconds
  150.  
  151.          mov     al, 60                  ; Multiply current hour by 60
  152.          mul     ch                      ;   to convert to minutes
  153.          sub     ch, ch
  154.          add     cx, ax                  ; Add current minutes to result
  155.                                          ; CX = minutes since midnight
  156.          mov     al, 60                  ; Multiply alarm hour by 60
  157.          mul     bh                      ;   to convert to minutes
  158.          sub     bh, bh
  159.          add     ax, bx                  ; AX = number of minutes since
  160.                                          ;   midnight for alarm setting
  161.          sub     ax, cx                  ; AX = time in minutes to elapse
  162.                                          ;   before alarm sounds
  163.          .IF     carry?                  ; If alarm time is tomorrow:
  164.          add     ax, 24 * 60             ; Add minutes in a day
  165.          .ENDIF
  166.  
  167.          mov     bx, 60
  168.          mul     bx                      ; DX:AX = minutes-to-elapse-times-60
  169.          pop     bx                      ; Recover current seconds
  170.          sub     ax, bx                  ; DX:AX = seconds to elapse before
  171.          sbb     dx, 0                   ;   alarm activates
  172.          .IF     carry?                  ; If negative:
  173.          mov     ax, 5                   ; Assume 5 seconds
  174.          cwd
  175.          .ENDIF
  176.          .ENDIF
  177.  
  178.          mov     bx, 5                   ; Divide result by 5 seconds
  179.          div     bx                      ; AX = number of 5-second intervals
  180.          mov     CountDown, ax           ;   to elapse before alarm sounds
  181.  
  182.          mov     ax, 3508h               ; Request function 35h
  183.          int     21h                     ; Get Vector for timer (interrupt 08)
  184.          mov     WORD PTR OldTimer[0], bx; Store address of original
  185.          mov     WORD PTR OldTimer[2], es;   timer interrupt
  186.          mov     ax, 2508h               ; Request function 25h
  187.          mov     dx, OFFSET NewTimer     ; DS:DX points to new timer handler
  188.          int     21h                     ; Set Vector with address of NewTimer
  189.  
  190.          mov     dx, OFFSET Install      ; DX = bytes in resident section
  191.          mov     cl, 4
  192.          shr     dx, cl                  ; Convert to number of paragraphs
  193.          inc     dx                      ;   plus one
  194.          mov     ax, 3100h               ; Request function 31h, error code=0
  195.          int     21h                     ; Terminate-and-Stay-Resident
  196.  
  197.  Install ENDP
  198.  
  199.          END
  200.  
  201.  
  202.  BASIC.ASM
  203.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\MIXED\BASIC.ASM
  204.  
  205.  ; Called by BASMAIN.BAS
  206.  ; Assemble with ML /c BASIC.ASM
  207.  
  208.          .MODEL  medium
  209.  
  210.  Power2  PROTO   PASCAL, Factor:PTR WORD, Power:PTR WORD
  211.          .CODE
  212.  Power2  PROC    PASCAL, Factor:PTR WORD, Power:PTR WORD
  213.  
  214.          mov     bx, WORD PTR Factor   ; Load Factor into
  215.          mov     ax, [bx]              ;  AX
  216.          mov     bx, WORD PTR Power    ; Load Power into
  217.          mov     cx, [bx]              ;   CX
  218.          shl     ax, cl                ; AX = AX * (2 to power of CX)
  219.  
  220.          ret
  221.  Power2  ENDP
  222.  
  223.          END
  224.  
  225.  
  226.  C.ASM
  227.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\MIXED\C.ASM
  228.  
  229.  ;  Called from main program in CMAIN.C
  230.  ;  Assemble with ML /c C.ASM
  231.  
  232.          .MODEL  small, c
  233.  
  234.  Power2  PROTO C factor:SWORD, power:SWORD
  235.          .CODE
  236.  
  237.  Power2  PROC  C factor:SWORD, power:SWORD
  238.          mov     ax, factor    ; Load Arg1 into AX
  239.          mov     cx, power     ; Load Arg2 into CX
  240.          shl     ax, cl        ; AX = AX * (2 to power of CX)
  241.                                ; Leave return value in AX
  242.          ret
  243.  Power2  ENDP
  244.          END
  245.  
  246.  
  247.  COMMON.ASM
  248.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\DEMOS\COMMON.ASM
  249.  
  250.          .MODEL  small, pascal, os_dos
  251.          INCLUDE demo.inc
  252.  
  253.          .DATA
  254.  vconfig VIDCONFIG <>           ; Global video configuration structure
  255.  
  256.          .CODE
  257.  
  258.  ;* GetVidConfig - Determines current video configuration and initializes
  259.  ;* the vconfig structure.
  260.  ;*
  261.  ;* Shows:   BIOS Interrupt - 10h, Function 0 (Set Video Mode)
  262.  ;*                           10h, Function 0Fh (Get Current Video Mode)
  263.  ;*                           10h, Function 1Ah (Video Display Combination)
  264.  ;*
  265.  ;* Uses:    vconfig - Video configuration structure, declared in the
  266.  ;*          DEMO.INC include file.
  267.  ;*
  268.  ;* Params:  None
  269.  ;*
  270.  ;* Return:  None
  271.  
  272.  GetVidConfig PROC
  273.  
  274.          mov     ax, 1A00h               ; Get video info for VGA
  275.          int     10h
  276.  chkVGA:
  277.          cmp     al, 1Ah                 ; Is VGA or MCGA present?
  278.          jne     chkEGA                  ; No?  Then check for EGA
  279.  
  280.          cmp     bl, 2                   ; If VGA exists as secondary adapter,
  281.          je      isCGA                   ;   check for CGA and mono as primary
  282.          jb      isMONO
  283.          cmp     bl, 5                   ; If EGA is primary, do normal
  284.          jbe     chkEGA                  ;   EGA checking
  285.  chkMCGA:
  286.          mov     vconfig.adapter, MCGA   ; Yes?  Assume MCGA
  287.          mov     vconfig.display, COLOR
  288.          cmp     bl, 8                   ; Correct assumption?
  289.          ja      gotmode                 ; Yes?  Continue
  290.  isVGA:
  291.          mov     vconfig.adapter, VGA    ; Assume it's VGA color
  292.          je      gotmode                 ; Yes?  Continue
  293.          mov     vconfig.display, MONO   ; No?  Must be VGA mono
  294.          jmp     gotmode                 ; Finished with VGA, so jump
  295.  chkEGA:
  296.          mov     ah, 12h                 ; Call EGA status function
  297.          mov     bl, 10h
  298.          sub     cx, cx                  ; Clear status bits
  299.          int     10h
  300.          jcxz    chkCGA                  ; If CX is unchanged, not EGA
  301.  isEGA:
  302.          mov     vconfig.adapter, EGA    ; Set structure fields for EGA
  303.          mov     vconfig.display, MONO   ; Assume EGA mono
  304.          or      bh, bh                  ; Correct assumption?
  305.          jnz     gotmode                 ; Yes?  Continue
  306.          mov     vconfig.display, COLOR  ; No?  Must be EGA color
  307.          jmp     gotmode                 ; Finished with EGA, so jump
  308.  chkCGA:
  309.          int     11h                     ; Get equipment list
  310.          and     al, 30h                 ; If bits 4-5 set, monochrome
  311.          cmp     al, 30h                 ; Monochrome text mode?
  312.          je      isMONO                  ; Yes?  Continue
  313.  isCGA:
  314.          mov     vconfig.adapter, CGA    ; No?  Must be CGA
  315.          mov     vconfig.display, COLOR
  316.          jmp     gotmode
  317.  isMONO:
  318.          mov     vconfig.adapter, MDA    ; Set MONO
  319.          mov     vconfig.display, MONO
  320.  gotmode:
  321.          mov     ah, 0Fh
  322.          int     10h                     ; Get current mode
  323.          mov     vconfig.mode, al        ; Record mode
  324.          mov     vconfig.dpage, bh       ;   and current page
  325.          mov     al, vconfig.display     ; Multiply display value
  326.          cbw                             ;   (which is either 0 or 1)
  327.          mov     bx, 800h                ;   by 800h, then add to B000h
  328.          mul     bx                      ;   for segment address of
  329.          add     ax, 0B000h              ;   video buffer
  330.          add     ah, vconfig.dpage       ; Adding display page gives
  331.          mov     vconfig.sgmnt, ax       ;   address of current page
  332.  
  333.          sub     ax, ax
  334.          mov     es, ax
  335.          mov     al, es:[44Ah]           ; Get number of display cols
  336.          mov     vconfig.cols, al        ; Store in structure
  337.          mov     vconfig.rows, 24        ; Assume bottom row # = 24
  338.          cmp     vconfig.adapter, EGA    ; EGA or VGA?
  339.          jl      exit                    ; No?  Exit
  340.          mov     ax, 1130h               ; Yes?  Request character info
  341.          sub     bh, bh                  ; Set BH to valid value
  342.          push    bp                      ; BP will change, so save it
  343.          int     10h                     ; Get number of rows/screen
  344.          mov     vconfig.rows, dl        ; Keep in structure
  345.          pop     bp                      ; Restore BP
  346.  exit:
  347.          ret
  348.  
  349.  GetVidConfig ENDP
  350.  
  351.  
  352.  ;* GetCurPos - Gets current cursor position.
  353.  ;*
  354.  ;* Uses:    vconfig - Video configuration structure (initialized
  355.  ;*          by calling the GetVidConfig procedure)
  356.  ;*
  357.  ;* Params:  None
  358.  ;*
  359.  ;* Return:  Short integer with high byte = row, low byte = column
  360.  
  361.  GetCurPos PROC USES bx dx
  362.  
  363.          mov     ah, 3                   ; Function 3
  364.          mov     bh, vconfig.dpage
  365.          int     10h                     ; Get cursor position
  366.          mov     ax, dx
  367.          ret
  368.  
  369.  GetCurPos ENDP
  370.  
  371.  
  372.  ;* SetCurPos - Sets cursor position.
  373.  ;*
  374.  ;* Shows:   BIOS Interrupt - 10h, Function 2 (Set Cursor Position)
  375.  ;*
  376.  ;* Uses:    vconfig - Video configuration structure (initialized
  377.  ;*          by calling the GetVidConfig procedure)
  378.  ;*
  379.  ;* Params:  Row - Target row
  380.  ;*          Col - Target column
  381.  ;*
  382.  ;* Return:  None
  383.  
  384.  SetCurPos PROC USES bx dx,
  385.          Row:WORD,
  386.          Col:WORD
  387.  
  388.          mov     dh, BYTE PTR Row        ; DH = row
  389.          mov     dl, BYTE ptr Col        ; DL = column
  390.          mov     ah, 2                   ; Function 2
  391.          mov     bh, vconfig.dpage       ; Current page
  392.          int     10h                     ; Set cursor position
  393.          ret
  394.  
  395.  SetCurPos ENDP
  396.  
  397.  
  398.  ;* StrWrite - Writes ASCIIZ string to video memory at specified row/column.
  399.  ;*
  400.  ;* Shows:   Instructions - lodsb     stosb
  401.  ;*
  402.  ;* Uses:    vconfig - Video configuration structure (initialized
  403.  ;*          by calling the GetVidConfig procedure)
  404.  ;*
  405.  ;* Params:  Row - Row coordinate
  406.  ;*          Col - Column coordinate
  407.  ;*          Sptr - Pointer to string
  408.  ;*
  409.  ;* Return:  None
  410.  
  411.  StrWrite PROC USES ds si di,
  412.          Row:WORD,
  413.          Col:WORD,
  414.          Sptr:PTR BYTE
  415.  
  416.          GetVidOffset Row, Col           ; Get video offset for these coords
  417.          mov     di, ax                  ; Copy to DI
  418.          LoadPtr ds, si, Sptr            ; DS:SI points to string
  419.          mov     es, vconfig.sgmnt       ; ES:DI points to video RAM
  420.          .WHILE  1                       ; Loop forever (or until break)
  421.          lodsb                           ; Get 1 character from string
  422.          .BREAK .IF al == 0              ; Quit if null terminator
  423.  
  424.  ; For CGA systems, StrWrite waits for the video to begin a horizontal
  425.  ; retrace before writing a character to memory. This avoids the problem
  426.  ; of video snow inherent with some (though not all) color/graphics adapters.
  427.  ; It also demonstrates a somewhat different approach to the problem than the
  428.  ; one taken in the WinOpen and WinClose procedures.
  429.  
  430.          .IF vconfig.adapter != CGA      ; If not CGA, skip this step
  431.          push    ax                      ; Save character
  432.          mov     dx, 3DAh                ; Address of status register
  433.          cli                             ; Disallow interruptions
  434.          .REPEAT
  435.          in      al, dx                  ; Read current video status
  436.          .UNTIL  !(al & 1)               ; Until horizontal retrace done
  437.  
  438.          .REPEAT
  439.          in      al, dx                  ; No?  Read status again
  440.          .UNTIL  al & 1                  ; Until retrace starts
  441.          pop     ax                      ; Recover character
  442.          .ENDIF  ; CGA only
  443.  
  444.          stosb                           ; Write char to video buffer
  445.          sti                             ; Reenable interrupts in case CGA
  446.          inc     di                      ; Skip attribute byte
  447.          .ENDW
  448.          ret
  449.  
  450.  StrWrite ENDP
  451.  
  452.  
  453.  ;* StrInput - Gets input string from keyboard using BIOS. Signals idle
  454.  ;* state by calling interrupt 28h while polling for keypress, making
  455.  ;* the procedure useful in TSR programs. Terminates when Enter or Esc
  456.  ;* keys pressed.
  457.  ;*
  458.  ;* Shows:   DOS interrupt - Interrupt 28h (DOS Idle Interrupt)
  459.  ;*
  460.  ;* Params:  Row - Row coordinate
  461.  ;*          Col - Column coordinate
  462.  ;*          Max - Maximum allowable string length
  463.  ;*          Sptr - Pointer to string
  464.  ;*
  465.  ;* Return:  Short integer with terminating char
  466.  
  467.  StrInput PROC USES ds si,
  468.          Row:WORD,
  469.          Col:WORD,
  470.          Max:WORD,
  471.          Sptr:PBYTE
  472.  
  473.          LoadPtr ds, si, Sptr            ; DS:SI points to string
  474.          add     Max, si
  475.          dec     Max                     ; MAX now points to string limit
  476.  
  477.          .WHILE  1                       ; Get key until break or continue
  478.  loop1:
  479.          INVOKE  StrWrite,               ; Display input string
  480.                  Row,
  481.                  Col,
  482.                  si
  483.  
  484.          mov     bx, si
  485.          mov     dx, Col                 ; DL = cursor column
  486.  
  487.          .WHILE  (BYTE PTR [bx] != 0)    ; Scan string for null terminator
  488.          inc     bx                      ; Else try next character
  489.          inc     dx                      ;   and increment cursor column
  490.          .ENDW
  491.  
  492.          ; Set cursor position, pass row and column (DX)
  493.          INVOKE  SetCurPos,
  494.                  Row,
  495.                  dx
  496.  
  497.          .REPEAT
  498.          int     28h                     ; Signal idle state
  499.          mov     ah, 1
  500.          int     16h                     ; Key waiting?
  501.          .CONTINUE .IF zero?
  502.          sub     ah, ah
  503.          int     16h                     ; Yes?  Get key
  504.  
  505.          cmp     ah, LEFT                ; Left arrow key?
  506.          je      backspace               ; Treat like backspace
  507.          .UNTIL  al != 0                 ; Ignore all other special keys
  508.  
  509.          .BREAK  .IF al == ESCAPE        ; Exit if Esc key
  510.          .BREAK  .IF al == CR            ; Exit if Return key
  511.  
  512.          .IF     al == BACKSP            ; If backspace or left, handle it
  513.  backspace:
  514.          cmp     bx, si                  ; At first letter?
  515.          jbe     loop1                   ; Yes?  Ignore backspace
  516.          dec     bx                      ; No?  Point to preceding char
  517.          dec     dx                      ; Decrement column
  518.          mov     BYTE PTR [bx], ' '      ; Blank char
  519.          push    bx                      ; Preserve pointer
  520.          INVOKE  StrWrite,               ; Overwrite last char with blank
  521.                  Row,
  522.                  dx,
  523.                  bx
  524.  
  525.          pop     bx
  526.          mov     BYTE PTR [bx], 0        ; Make last char the new terminator
  527.          .CONTINUE
  528.          .ENDIF
  529.  
  530.          .CONTINUE .IF bx > Max          ; Ignore key if too many letters
  531.          sub     ah, ah
  532.          mov     [bx], ax                ; Store letter and null terminator
  533.          .ENDW
  534.  
  535.          ret
  536.  
  537.  StrInput ENDP
  538.  
  539.  
  540.  ;* ClearBox - Clears portion of screen with specified fill attribute.
  541.  ;*
  542.  ;* Shows:   BIOS Interrupt - 10h, Function 6 (Scroll Up)
  543.  ;*
  544.  ;* Params:  Attr - Fill attribute
  545.  ;*          Row1 - Top screen row of cleared section
  546.  ;*          Col1 - Left column of cleared section
  547.  ;*          Row2 - Bottom screen row of cleared section
  548.  ;*          Col2 - Right column of cleared section
  549.  ;*
  550.  ;* Return:  None
  551.  
  552.  ClearBox PROC,
  553.          Attr:WORD,
  554.          Row1:WORD,
  555.          Col1:WORD,
  556.          Row2:WORD,
  557.          Col2:WORD
  558.  
  559.          mov     ax, 0600h               ; Scroll service
  560.          mov     bh, BYTE PTR Attr       ; BH = fill attribute
  561.          mov     ch, BYTE PTR Row1       ; CH = top row of clear area
  562.          mov     cl, BYTE PTR Col1       ; CL = left column
  563.          mov     dh, BYTE PTR Row2       ; DH = bottom row of clear area
  564.          mov     dl, BYTE PTR Col2       ; DL = right column
  565.          int     10h                     ; Clear screen by scrolling up
  566.          ret
  567.  
  568.  ClearBox ENDP
  569.  
  570.  
  571.  ;* DisableCga - Disables CGA video by reprogramming the control register.
  572.  ;*
  573.  ;* Shows:   Instructions - cli     sti
  574.  ;*
  575.  ;* Params:  None
  576.  ;*
  577.  ;* Return:  None
  578.  
  579.  DisableCga PROC USES ax cx dx           ; Preserve registers
  580.  
  581.          mov     cx, -1                  ; Set maximum loop count
  582.          mov     dx, 03DAh               ; Address of status register
  583.  
  584.          .REPEAT
  585.          in      al, dx                  ; Get video status
  586.          .UNTILCXZ !(al & 8)             ; Until retrace end/timeout
  587.          cli                             ; Disallow interruptions
  588.          mov     cx, -1                  ; Reset loop count
  589.  
  590.          .REPEAT
  591.          in      al, dx                  ; Get video status
  592.          .UNTILCXZ al & 8                ; Until retrace start/timeout
  593.  
  594.          sub     dx, 2                   ; DX = address of control reg
  595.          mov     al, 1                   ; Value to disable CGA video
  596.          out     dx, al                  ; Disable video
  597.          sti                             ; Reenable interrupts
  598.          ret
  599.  
  600.  DisableCga ENDP
  601.  
  602.  
  603.  ;* EnableCga - Enables CGA video by reprogramming the control register.
  604.  ;*
  605.  ;* Params:  None
  606.  ;*
  607.  ;* Return:  None
  608.  
  609.  EnableCga PROC USES ax dx es                    ; Preserve registers
  610.  
  611.          sub     ax, ax
  612.          mov     es, ax                          ; Point ES to low memory
  613.          mov     al, es:[0465h]                  ; Get former mode setting
  614.          mov     dx, 03D8h                       ; Address of control register
  615.          out     dx, al                          ; Enable video
  616.          ret
  617.  
  618.  EnableCga ENDP
  619.  
  620.  
  621.  ;* GetVer - Gets DOS version.
  622.  ;*
  623.  ;* Shows:   DOS Function - 30h (Get MS-DOS Version Number)
  624.  ;*
  625.  ;* Params:  None
  626.  ;*
  627.  ;* Return:  Short integer of form (M*100)+m, where M is major
  628.  ;*          version number and m is minor version, or 0 if
  629.  ;*          DOS version earlier than 2.0
  630.  
  631.  GetVer  PROC
  632.  
  633.          mov     ah, 30h                 ; DOS Function 30h
  634.          int     21h                     ; Get MS-DOS Version Number
  635.          .IF     al == 0                 ; If version, version 1
  636.          sub     ax, ax                  ; Set AX to 0
  637.          .ELSE                           ; Version 2.0 or higher
  638.          sub     ch, ch                  ; Zero CH and move minor
  639.          mov     cl, ah                  ;   version number into CX
  640.          mov     bl, 100
  641.          mul     bl                      ; Multiply major by 10
  642.          add     ax, cx                  ; Add minor to major*10
  643.          .ENDIF
  644.          ret                             ; Return result in AX
  645.  
  646.  GetVer  ENDP
  647.  
  648.          END
  649.  
  650.  
  651.  FILE.ASM
  652.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\DEMOS\FILE.ASM
  653.  
  654.          .MODEL small, pascal, os_dos
  655.          INCLUDE demo.inc
  656.          .CODE
  657.  
  658.  ;* ReadCharAttr - Reads character and display attribute at cursor location.
  659.  ;*
  660.  ;* Shows:   BIOS Interrupt - 10h, Function 8 (Read Character and Attribute
  661.  ;*                                at Cursor)
  662.  ;*
  663.  ;* Uses:    vconfig - Video configuration structure (initialized
  664.  ;*          by calling the GetVidConfig procedure)
  665.  ;*
  666.  ;* Params:  Attr - Pointer to short integer for display attribute
  667.  ;*
  668.  ;* Return:  Short integer with ASCII value of character
  669.  
  670.  ReadCharAttr PROC USES di,
  671.          Attr:PWORD
  672.  
  673.          mov     ah, 8                   ; Function 8
  674.          mov     bh, vconfig.dpage       ; Current page
  675.          int     10h                     ; Read Character and Attribute
  676.          sub     bh, bh
  677.          mov     bl, ah                  ; BX = attribute
  678.          cbw                             ; AX = character
  679.          LoadPtr es, di, Attr            ; ES:DI = pointer to int
  680.          mov     es:[di], bx             ; Copy attribute
  681.          ret
  682.  
  683.  ReadCharAttr ENDP
  684.  
  685.  
  686.  ;* CopyFile - Copies a file from a specified directory to another. Allows
  687.  ;* two different copy methods. See the OpenFile, CloseFile, ReadFile, and
  688.  ;* WriteFile procedures for specific examples on opening, closing, reading
  689.  ;* from, and writing to files.
  690.  ;*
  691.  ;* Shows:   DOS Functions - 3Ch (Create File)
  692.  ;*                          5Bh (Create New File)
  693.  ;*          Instruction - clc
  694.  ;*
  695.  ;* Params:  Imode  - 0 = Create new target file or overwrite existing file
  696.  ;*                   1 = Abort and return error code if target file already
  697.  ;*                       exists (only for DOS versions 3.0 and higher)
  698.  ;*          Fspec1 - Pointer to ASCIIZ source file specification
  699.  ;*          Fspec2 - Pointer to ASCIIZ target file specification
  700.  ;*
  701.  ;* Return:  Short integer with error code
  702.  ;*          0 if successful
  703.  ;*          1 if error
  704.  
  705.          .DATA
  706.  Buffer  BYTE    BUFFERSIZE DUP (?)     ; Buffer for diskette read
  707.  
  708.          .CODE
  709.  
  710.  CopyFile PROC USES ds si di,
  711.          Imode:WORD,
  712.          Fspec1:PBYTE,
  713.          Fspec2:PBYTE
  714.  
  715.          LOCAL eof_flag:BYTE
  716.  
  717.  ; Open source file for read only
  718.  
  719.          LoadPtr ds, dx, Fspec1          ; Point DS:DX to source file
  720.          mov     ax, 3D00h               ; AH = function #, AL = access code
  721.          int     21h                     ; Open File (for read only)
  722.          jc      e_exit
  723.          mov     si, ax                  ; SI = file handle for source
  724.  
  725.  ; Open target file according to copy mode
  726.  
  727.          LoadPtr ds, dx, Fspec2          ; Point DS:DX to target file
  728.          .IF     Imode != 1              ; If Imode (DOS function) is not 1
  729.          mov     ah, 3Ch                 ; Request Create File
  730.          .ELSE
  731.  
  732.          ; Check DOS version
  733.          INVOKE  GetVer
  734.  
  735.          cmp     ax, 300                 ; 3.0 or higher?
  736.          jb      close                   ; No?  Abort with error code
  737.          mov     ah, 5Bh                 ; Request Create New File
  738.          .ENDIF
  739.          sub     cx, cx                  ; Normal attribute for target
  740.          int     21h                     ; DOS function for target file
  741.          jc      close                   ; If open error, abort
  742.          mov     di, ax                  ; DI = file handle for target
  743.  
  744.  ; Both files successfully opened. Now read from source and copy to target.
  745.  
  746.          mov     ax, @data
  747.          mov     ds, ax                  ; DS:DX = buffer. Read/write
  748.          mov     dx, OFFSET Buffer       ;   to and from here.
  749.          mov     eof_flag, 0             ; Initialize end-of-file flag
  750.  
  751.          .REPEAT
  752.          mov     bx, si                  ; Handle for source file
  753.          mov     cx, BUFFERSIZE          ; CX = number of bytes to read
  754.          mov     ah, 3Fh                 ; Request DOS read
  755.          int     21h                     ; Read from File
  756.          jc      close                   ; If error, exit
  757.          .IF     ax != cx                ; If bytes not read successfully:
  758.          inc     eof_flag                ; Raise flag
  759.          .ENDIF
  760.          mov     bx, di                  ; Handle for target file
  761.          mov     cx, ax                  ; Write number of bytes read
  762.          mov     ah, 40h                 ; Request DOS write
  763.          int     21h                     ; Write from buffer to target file
  764.          jc      close                   ; If error, exit
  765.          .UNTIL  eof_flag != 0           ; Loop to read next block
  766.          clc                             ; Clear CY to indicate
  767.  close:
  768.          pushf                           ; Preserve flags while closing
  769.          mov     bx, di                  ; Handle for target file
  770.          mov     ah, 3Eh                 ; Request DOS Function 3Eh
  771.          int     21h                     ; Close File
  772.          sub     ax, ax                  ; Clear error code
  773.          popf                            ; Recover flags
  774.          .IF     carry?
  775.  e_exit:
  776.          mov     ax, 1                   ; Else set error code
  777.          .ENDIF
  778.          ret
  779.  
  780.  CopyFile ENDP
  781.  
  782.  
  783.  ;* ChangeDrive - Changes default drive.
  784.  ;*
  785.  ;* Shows:   DOS Function - 0Eh (Select Disk)
  786.  ;*
  787.  ;* Params:  Drive - Uppercase letter designation for new drive
  788.  ;*
  789.  ;* Return:  None
  790.  
  791.  ChangeDrive PROC,
  792.          Drive:WORD
  793.  
  794.          mov     ah, 0Eh                 ; DOS Function 0Eh
  795.          mov     dx, Drive               ; Drive designation in DL,
  796.          sub     dl, 'A'                 ;   0=A, 1=B, 2=C, etc
  797.          int     21h                     ; Select Disk
  798.          ret
  799.  
  800.  ChangeDrive ENDP
  801.  
  802.  
  803.  ;* GetCurDrive - Gets designation of current drive.
  804.  ;*
  805.  ;* Shows:   DOS Function - 19h (Get Current Disk)
  806.  ;*          Instruction - cbw
  807.  ;*
  808.  ;* Params:  None
  809.  ;*
  810.  ;* Return:  Short integer with drive designation
  811.  ;*          0 = A, 1 = B, 2 = C, etc.
  812.  
  813.  GetCurDrive PROC
  814.  
  815.          mov     ah, 19h                 ; DOS Function 19h
  816.          int     21h                     ; Get Current Disk
  817.          cbw                             ; AX = drive designation
  818.          ret
  819.  
  820.  GetCurDrive ENDP
  821.  
  822.  
  823.  ;* SetDTA - Sets address for new Disk Transfer Area.
  824.  ;*
  825.  ;* Shows:   DOS Function - 1Ah (Set DTA Address)
  826.  ;*
  827.  ;* Params:  Dta - Far pointer to new transfer address
  828.  ;*
  829.  ;* Return:  None
  830.  
  831.  SetDTA  PROC USES ds,
  832.          Dta:FPBYTE
  833.  
  834.          lds     dx, [Dta]               ; Point DS:DX to DTA
  835.          mov     ah, 1Ah                 ; DOS Function 1Ah
  836.          int     21h                     ; Set DTA Address
  837.          ret
  838.  
  839.  SetDTA  ENDP
  840.  
  841.  
  842.  ;* GetDTA - Gets address of current Disk Transfer Area.
  843.  ;*
  844.  ;* Shows:   DOS Function - 2Fh (Get DTA Address)
  845.  ;*
  846.  ;* Params:  Dta - Far pointer to receive transfer address
  847.  ;*
  848.  ;* Return:  None
  849.  
  850.  GetDTA  PROC,
  851.          Dta:FPBYTE
  852.  
  853.          mov     ah, 2Fh                 ; DOS Function 2Fh
  854.          int     21h                     ; Get DTA Address in ES:BX
  855.          mov     ax, es                  ; Save DTA segment
  856.          mov     dx, bx                  ; Save DTA offset
  857.          les     bx, Dta                 ; Now ES:BX points to variable
  858.          mov     es:[bx], dx             ; Copy DTA address to
  859.          mov     es:[bx+2], ax           ;       dta variable
  860.          ret
  861.  
  862.  GetDTA  ENDP
  863.  
  864.  
  865.  ;* CreateFile - Creates file with specified attribute.
  866.  ;*
  867.  ;* Shows:   DOS Function - 3Ch (Create File)
  868.  ;*
  869.  ;* Params:  Attr - Attribute code:  0 = normal        8 = volume label
  870.  ;*                                        1 = read only    16 = subdirectory
  871.  ;*                                        2 = hidden              32 = archiv
  872.  ;*                                        4 = system
  873.  ;*          Fspec - Pointer to ASCIIZ file specification
  874.  ;*
  875.  ;* Return:  Short integer with file handle or -1 for error
  876.  
  877.  CreateFile PROC USES ds,
  878.          Attr:WORD, Fspec:PBYTE
  879.  
  880.          LoadPtr ds, dx, Fspec           ; Point DS:DX to file spec
  881.          mov     cx, Attr                ; CX = attribute
  882.          mov     ah, 3Ch                 ; AH = function number
  883.          int     21h                     ; Create file
  884.          .IF     carry?
  885.          mov     ax, -1                  ; Set error code
  886.          .ENDIF
  887.          ret
  888.  
  889.  CreateFile ENDP
  890.  
  891.  
  892.  ;* OpenFile - Opens specified file for reading or writing. See the CopyFile
  893.  ;* procedure for another example of using DOS Function 3Dh to open files.
  894.  ;*
  895.  ;* Shows:   DOS Function - 3Dh (Open File)
  896.  ;*
  897.  ;* Params:  Access - Access code:  0 = read    1 = write    2 = read/write
  898.  ;*          Fspec - Pointer to ASCIIZ file specification
  899.  ;*
  900.  ;* Return:  Short integer with file handle or -1 for error
  901.  
  902.  OpenFile PROC USES ds,
  903.          Access:WORD, Fspec:PBYTE
  904.  
  905.          LoadPtr ds, dx, Fspec           ; Point DS:DX to file spec
  906.          mov     ax, Access              ; AL = access code
  907.          mov     ah, 3Dh                 ; AH = function number
  908.          int     21h                     ; Open file
  909.          .IF     carry?
  910.          mov     ax, -1                  ; Set error code
  911.          .ENDIF
  912.          ret
  913.  
  914.  OpenFile ENDP
  915.  
  916.  
  917.  ;* CloseFile - Closes an open file, specified by handle. See the CopyFile
  918.  ;* procedure for another example of using DOS Function 3Eh to close files.
  919.  ;*
  920.  ;* Shows:   DOS Function - 3EH (Close File)
  921.  ;*
  922.  ;* Params:  Handle - File handle
  923.  ;*
  924.  ;* Return:  None
  925.  
  926.  CloseFile PROC,
  927.          Handle:WORD
  928.  
  929.          mov     bx, Handle              ; BX = file handle
  930.          mov     ah, 3Eh                 ; DOS Function 3Eh
  931.          int     21h                     ; Close file
  932.          ret
  933.  
  934.  CloseFile ENDP
  935.  
  936.  
  937.  ;* ReadFile - Read from open file to specified buffer. See the CopyFile
  938.  ;* procedure for another example of using DOS Function 3Fh to read files.
  939.  ;*
  940.  ;* Shows:   DOS Function - 3Fh (Read File or Device)
  941.  ;*
  942.  ;* Params:  Handle - File handle
  943.  ;*          Len - Number of bytes to read
  944.  ;*          Pbuff - Pointer to buffer
  945.  ;*
  946.  ;* Return:  Short integer with number of bytes read, or 0 if read error
  947.  
  948.  ReadFile PROC USES ds di,
  949.          Handle:WORD, Len:WORD, Pbuff:PBYTE
  950.  
  951.          LoadPtr ds, dx, Pbuff           ; Point DS:DX to buffer
  952.          mov     di, dx                  ; Keep string offset in DI
  953.          mov     bx, Handle              ; BX = handle
  954.          mov     cx, Len                 ; CX = number of bytes to read
  955.          mov     ah, 3Fh                 ; Request DOS read
  956.          int     21h                     ; Read File
  957.          .IF     carry?
  958.          sub     ax, ax                  ; Set error code
  959.          .ENDIF
  960.          ret
  961.  
  962.  ReadFile ENDP
  963.  
  964.  
  965.  ;* WriteFile - Write ASCIIZ string to file. If Handle = 0, the string is
  966.  ;* written to STDOUT (console). See the CopyFile procedure for another
  967.  ;* example of using DOS Function 40h to write to files.
  968.  ;*
  969.  ;* Shows:   DOS Function - 40h (Write File or Device)
  970.  ;*          Instructions - inc      dec
  971.  ;*
  972.  ;* Params:  Handle - File handle
  973.  ;*          SPtr - Pointer to ASCIIZ string
  974.  ;*
  975.  ;* Return:  Short integer with error code
  976.  ;*          0 if successful
  977.  ;*          1 if write error
  978.  ;*          2 if number of bytes written not equal to string length
  979.  
  980.  WriteFile PROC USES ds di,
  981.          Handle:WORD, Sptr:PBYTE
  982.  
  983.          LoadPtr es, di, Sptr            ; Point ES:DI to string
  984.          push    di                      ; Hold on to string pointer
  985.          mov     cx, -1                  ; Set CX to maximum
  986.          sub     al, al                  ; AL = 0
  987.          repne   scasb                   ; Scan string for NULL
  988.          pop     dx                      ; Recover string pointer
  989.          dec     di
  990.          sub     di, dx                  ; Get string length (w/o NULL)
  991.          mov     cx, di                  ; Put it into CX
  992.          mov     bx, Handle              ; Load BX with handle
  993.          push    es                      ; Set DS to ES to ensure
  994.          pop     ds                      ;   DS:DX points to string
  995.          mov     ah, 40h                 ; Request DOS write
  996.          int     21h                     ; Write File or Device
  997.          mov     bx, ax                  ; Get number of bytes written
  998.          mov     ax, 0                   ; Set error code, preserve carry
  999.          .IF     carry?                   ; If carry:
  1000.          inc     ax                      ; Increment once for write error
  1001.          .ENDIF  ; carry
  1002.          .IF     bx != cx                ; If bytes not all written:
  1003.          inc     ax                      ; Increment twice
  1004.          .ENDIF  ; bx ! cx
  1005.          ret
  1006.  
  1007.  WriteFile ENDP
  1008.  
  1009.  
  1010.  ;* GetDiskSize - Gets size information from specified disk.
  1011.  ;*
  1012.  ;* Shows:   DOS Function - 36h (Get Drive Allocation Information)
  1013.  ;*
  1014.  ;* Params:  Drive - Drive code (0 = default, 1 = A, 2 = B, etc.)
  1015.  ;*          Disk  - Pointer to a structure with 4 short integer members:
  1016.  ;*                      Member 1 - Total clusters on disk
  1017.  ;*                      Member 2 - Number of available clusters
  1018.  ;*                      Member 3 - Sectors/cluster (-1 if invalid drive)
  1019.  ;*                      Member 4 - Bytes/sector
  1020.  ;*
  1021.  ;* Return:  None
  1022.  
  1023.  GetDiskSize PROC USES di,
  1024.          Drive:WORD, Disk:PDISKSTAT
  1025.  
  1026.          mov     dx, Drive               ; DL = drive code
  1027.          mov     ah, 36h                 ; DOS Function 36h
  1028.          int     21h                     ; Get Drive Allocation Information
  1029.          LoadPtr es, di, Disk            ; ES:DI = disk structure
  1030.          mov     (DISKSTAT PTR es:[di]).\
  1031.                  total, dx               ; DX = total clusters
  1032.          mov     (DISKSTAT PTR es:[di]).\
  1033.                  avail, bx               ; BX = number of free clusters
  1034.          mov     (DISKSTAT PTR es:[di]).\
  1035.                  sects, ax               ; AX = sectors/cluster
  1036.          mov     (DISKSTAT PTR es:[di]).\
  1037.                  bytes, cx               ; CX = bytes/sector
  1038.          ret
  1039.  
  1040.  GetDiskSize ENDP
  1041.  
  1042.  
  1043.  ;* MakeDir - Creates a specified subdirectory.
  1044.  ;*
  1045.  ;* Shows:   DOS Function - 39h (Create Directory)
  1046.  ;*
  1047.  ;* Params:  Pspec - Pointer to ASCIIZ pathname of new subdirectory
  1048.  ;*
  1049.  ;* Return:  Short integer with error code
  1050.  ;*          0 if successful
  1051.  ;*          1 if create error
  1052.  
  1053.  MakeDir PROC USES ds,
  1054.          Pspec:PBYTE
  1055.  
  1056.          LoadPtr ds, dx, Pspec           ; Point DS:DX to path spec
  1057.          mov     ah, 39h                 ; DOS Function 39h
  1058.          int     21h                     ; Create Directory
  1059.          mov     ax, 0                   ; Set error code, keep flags
  1060.          .IF     carry?
  1061.          inc     ax                      ; Set error code to 1
  1062.          .ENDIF
  1063.          ret
  1064.  
  1065.  MakeDir ENDP
  1066.  
  1067.  
  1068.  ;* RemoveDir - Removes a specified subdirectory.
  1069.  ;*
  1070.  ;* Shows:   DOS Function - 3Ah (Delete Directory)
  1071.  ;*
  1072.  ;* Params:  Pspec - Pointer to ASCIIZ pathname of subdirectory
  1073.  ;*
  1074.  ;* Return:  Short integer with error code
  1075.  ;*          0 if successful
  1076.  ;*          1 if delete error or subdirectory not empty
  1077.  
  1078.  RemoveDir PROC USES ds,
  1079.          Pspec:PBYTE
  1080.  
  1081.          LoadPtr ds, dx, Pspec           ; Point DS:DX to path spec
  1082.          mov     ah, 3Ah                 ; DOS Function 3Ah
  1083.          int     21h                     ; Delete Directory
  1084.          mov     ax, 0                   ; Set error code, keep flags
  1085.          .IF     carry?
  1086.          inc     ax                      ; Set error code to 1
  1087.          .ENDIF
  1088.          ret
  1089.  
  1090.  RemoveDir ENDP
  1091.  
  1092.  
  1093.  ;* ChangeDir - Changes current (default) directory.
  1094.  ;*
  1095.  ;* Shows:   DOS Function - 3Bh (Set Current Directory)
  1096.  ;*
  1097.  ;* Params:  Pspec - Pointer to ASCIIZ pathname of target subdirectory
  1098.  ;*
  1099.  ;* Return:  Short integer with error code
  1100.  ;*          0 if successful
  1101.  ;*          1 if delete error or subdirectory not empty
  1102.  
  1103.  ChangeDir PROC USES ds,
  1104.          Pspec:PBYTE
  1105.  
  1106.          LoadPtr ds, dx, Pspec           ; Point DS:DX to path spec
  1107.          mov     ah, 3Bh                 ; DOS Function 3Bh
  1108.          int     21h                     ; Set Current Directory
  1109.          mov     ax, 0                   ; Set error code, keep flags
  1110.          .IF     carry?
  1111.          inc     ax                      ; Set error code to 1
  1112.          .ENDIF
  1113.          ret
  1114.  
  1115.  ChangeDir ENDP
  1116.  
  1117.  
  1118.  ;* DelFile - Deletes a specified file.
  1119.  ;*
  1120.  ;* Shows:   DOS Function - 41h (Delete File)
  1121.  ;*
  1122.  ;* Params:  Fspec - Pointer to ASCIIZ file specification
  1123.  ;*
  1124.  ;* Return:  Short integer with error code
  1125.  ;*          0 if successful
  1126.  ;*          1 if delete error
  1127.  
  1128.  DelFile PROC USES ds,
  1129.          Fspec:PBYTE
  1130.  
  1131.          LoadPtr ds, dx, Fspec           ; Point DS:DX to file spec
  1132.          mov     ah, 41h                 ; DOS Function 41h
  1133.          int     21h                     ; Delete File
  1134.          mov     ax, 0                   ; Set error code, keep flags
  1135.          .IF     carry?
  1136.          inc     ax                      ; Set error code to 1
  1137.          .ENDIF
  1138.          ret
  1139.  
  1140.  DelFile ENDP
  1141.  
  1142.  
  1143.  ;* Rewind - Rewinds an open file, specified by handle. See the GetFileSize
  1144.  ;* procedure for an example of using Function 42h to determine file size.
  1145.  ;*
  1146.  ;* Shows:   DOS Function - 42h (Set File Pointer)
  1147.  ;*
  1148.  ;* Params:  Handle - File handle
  1149.  ;*
  1150.  ;* Return:  None
  1151.  
  1152.  Rewind  PROC,
  1153.          Handle:WORD
  1154.  
  1155.          mov     bx, Handle              ; BX = file handle
  1156.          mov     ax, 4200h               ; AH = function #,
  1157.                                          ; AL = move to beginning of
  1158.          sub     cx, cx                  ;      file plus offset
  1159.          sub     dx, dx                  ; CX:DX = offset (zero)
  1160.          int     21h                     ; Set File Pointer
  1161.          ret
  1162.  
  1163.  Rewind  ENDP
  1164.  
  1165.  
  1166.  ;* GetFileSize - Gets the size of an open file, specified by handle.
  1167.  ;*
  1168.  ;* Shows:   DOS Function - 42h (Set File Pointer)
  1169.  ;*
  1170.  ;* Params:  Handle - File handle
  1171.  ;*
  1172.  ;* Return:  Long integer with file size in bytes
  1173.  
  1174.  GetFileSize PROC,
  1175.          Handle:WORD
  1176.  
  1177.          mov     bx, Handle              ; BX = file handle
  1178.          mov     ax, 4202h               ; AH = function #,
  1179.                                          ; AL = move to end of
  1180.          sub     cx, cx                  ;      file plus offset
  1181.          sub     dx, dx                  ; CX:DX = offset (zero)
  1182.          int     21h                     ; Set File Pointer
  1183.          mov     ax, dx                  ; Set DX:AX = file size in
  1184.          mov     dx, cx                  ;   bytes, return long int
  1185.          ret
  1186.  
  1187.  GetFileSize ENDP
  1188.  
  1189.  
  1190.  ;* GetAttribute - Gets the attribute(s) of a specified file.
  1191.  ;*
  1192.  ;* Shows:   DOS Function - 43h (Get or Set File Attributes)
  1193.  ;*
  1194.  ;* Params:  Fspec - Pointer to ASCIIZ file specification
  1195.  ;*
  1196.  ;* Return:  Short integer with file attribute bits set as follows:
  1197.  ;*                bit 0 = read-only              bit 3 = volume label
  1198.  ;*                bit 1 = hidden                 bit 4 = subdirectory
  1199.  ;*                bit 2 = system                 bit 5 = archive
  1200.  ;*          0 indicates normal data file
  1201.  ;*          -1 indicates error
  1202.  
  1203.  GetAttribute PROC USES ds,
  1204.          Fspec:PBYTE
  1205.  
  1206.          LoadPtr ds, dx, Fspec           ; DS:DX = file specification
  1207.          mov     ax, 4300h               ; AH = function #
  1208.                                          ; AL = 0 (return attribute)
  1209.          int     21h                     ; Get File Attributes
  1210.          mov     ax, -1                  ; Set code, keep flags
  1211.          .IF     !carry?
  1212.          mov     ax, cx                  ; Return with file attribute bits
  1213.          .ENDIF
  1214.          ret
  1215.  
  1216.  GetAttribute ENDP
  1217.  
  1218.  
  1219.  ;* SetAttribute - Sets the attribute(s) of a specified file.
  1220.  ;*
  1221.  ;* Shows:   DOS Function - 43h (Get or Set File Attributes)
  1222.  ;*
  1223.  ;* Params:  Attr - Attribute bits set as follows:
  1224.  ;*                        bit 0 = read-only      bit 3 = volume label
  1225.  ;*                        bit 1 = hidden                 bit 4 = subdirectory
  1226.  ;*                        bit 2 = system                 bit 5 = archive
  1227.  ;*                 (Attr = 0 for normal data file)
  1228.  ;*          Fspec - Pointer to ASCIIZ file specification
  1229.  ;*
  1230.  ;* Return:  Short integer with error code
  1231.  ;*          0 if successful
  1232.  ;*          1 if delete error
  1233.  
  1234.  SetAttribute PROC USES ds,
  1235.          Attr:WORD,
  1236.          Fspec:PBYTE
  1237.  
  1238.          LoadPtr ds, dx, Fspec           ; DS:DX = file specification
  1239.          mov     cx, Attr                ; Put attribute code in CX
  1240.          mov     ax, 4301h               ; AH = function #
  1241.                                          ; AL = 1 (set attribute)
  1242.          int     21h                     ; Set File Attributes
  1243.          mov     ax, 0                   ; Clear code, keep flags
  1244.          .IF     carry?
  1245.          inc     ax                      ; Set error code to 1
  1246.          .ENDIF
  1247.          ret
  1248.  
  1249.  SetAttribute ENDP
  1250.  
  1251.  
  1252.  ;* GetCurDir - Gets the current directory of default drive.
  1253.  ;*
  1254.  ;* Shows:   DOS Function - 47h (Get Current Directory)
  1255.  ;*
  1256.  ;* Params:  Spec - Pointer to 64-byte buffer to receive directory
  1257.  ;*          path. Path terminates with 0 but does not include
  1258.  ;*          drive and does not begin with backslash.
  1259.  ;*
  1260.  ;* Return:  Short integer with error code
  1261.  ;*          0 if successful
  1262.  ;*          1 if delete error or subdirectory not empty
  1263.  
  1264.  GetCurDir PROC USES ds si,
  1265.          Spec:PBYTE
  1266.  
  1267.          LoadPtr ds, si, Spec            ; DS:SI = spec address
  1268.          mov     ah, 47h                 ; AH = function number
  1269.          sub     dl, dl                  ; DL = current drive (0)
  1270.          int     21h                     ; Get Current Directory
  1271.          mov     ax, 0                   ; Set error code, keep flags
  1272.          .IF     carry?
  1273.          inc     ax                      ; Set error code to 1
  1274.          .ENDIF
  1275.          ret
  1276.  
  1277.  GetCurDir ENDP
  1278.  
  1279.  
  1280.  ;* FindFirst - Finds first entry in given directory matching specification.
  1281.  ;*
  1282.  ;* Shows:   DOS Function - 4Eh (Find First File)
  1283.  ;*          Instructions - pushf    popf
  1284.  ;*
  1285.  ;* Params:  Attr - Attribute code (see header comments for CreateFile)
  1286.  ;*          Fspec - Pointer to ASCIIZ file specification
  1287.  ;*          Finfo - Pointer to 43-byte buffer to receive
  1288.  ;*                      data from matched entry
  1289.  ;*
  1290.  ;* Return:  Short integer with error code
  1291.  ;*          0 if successful
  1292.  ;*          1 if no match found
  1293.  
  1294.          .DATA
  1295.  OldDta  FPVOID  ?                       ; Storage for old DTA address
  1296.  
  1297.          .CODE
  1298.  
  1299.  FindFirst PROC USES ds,
  1300.          Attr:WORD,
  1301.          Fspec:PBYTE,
  1302.          Finfo:PFILEINFO
  1303.  
  1304.          ; Get current DTA address, pass address of pointer to hold value
  1305.          INVOKE  GetDTA,
  1306.                  ADDR OldDta
  1307.  
  1308.          mov     cx, Attr                ; Load CX with file attribute
  1309.  
  1310.          ; Set DTA address, pass pointer to structure
  1311.          INVOKE  SetDTA,
  1312.                  Finfo
  1313.  
  1314.          LoadPtr ds, dx, Fspec           ; Point DS:DX to file spec
  1315.          mov     ah, 4Eh                 ; AH = function number
  1316.          int     21h                     ; Find First File
  1317.  
  1318.          pushf                           ; Preserve flags
  1319.  
  1320.          ; Restore DTA address, pass pointer
  1321.          INVOKE  SetDTA,
  1322.                  OldDta
  1323.  
  1324.          sub     ax, ax                  ; Set error code
  1325.          popf                            ; Recover flags
  1326.          .IF     carry?
  1327.          inc     ax                      ; Set error code to 1
  1328.          .ENDIF
  1329.          ret
  1330.  
  1331.  FindFirst ENDP
  1332.  
  1333.  
  1334.  ;* FindNext - Finds next entry in given directory matching specification.
  1335.  ;* (Should be called only after successfully calling the FindFirst procedure.
  1336.  ;*
  1337.  ;* Shows:   DOS Function - 4Fh (Find Next File)
  1338.  ;*          Operator - OFFSET
  1339.  ;*
  1340.  ;* Params:  Finfo - Pointer to 43-byte buffer. This must be the same buffer
  1341.  ;*                      (or a duplicate) returned from the FindFirst procedur
  1342.  ;*
  1343.  ;* Return:  Short integer with error code
  1344.  ;*          0 if successful
  1345.  ;*          1 if no more matches found
  1346.  
  1347.  FindNext PROC USES ds,
  1348.          Finfo:PFILEINFO
  1349.  
  1350.          ; Get current DTA address, pass address of pointer to hold value
  1351.          INVOKE  GetDTA,
  1352.                  ADDR OldDta
  1353.  
  1354.          ; Set DTA address, pass pointer to structure
  1355.          INVOKE  SetDTA,
  1356.                  Finfo
  1357.  
  1358.          mov     ah, 4Fh                 ; AH = function number
  1359.          int     21h                     ; Find Next File
  1360.  
  1361.          pushf                           ; Preserve flags
  1362.  
  1363.          ; Restore DTA address, pass pointer
  1364.          INVOKE  SetDTA,
  1365.                  OldDta
  1366.  
  1367.          sub     ax, ax                  ; Set error code
  1368.          popf                            ; Recover flags
  1369.          .IF     carry?
  1370.          inc     ax                      ; Set error code to 1
  1371.          .ENDIF
  1372.          ret
  1373.  
  1374.  FindNext ENDP
  1375.  
  1376.  
  1377.  ;* RenameFile - Renames specified file.
  1378.  ;*
  1379.  ;* Shows:   DOS Function - 56h (Rename File)
  1380.  ;*
  1381.  ;* Params:  Fspec1 - Pointer to old ASCIIZ file specification
  1382.  ;*          Fspec2 - Pointer to new ASCIIZ file specification
  1383.  ;*
  1384.  ;*          The drive must be the same for both arguments, but the path
  1385.  ;*          does not. This allows files to be moved between directories.
  1386.  ;*
  1387.  ;* Return:  Short integer with error code
  1388.  ;*          0 if successful
  1389.  ;*          1 if error
  1390.  
  1391.  RenameFile PROC USES ds di,
  1392.          Fspec1:PBYTE,
  1393.          Fspec2:PBYTE
  1394.  
  1395.          LoadPtr ds, dx, Fspec1          ; Point DS:DX to old file spec
  1396.          LoadPtr es, di, Fspec2          ; Point ES:DI to new file spec
  1397.          mov     ah, 56h                 ; AH = function number
  1398.          int     21h                     ; Rename File
  1399.          mov     ax, 0                   ; Clear error code, keep flags
  1400.          .IF     carry?
  1401.          inc     ax                      ; Set error code to 1
  1402.          .ENDIF
  1403.          ret
  1404.  
  1405.  RenameFile ENDP
  1406.  
  1407.  
  1408.  ;* GetFileTime - Gets date/time for open file specified by handle.
  1409.  ;*
  1410.  ;* Shows:   DOS Function - 57h (Get or Set File Date and Time)
  1411.  ;*          Instructions - shl     shr
  1412.  ;*
  1413.  ;* Params:  Handle - Handle of open file
  1414.  ;*          Sptr - Pointer to 18-byte buffer to receive date/time
  1415.  ;*
  1416.  ;* Return:  Short integer with error code
  1417.  ;*          0 if successful
  1418.  ;*          1 if error
  1419.  
  1420.  GetFileTime PROC USES di,
  1421.          Handle:WORD,
  1422.          Sptr:PBYTE
  1423.  
  1424.          mov     ax, 5700h               ; AH = function number
  1425.                                          ; AL = get request
  1426.          mov     bx, Handle              ; BX = file handle
  1427.          int     21h                     ; Get File Date and Time
  1428.          mov     ax, 1                   ; Set error code, keep flags
  1429.          .IF     !carry?                 ; If not carry, continue
  1430.          mov     bx, cx                  ; Else save time in BX
  1431.          mov     al, bl                  ; Get low byte of time
  1432.          and     al, 00011111y           ; Mask to get 2-second incrs,
  1433.          shl     al, 1                   ;   convert to seconds
  1434.          push    ax                      ; Save seconds
  1435.          mov     cl, 5
  1436.          shr     bx, cl                  ; Shift minutes into low byte
  1437.          mov     al, bl                  ; Get new low byte
  1438.          and     al, 00111111y           ; Mask to get minutes
  1439.          push    ax                      ; Save minutes
  1440.          mov     cl, 6
  1441.          shr     bx, cl                  ; Shift hours into low byte
  1442.          push    bx                      ; Save hours
  1443.  
  1444.          mov     bl, dl                  ; Get low byte of date
  1445.          and     bl, 00011111y           ; Mask to get day in BX
  1446.          mov     cl, 5
  1447.          shr     dx, cl                  ; Shift month into low byte
  1448.          mov     al, dl                  ; Get new low byte
  1449.          and     al, 00001111y           ; Mask to get month
  1450.          mov     cl, 4
  1451.          shr     dx, cl                  ; Shift year into low byte
  1452.          add     dx, 80                  ; Year is relative to 1980
  1453.          push    dx                      ; Save year
  1454.          push    bx                      ; Save day
  1455.          push    ax                      ; Save month
  1456.  
  1457.          LoadPtr es, di, Sptr            ; Point ES:DI to 18-byte
  1458.          mov     cx, 6                   ;   string
  1459.  
  1460.          .REPEAT
  1461.          pop     ax                      ; Get 6 numbers sequentially in AL
  1462.          aam                             ; Convert to unpacked BCD
  1463.          xchg    al, ah                  ; Switch bytes for word move
  1464.          or      ax, '00'                ; Make ASCII numerals
  1465.          stosw                           ; Copy to string
  1466.          mov     al, '-'                 ; Separator for date text
  1467.          cmp     cl, 4                   ; First 3 iters are for date
  1468.          jg      @F                      ; If CX=6 or 5, insert hyphen
  1469.          mov     al, ' '                 ; Separator date and time
  1470.          je      @F                      ; If CX = 4, insert hyphen
  1471.          mov     al, ':'                 ; Separator for time text
  1472.          .IF     cl != 1
  1473.  @@:     stosb                           ; Copy separator to string
  1474.          .ENDIF
  1475.          .UNTILCXZ
  1476.  
  1477.          sub     ax, ax                  ; Clear return code
  1478.          stosb                           ; Terminate string with null
  1479.          .ENDIF                          ;   to make ASCIIZ
  1480.          ret
  1481.  
  1482.  GetFileTime ENDP
  1483.  
  1484.  
  1485.  ;* UniqueFile - Creates and opens a new file with a name unique to the
  1486.  ;* specified directory. The name is manufactured from the current time,
  1487.  ;* making it useful for temporary files. For DOS versions 3.0 and higher.
  1488.  ;*
  1489.  ;* Shows:   DOS Function - 5Ah (Create Temporary File)
  1490.  ;*
  1491.  ;* Params:  Attr - Attribute code (see header comments for CreateFile)
  1492.  ;*          Pspec - Pointer to ASCIIZ path specification
  1493.  ;*
  1494.  ;* Return:  Short integer with file handle or -1 for error
  1495.  
  1496.  UniqueFile PROC USES ds,
  1497.          Attr:WORD,
  1498.          Pspec:PBYTE
  1499.  
  1500.          ; Get DOS version
  1501.          INVOKE  GetVer
  1502.  
  1503.          cmp     ax, 300                 ; 3.0 or higher?
  1504.          jb      e_exit                  ; No?  Quit with error
  1505.          LoadPtr ds, dx, Pspec           ; Point DS:DX to path spec
  1506.          mov     cx, Attr                ; CX = attribute
  1507.          mov     ah, 5Ah                 ; AH = function number
  1508.          int     21h                     ; Create Temporary File
  1509.          .IF     carry?
  1510.  e_exit: mov     ax, -1                  ; Set error code
  1511.          .ENDIF
  1512.          ret
  1513.  
  1514.  UniqueFile ENDP
  1515.  
  1516.  
  1517.  ;* CreateNewFile - Creates a new file with specified attribute. Differs
  1518.  ;* from the CreateFile procedure in that it returns an error if file
  1519.  ;* already exists. For DOS versions 3.0 and higher.
  1520.  ;*
  1521.  ;* Shows:   DOS Function - 5Bh (Create New File)
  1522.  ;*
  1523.  ;* Params:  Attr - Attribute code (see header comments for CreateFile)
  1524.  ;*          Fspec - Pointer to ASCIIZ file specification
  1525.  ;*
  1526.  ;* Return:  Short integer with file handle or -1 for error
  1527.  
  1528.  CreateNewFile PROC USES ds,
  1529.          Attr:WORD,
  1530.          Fspec:PBYTE
  1531.  
  1532.          LoadPtr ds, dx, Fspec           ; Point DS:DX to file spec
  1533.          mov     cx, Attr                ; CX = attribute
  1534.          mov     ah, 5Bh                 ; AH = function number
  1535.          int     21h                     ; Create New File
  1536.          .IF     carry?
  1537.          mov     ax, -1                  ; Set error code
  1538.          .ENDIF
  1539.          ret
  1540.  
  1541.  CreateNewFile ENDP
  1542.  
  1543.  
  1544.  ;* StrCompare - Compares two strings for equality. See StrWrite, StrFindChar,
  1545.  ;* WinOpen, and WinClose procedures for other examples of string instructions
  1546.  ;*
  1547.  ;* Shows:   Instructions - cmpsb     cmpsw     repe     jcxz
  1548.  ;*
  1549.  ;* Params:  Sptr1 - Pointer to first string
  1550.  ;*          Sptr2 - Pointer to second string
  1551.  ;*          Len  - Length in bytes for comparison. Strings need not be of
  1552.  ;*                 equal length; however if len is an even number, comparison
  1553.  ;*                 is made on a word-by-word basis and thus is more efficient
  1554.  ;*
  1555.  ;* Return:  Null pointer if strings match; else pointer to string #1 where
  1556.  ;*          match failed.
  1557.  
  1558.  StrCompare PROC USES ds di si,
  1559.          Sptr1:PBYTE,
  1560.          Sptr2:PBYTE,
  1561.          Len:WORD
  1562.  
  1563.          LoadPtr es, di, Sptr1           ; ES:DI points to string #1
  1564.          LoadPtr ds, si, Sptr2           ; DS:SI points to string #2
  1565.          mov     cx, Len                 ; Length of search in bytes
  1566.          and     al, 0                   ; Set ZR flag in case CX = 0
  1567.          .IF     cx != 0                 ; If length is not 0:
  1568.          .IF     !(cl & 1)               ; If not even number:
  1569.          repe    cmpsb                   ; Compare byte-by-byte
  1570.          .ELSE                           ; Else compare word-by-word
  1571.          shr     cx, 1                   ; Decrease count by half
  1572.          repe    cmpsw                   ; Compare word-by-word
  1573.          sub     di, 2                   ; Back up 2 characters
  1574.          sub     si, 2
  1575.          cmpsb                           ; Match?
  1576.          .IF     zero?                   ; No?  Then failure
  1577.          cmpsb                           ; Compare last characters
  1578.          .ENDIF  ; zero
  1579.          .ENDIF  ; cl & 1
  1580.          .ENDIF  ; cx != 0
  1581.  
  1582.          mov     ax, 0                   ; Set null pointer without
  1583.          mov     dx, 0                   ;   disturbing flags
  1584.          .IF     !zero?                  ; If no match:
  1585.          dec     di                      ; Point to failure
  1586.          mov     ax, di
  1587.          mov     dx, es
  1588.          .ENDIF
  1589.          ret
  1590.  
  1591.  StrCompare ENDP
  1592.  
  1593.  
  1594.  ;* StrFindChar - Finds first occurence of character in given ASCIIZ string,
  1595.  ;* searching either from beginning or end of string. See StrWrite, WinOpen,
  1596.  ;* WinClose, and StrCompare procedures for other examples of string
  1597.  ;* instructions.
  1598.  ;*
  1599.  ;* Shows:   Instructions - repne     scasb    cld     std
  1600.  ;*
  1601.  ;* Params:  Ichar - Character to search for
  1602.  ;*          Sptr - Pointer to ASCIIZ string in which to search
  1603.  ;*          Direct - Direction flag:
  1604.  ;*                       0 = search from start to end
  1605.  ;*                       1 = search from end to start
  1606.  ;*
  1607.  ;* Return:  Null pointer if character not found, else pointer to string where
  1608.  ;*          character first encountered
  1609.  
  1610.  StrFindChar PROC USES ds di si,
  1611.          IChar:SBYTE,
  1612.          Sptr:PBYTE,
  1613.          Direct:WORD
  1614.  
  1615.          LoadPtr es, di, Sptr            ; ES:DI points to string
  1616.          LoadPtr ds, si, Sptr            ;   as does DS:SI
  1617.          mov     cx, -1                  ; Set scan counter to maximum
  1618.          mov     bx, cx                  ; BX = max string tail
  1619.          cld                             ; Assume head-to-tail search
  1620.  
  1621.          .IF     Direct != 0             ; If assumption correct:
  1622.          mov     bx, di                  ; Set BX to byte before
  1623.          dec     bx                      ;   string head and scan
  1624.          sub     al, al                  ;   string for null terminator
  1625.          push    cx                      ;   to find string tail
  1626.          repne   scasb
  1627.          pop     cx                      ; Recover scan counter
  1628.          dec     di                      ; Backup pointer to last
  1629.          dec     di                      ;   character in string and
  1630.          mov     si, di                  ;   begin search from there
  1631.          std                             ; Set direction flag
  1632.          .ENDIF
  1633.  
  1634.          .REPEAT
  1635.          lodsb                           ; Get first char from string
  1636.          .IF     (si == bx) || (al == 0) ; If at head or tail limit:
  1637.          sub     ax, ax                  ; No match
  1638.          sub     dx, dx                  ; Set null pointer
  1639.          jmp     exit
  1640.          .ENDIF
  1641.          .UNTILCXZ al == IChar
  1642.  
  1643.          mov     ax, si                  ; Match, so point to first
  1644.          dec     ax                      ;   occurence
  1645.          .IF     Direct != 0             ; If head-to-tail search:
  1646.          inc     ax                      ; Adjust pointer forward
  1647.          inc     ax
  1648.          mov     dx, ds                  ; Pointer segment
  1649.          .ENDIF
  1650.  exit:
  1651.          ret
  1652.  
  1653.  StrFindChar ENDP
  1654.  
  1655.  
  1656.  ;* GetStr - Gets a string of up to 128 characters from the user. Since
  1657.  ;* this function uses the DOS input mechanism, it can use the DOS editing
  1658.  ;* keys or the keys of a DOS command-line editor if one is loaded.
  1659.  ;*
  1660.  ;* Shows:   DOS Function - 0Ah (Buffered Keyboard Input)
  1661.  ;*          Directive    - EQU
  1662.  ;*
  1663.  ;* Params:  Strbuf - Pointer to area where input string will be placed
  1664.  ;*          Maxlen - Maximum length (up to 128 characters) of string
  1665.  ;*
  1666.  ;* Return:  0 if successful, 1 if error (Maxlen is too long)
  1667.  
  1668.          .DATA
  1669.  MAXSTR  EQU     128
  1670.  max     BYTE    MAXSTR
  1671.  actual  BYTE    ?
  1672.  string  BYTE    MAXSTR DUP (?)
  1673.  
  1674.          .CODE
  1675.  GetStr  PROC USES si di,
  1676.          Strbuf:PBYTE,
  1677.          Maxlen:WORD
  1678.  
  1679.          mov     ax, 1                   ; Assume error
  1680.          mov     cx, Maxlen              ; Copy length to register
  1681.  
  1682.          .IF (cx != 0) && (cx <= MAXSTR) ; Error if 0 or too long
  1683.          mov     max, cl                 ; Load maximum length
  1684.          mov     ah, 0Ah                 ; Request DOS Function 0Ah
  1685.          mov     dx, OFFSET max          ; Load offset of string
  1686.          int     21h                     ; Buffered Keyboard Input
  1687.  
  1688.          mov     bl, actual              ; Put number of characters read
  1689.          sub     bh, bh                  ;   in BX
  1690.          mov     string[bx], 0           ; Null-terminate string
  1691.          mov     cx, bx                  ; Put count in CX
  1692.          inc     cx                      ; Plus one for the null terminator
  1693.  
  1694.          LoadPtr es, di, Strbuf          ; ES:DI points to destination buffer
  1695.          mov     si, OFFSET string       ; DS:SI points to source string
  1696.          rep     movsb                   ; Copy source to destination
  1697.          sub     ax, ax                  ; Return 0 for success
  1698.          .ENDIF
  1699.  
  1700.          ret
  1701.  
  1702.  GetStr  ENDP
  1703.  
  1704.          END
  1705.  
  1706.  
  1707.  FORTRAN.ASM
  1708.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\MIXED\FORTRAN.ASM
  1709.  
  1710.  ; Power2 routine called by FMAIN.FOR
  1711.  ; Assemble with ML /c FORTRAN.ASM
  1712.  
  1713.          .MODEL LARGE, FORTRAN
  1714.  
  1715.  Power2  PROTO  FORTRAN, factor:FAR PTR SWORD, power:FAR PTR SWORD
  1716.  
  1717.          .CODE
  1718.  
  1719.  Power2  PROC   FORTRAN, factor:FAR PTR SWORD, power:FAR PTR SWORD
  1720.  
  1721.          les     bx, factor
  1722.          mov     ax, ES:[bx]
  1723.          les     bx, power
  1724.          mov     cx, ES:[bx]
  1725.          shl     ax, cl
  1726.          ret
  1727.  Power2  ENDP
  1728.          END
  1729.  
  1730.  
  1731.  HANDLERS.ASM
  1732.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\TSR\HANDLERS.ASM
  1733.  
  1734.          .MODEL  small, pascal, os_dos
  1735.  
  1736.  ; Prototypes for internal procedures
  1737.  Activate        PROTO  NEAR
  1738.  CheckRequest    PROTO  NEAR
  1739.  CheckDos        PROTO  NEAR
  1740.  CheckHardware   PROTO  NEAR
  1741.  GetDosFlags     PROTO  NEAR
  1742.  
  1743.          INCLUDE tsr.inc
  1744.  
  1745.          .CODE
  1746.  
  1747.  ; Stack buffer used by TSR. Size is determined by constant STACK_SIZ,
  1748.  ; declared in TSR.INC file. NewStack points to top of stack.
  1749.  
  1750.           EVEN
  1751.           BYTE   STACK_SIZ DUP(?)        ; Stack buffer
  1752.  NewStack LABEL BYTE                     ; Pointer to top of stack
  1753.  
  1754.  ; Structures for interrupt handlers or "interrupt service routines".
  1755.  ; The following handlers are replaced during installation. Such routines
  1756.  ; usually set a flag to indicate they are active, optionally do some
  1757.  ; processing (such as detecting a hot key), call the old system interrupt
  1758.  ; routine, and when finished clear the active flag.
  1759.  
  1760.  HandArray       LABEL   BYTE                    ; Array of handler structures
  1761.  ;                Num   Flag     OldHand  NewHand
  1762.  intClock  INTR  < 8h,  FALSE,   NULL,    Clock>
  1763.  intKeybrd INTR  < 9h,  FALSE,   NULL,    Keybrd>
  1764.  intVideo  INTR  <10h,  FALSE,   NULL,    Video>
  1765.  intDiskIO INTR  <13h,  FALSE,   NULL,    DiskIO>
  1766.  intMisc   INTR  <15h,  FALSE,   NULL,    SkipMiscServ>
  1767.  intIdle   INTR  <28h,  FALSE,   NULL,    Idle>
  1768.  intMultex INTR  <2Fh,  FALSE,   NULL,    Multiplex>
  1769.  
  1770.  CHAND   EQU     ($ - HandArray) / (SIZEOF INTR) ; Number of handlers in array
  1771.  
  1772.  ; Interrupt trap routines. These interrupt routines are set up
  1773.  ; temporarily to trap keyboard break errors and critical errors
  1774.  ; while the TSR is active. When the TSR finishes its tasks, it
  1775.  ; restores the old interrupts before returning.
  1776.  
  1777.  TrapArray       LABEL   BYTE                    ; Array of trap structures
  1778.  ;                Num   Flag     OldHand  NewHand
  1779.  intCtrlBk INTR  <1Bh,  FALSE,   NULL,    CtrlBreak>
  1780.  intCtrlC  INTR  <23h,  FALSE,   NULL,    CtrlC>
  1781.  intCritEr INTR  <24h,  FALSE,   NULL,    CritError>
  1782.  
  1783.  CTRAP   EQU     ($ - TrapArray) / (SIZEOF INTR) ; Number of traps in array
  1784.  
  1785.  ; Address of application's stack. Before calling the main body of the TSR,
  1786.  ; the Activate procedure stores the application's stack address, then resets
  1787.  ; SS:SP to point to LABEL NewStack (see above). This gives the TSR its own
  1788.  ; stack space without making demands on the current stack. Activate restores
  1789.  ; the application's stack before returning.
  1790.  
  1791.  OldStackAddr    FPVOID  ?               ; SS:SP pointer to application stack
  1792.  
  1793.  ; The TSR must set up its own disk transfer area if it calls DOS functions
  1794.  ; that use the DTA (see Section 17.5.3 of the Programmer's Guide). DTA_SIZ
  1795.  ; is defined in the TSR.INC include file.
  1796.  
  1797.                  IFDEF   DTA_SIZ
  1798.  OldDtaAddr      FPVOID  ?               ; Address of application's DTA
  1799.  DtaBuff         BYTE    DTA_SIZ DUP(?)  ; DTA buffer
  1800.                  ENDIF
  1801.  
  1802.  ; Multiplex data. STR_LEN is defined in the TSR.INC include file
  1803.  
  1804.  IDnumber        BYTE    0               ; TSR's identity number
  1805.  IDstring        BYTE    STR_LEN DUP (0) ; Copy of identifier string
  1806.  IDstrlen        WORD    ?               ; Length of identifier string
  1807.  ShareAddr       FPVOID  ?               ; Address of shared memory
  1808.  
  1809.  ; Miscellaneous data
  1810.  
  1811.  TsrRequestFlag  BYTE    FALSE           ; Flag set when hot key is pressed
  1812.  TsrActiveFlag   BYTE    FALSE           ; Flag set when TSR executes
  1813.  BreakCheckFlag  BYTE    ?               ; Break-checking status of applicatio
  1814.  TsrPspSeg       WORD    ?               ; Segment address of PSP
  1815.  TsrAddr         FPVOID  ?               ; Pointer to main part of TSR
  1816.  CritErrAddr     FPVOID  ?               ; Pointer to MS-DOS critical error fl
  1817.  InDosAddr       FPVOID  ?               ; Pointer to MS-DOS InDos flag
  1818.  
  1819.  ; Scan and shift codes for hot key. Install procedure initializes
  1820.  ; HotScan, HotShift, and HotMask during installation.
  1821.  
  1822.  HotScan         BYTE    ?               ; Scan code hot key
  1823.  HotShift        BYTE    ?               ; Shift value of hot key
  1824.  HotMask         BYTE    ?               ; Mask unwanted shift values
  1825.  
  1826.  Version         LABEL   WORD            ; DOS version number
  1827.  minor           BYTE    ?
  1828.  major           BYTE    ?
  1829.  
  1830.  ; Timer data, used when the TSR is activated at a preset time instead
  1831.  ; of activated from the keyboard. The following variables serve the
  1832.  ; same purposes as the counter variables used in the ALARM.ASM program
  1833.  ; presented in Section 17.3 of the Programmer's Guide. Refer to the
  1834.  ; header comments in the Install procedure for an explanation of how
  1835.  ; to set up a time-activated TSR.
  1836.  
  1837.  Tick91          BYTE    91              ; Measures 91 timer ticks (5 seconds)
  1838.  CountDown       WORD    0               ; Counts 5-second intervals
  1839.  
  1840.  
  1841.  
  1842.  ;* Clock - Interrupt handler for Interrupt 08 (timer). Executes at each
  1843.  ;* timer interrupt, which occur an average of 18.2 times per second. Clock
  1844.  ;* first allows the original timer service routine to execute. It then
  1845.  ;* checks the flag TsrRequestFlag maintained either by the keyboard handler
  1846.  ;* (if keyboard-activated) or by this procedure (if time-activated). If
  1847.  ;* TsrRequestFlag = TRUE and system is okay, Clock invokes the TSR by
  1848.  ;* calling the Activate procedure. Uses an active flag to prevent the
  1849.  ;* Clock procedure from being reentered while executing.
  1850.  ;*
  1851.  ;* Uses:   intClock, TsrActiveFlag, CountDown
  1852.  ;*
  1853.  ;* Params: None
  1854.  ;*
  1855.  ;* Return: None
  1856.  
  1857.  Clock   PROC    FAR
  1858.  
  1859.          pushf                           ; Simulate interrupt by pushing flags
  1860.          call    cs:intClock.OldHand     ;   far-calling orig Int 08 routine
  1861.  
  1862.          .IF     cs:intClock.Flag == FALSE ; If not already in this handler:
  1863.          mov     cs:intClock.Flag, TRUE  ; Set active flag
  1864.  
  1865.          sti                             ; Interrupts are okay
  1866.          push    ds                      ; Save application's DS
  1867.          push    cs
  1868.          pop     ds                      ; Set DS to resident code segment
  1869.          ASSUME  ds:@code
  1870.  
  1871.          INVOKE  CheckRequest            ; Check conditions
  1872.          .IF     !carry?                 ; If TSR requested and safe:
  1873.          mov     TsrActiveFlag, TRUE     ; Activate TSR
  1874.          INVOKE  Activate
  1875.          mov     TsrActiveFlag, FALSE
  1876.          .ENDIF                          ; End carry flag check
  1877.  
  1878.          cmp     CountDown, 0            ; If CountDown = 0, TSR is not time-
  1879.          je      ticked                  ;   activated or has already executed
  1880.          dec     Tick91                  ; Else count down 91 timer ticks
  1881.          jnz     ticked                  ; If 91 ticks have not elapsed, exit
  1882.          mov     Tick91, 91              ; Else reset secondary counter and
  1883.          dec     CountDown               ;   subract one 5-second interval
  1884.          ja      ticked                  ; If counter not yet drained, exit
  1885.          mov     TsrRequestFlag, TRUE    ; Else raise request flag
  1886.  ticked:
  1887.          mov     intClock.Flag, FALSE    ; Clear active flag
  1888.          pop     ds                      ; Recover application's DS
  1889.          ASSUME  ds:NOTHING
  1890.  
  1891.          .ENDIF                          ; End in-handler check
  1892.          iret
  1893.  
  1894.  Clock   ENDP
  1895.  
  1896.  
  1897.  ;* Keybrd - Interrupt handler for Interrupt 09 (keyboard).
  1898.  ;*
  1899.  ;* IBM PC/AT and compatibles:
  1900.  ;*      Gets the scan code of the current keystroke from port 60h. Then
  1901.  ;*      compares the scan code and shift state to the hot key. If they
  1902.  ;*      match, sets TsrRequestFlag to signal the handlers Clock and Idle
  1903.  ;*      that the TSR is requested.
  1904.  ;*
  1905.  ;* IBM PS/2 series:
  1906.  ;*      Only the instructions at KeybrdMonitor (see below) are installed
  1907.  ;*      as Interrupt 09 handler, since above method should not be used to
  1908.  ;*      determine current keystroke in IBM PS/2 series. In this case, the
  1909.  ;*      Interrupt 15h handler MiscServ takes care of checking the scan codes
  1910.  ;*      and setting the request flag when the hot key is pressed.
  1911.  ;*
  1912.  ;* Time-activated TSRs:
  1913.  ;*      If the TSR is activated by time instead of by a hotkey, KeybrdMonitor
  1914.  ;*      serves as the Interrupt 09 handler for both PC/AT and PS/2 systems.
  1915.  ;*
  1916.  ;* Uses:   intKeybrd, TsrRequestFlag
  1917.  ;*
  1918.  ;* Params: None
  1919.  ;*
  1920.  ;* Return: None
  1921.  
  1922.  Keybrd  PROC    FAR
  1923.  
  1924.          sti                             ; Interrupts are okay
  1925.          push    ax                      ; Save AX register
  1926.          in      al, 60h                 ; AL = scan code of current key
  1927.          call    CheckHotKey             ; Check for hot key
  1928.          .IF     !carry?                 ; If not hot key:
  1929.  
  1930.  ; Hot key pressed. Reset the keyboard to throw away keystroke.
  1931.  
  1932.          cli                             ; Disable interrupts while resetting
  1933.          in      al, 61h                 ; Get current port 61h state
  1934.          or      al, 10000000y           ; Turn on bit 7 to signal clear keybr
  1935.          out     61h, al                 ; Send to port
  1936.          and     al, 01111111y           ; Turn off bit 7 to signal break
  1937.          out     61h, al                 ; Send to port
  1938.          mov     al, 20h                 ; Reset interrupt controller
  1939.          out     20h, al
  1940.          sti                             ; Reenable interrupts
  1941.  
  1942.          pop     ax                      ; Recover AX
  1943.          mov     cs:TsrRequestFlag, TRUE ; Raise request flag
  1944.          iret                            ; Exit interrupt handler
  1945.          .ENDIF                          ; End hot-key check
  1946.  
  1947.  ; No hot key was pressed, so let normal Int 09 service routine take over
  1948.  
  1949.          pop     ax                      ; Recover AX and fall through
  1950.          cli                             ; Interrupts cleared for service
  1951.  
  1952.  KeybrdMonitor LABEL FAR                 ; Installed as Int 09 handler for
  1953.                                          ;   PS/2 or for time-activated TSR
  1954.          mov     cs:intKeybrd.Flag, TRUE ; Signal that interrupt is busy
  1955.          pushf                           ; Simulate interrupt by pushing flags
  1956.          call    cs:intKeybrd.OldHand    ;   far-calling old Int 09 routine
  1957.          mov     cs:intKeybrd.Flag, FALSE
  1958.          iret
  1959.  
  1960.  Keybrd  ENDP
  1961.  
  1962.  
  1963.  ;* Video - Interrupt handler for Interrupt 10h (video). Allows the original
  1964.  ;* video service routine to execute. Maintains an active flag to prevent
  1965.  ;* the TSR from being called while Interrupt 10h is executing.
  1966.  ;*
  1967.  ;* Uses:   intVideo
  1968.  ;*
  1969.  ;* Params: Registers passed to Interrupt 10h
  1970.  ;*
  1971.  ;* Return: Registers returned by Interrupt 10h
  1972.  
  1973.  Video   PROC    FAR
  1974.  
  1975.          mov     cs:intVideo.Flag, TRUE  ; Set active flag
  1976.          pushf                           ; Simulate interrupt by pushing flags
  1977.          call    cs:intVideo.OldHand     ;   far-calling old Int 10h routine
  1978.          mov     cs:intVideo.Flag, FALSE ; Clear active flag
  1979.          iret
  1980.  
  1981.  Video   ENDP
  1982.  
  1983.  
  1984.  ;* DiskIO - Interrupt handler for Interrupt 13h (disk I/O). Allows the
  1985.  ;* original disk I/O service routine to execute. Maintains an active flag
  1986.  ;* to prevent the TSR from being called while Interrupt 13h is executing.
  1987.  ;*
  1988.  ;* Uses:   intDiskIO
  1989.  ;*
  1990.  ;* Params: Registers passed to Interrupt 13h
  1991.  ;*
  1992.  ;* Return: Registers and the carry flag returned by Interrupt 13h
  1993.  
  1994.  DiskIO  PROC    FAR
  1995.  
  1996.          mov     cs:intDiskIO.Flag, TRUE ; Set active flag
  1997.          pushf                           ; Simulate interrupt by pushing flags
  1998.          call    cs:intDiskIO.OldHand    ;   far-calling old Int 13h routine
  1999.          mov     cs:intDiskIO.Flag, FALSE; Clear active flag without
  2000.                                          ;   disturbing flags register
  2001.          sti                             ; Enable interrupts
  2002.          ret     2                       ; Simulate IRET without popping flags
  2003.                                          ;   (since services use carry flag)
  2004.  DiskIO  ENDP
  2005.  
  2006.  
  2007.  ;* MiscServ - Interrupt handler for Interrupt 15h (Miscellaneous System
  2008.  ;* Services).
  2009.  ;*
  2010.  ;* IBM PC/AT and compatibles:
  2011.  ;*     Stub at SkipMiscServ is used as handler, bypassing all calls to
  2012.  ;*     Interrupt 15h. Keypresses are checked by Keybrd (Int 09 handler).
  2013.  ;*
  2014.  ;* IBM PS/2 series:
  2015.  ;*     This procedure handles calls to Interrupt 15h, searching for
  2016.  ;*     Function 4Fh (Keyboard Intercept Service). When AH = 4Fh, gets
  2017.  ;*     scan code of current keystroke in AL register. Then compares the
  2018.  ;*     scan code and shift state to the hot key. If they match, sets
  2019.  ;*     TsrRequestFlag to signal the handlers Clock and Idle that the
  2020.  ;*     TSR is requested.
  2021.  ;*
  2022.  ;* Uses:   intMisc, TsrRequestFlag
  2023.  ;*
  2024.  ;* Params: Registers passed to Interrupt 15h
  2025.  ;*
  2026.  ;* Return: Registers returned by Interrupt 15h
  2027.  
  2028.  MiscServ PROC   FAR
  2029.  
  2030.          sti                             ; Interrupts okay
  2031.          .IF     ah == 4Fh               ; If Keyboard Intercept Service:
  2032.          push    ax                      ; Preserve AX
  2033.          call    CheckHotKey             ; Check for hot key
  2034.          pop     ax
  2035.          .IF     !carry?                 ; If hot key:
  2036.          mov     cs:TsrRequestFlag, TRUE ; Raise request flag
  2037.          clc                             ; Signal BIOS not to process the key
  2038.          ret     2                       ; Simulate IRET without popping flags
  2039.          .ENDIF                          ; End carry flag check
  2040.          .ENDIF                          ; End Keyboard Intercept check
  2041.  
  2042.          cli                             ; Disable interrupts and fall through
  2043.  
  2044.  SkipMiscServ LABEL FAR                  ; Interrupt 15h handler if PC/AT
  2045.  
  2046.          jmp     cs:intMisc.OldHand
  2047.  
  2048.  MiscServ ENDP
  2049.  
  2050.  
  2051.  ;* CtrlBreak - Interrupt trap for Interrupt 1Bh (CTRL+BREAK Handler).
  2052.  ;* Disables CTRL+BREAK processing.
  2053.  ;*
  2054.  ;* Params: None
  2055.  ;*
  2056.  ;* Return: None
  2057.  
  2058.  CtrlBreak PROC  FAR
  2059.  
  2060.          iret
  2061.  
  2062.  CtrlBreak ENDP
  2063.  
  2064.  
  2065.  ;* CtrlC - Interrupt trap for Interrupt 23h (CTRL+C Handler).
  2066.  ;* Disables CTRL+C processing.
  2067.  ;*
  2068.  ;* Params: None
  2069.  ;*
  2070.  ;* Return: None
  2071.  
  2072.  CtrlC   PROC    FAR
  2073.  
  2074.          iret
  2075.  
  2076.  CtrlC   ENDP
  2077.  
  2078.  
  2079.  ;* CritError - Interrupt trap for Interrupt 24h (Critical Error Handler).
  2080.  ;* Disables critical error processing.
  2081.  ;*
  2082.  ;* Params: None
  2083.  ;*
  2084.  ;* Return: AL = Stop code 0 or 3
  2085.  
  2086.  CritError PROC  FAR
  2087.  
  2088.          sti
  2089.          sub     al, al                  ; Assume DOS 2.x
  2090.                                          ; Set AL = 0 for ignore error
  2091.          .IF     cs:major != 2           ; If DOS 3.x, set AL = 3
  2092.          mov     al, 3                   ;  DOS call fails
  2093.          .ENDIF
  2094.  
  2095.          iret
  2096.  
  2097.  CritError ENDP
  2098.  
  2099.  
  2100.  ;* Idle - Interrupt handler for Interrupt 28h (DOS Idle). Allows the
  2101.  ;* original Interrupt 28h service routine to execute. Then checks the
  2102.  ;* request flag TsrRequestFlag maintained either by the keyboard handler
  2103.  ;* (keyboard-activated TSR) or by the timer handler (time-activated TSR).
  2104.  ;* See header comments above for Clock, Keybrd, and MiscServ procedures.
  2105.  ;*
  2106.  ;* If TsrRequestFlag = TRUE and system is in interruptable state, Idle
  2107.  ;* invokes the TSR by calling the Activate procedure. Uses an active flag
  2108.  ;* to prevent the Idle procedure from being reentered while executing.
  2109.  ;*
  2110.  ;* Uses:   intIdle and TsrActiveFlag
  2111.  ;*
  2112.  ;* Params: None
  2113.  ;*
  2114.  ;* Return: None
  2115.  
  2116.  Idle    PROC    FAR
  2117.  
  2118.          pushf                           ; Simulate interrupt by pushing flags
  2119.          call    cs:intIdle.OldHand      ;   far-calling old Int 28h routine
  2120.  
  2121.          .IF     cs:intIdle.Flag == FALSE; If not already in this handler:
  2122.          mov     cs:intIdle.Flag, TRUE   ; Set active flag
  2123.  
  2124.          sti                             ; Interrupts are okay
  2125.          push    ds                      ; Save application's DS
  2126.          push    cs
  2127.          pop     ds                      ; Set DS to resident code segment
  2128.          ASSUME  ds:@code
  2129.  
  2130.          INVOKE  CheckRequest            ; Check conditions
  2131.          .IF     !carry?                 ; If TSR requested and safe:
  2132.          mov     TsrActiveFlag, TRUE     ; Activate TSR
  2133.          INVOKE  Activate
  2134.          mov     TsrActiveFlag, FALSE
  2135.          .ENDIF                          ; End carry flag check
  2136.  
  2137.          mov     intIdle.Flag, FALSE     ; Clear active flag
  2138.          pop     ds                      ; Recover application's DS
  2139.          .ENDIF                          ; End in-handler check
  2140.  
  2141.          iret
  2142.  
  2143.  Idle    ENDP
  2144.  
  2145.  
  2146.  ;* Multiplex - Handler for Interrupt 2Fh (Multiplex Interrupt). Checks
  2147.  ;* AH for this TSR's identity number. If no match (indicating call is
  2148.  ;* not intended for this TSR), Multiplex passes control to the previous
  2149.  ;* Interrupt 2Fh handler.
  2150.  ;*
  2151.  ;* Params: AH = Handler identity number
  2152.  ;*         AL = Function number 0-2
  2153.  ;*
  2154.  ;* Return: AL    = 0FFh (function 0)
  2155.  ;*         ES:DI = Pointer to identifier string (function 0)
  2156.  ;*         ES:DI = Pointer to resident PSP segment (function 1)
  2157.  ;*         ES:DI = Pointer to shared memory (function 2)
  2158.  
  2159.  Multiplex PROC  FAR
  2160.  
  2161.          .IF     ah != cs:IDnumber       ; If this handler not reqested:
  2162.          jmp     cs:intMultex.OldHand    ; Pass control to old Int 2Fh handler
  2163.          .ENDIF
  2164.  
  2165.          .IF     al == 0                 ; If function 0 (verify presence):
  2166.          mov     al, 0FFh                ; AL = 0FFh,
  2167.          push    cs                      ; ES = resident code segment
  2168.          pop     es
  2169.          mov     di, OFFSET IDstring     ; DI = offset of identifier string
  2170.  
  2171.          .ELSEIF al == 1                 ; If function 1 (get PSP address):
  2172.          mov     es, cs:TsrPspSeg        ; ES:DI = far address of resident PSP
  2173.          sub     di, di
  2174.  
  2175.          .ELSE
  2176.          les     di, cs:ShareAddr        ; If function 2 (get shared memory):
  2177.          .ENDIF                          ;    set ES:DI = far address
  2178.  
  2179.  NoMultiplex LABEL  FAR                  ; Secondary entry for null Multiplex
  2180.  
  2181.          iret
  2182.  
  2183.  Multiplex ENDP
  2184.  
  2185.  
  2186.  ;* CheckHotKey - Checks current keystroke for hot key. Called from Keybrd
  2187.  ;* handler if IBM PC/AT or compatible, or from MiscServ handler if PS/2.
  2188.  ;*
  2189.  ;* Uses:   HotScan, HotShift, HotMask, and SHFT_STAT
  2190.  ;*
  2191.  ;* Params: AL = Scan code
  2192.  ;*
  2193.  ;* Return: Carry flag set = FALSE; carry flag clear = TRUE
  2194.  
  2195.  CheckHotKey PROC NEAR
  2196.  
  2197.          cmp     al, cs:HotScan          ; If current scan code isn't code
  2198.          jne     e_exit                  ;   for hot key, exit with carry set
  2199.  
  2200.          push    es                      ; Else look into BIOS data area
  2201.          sub     ax, ax                  ;   (segment 0) to check shift state
  2202.          mov     es, ax
  2203.          mov     al, es:[SHFT_STAT]      ; Get shift-key flags
  2204.          and     al, cs:HotMask          ; AND with "don't care" mask
  2205.          cmp     al, cs:HotShift         ; Compare result with hot shift key
  2206.          pop     es
  2207.          je      exit                    ; If match, exit with carry clear
  2208.  
  2209.  e_exit: stc                             ; Set carry if not hot key
  2210.  exit:   ret
  2211.  
  2212.  CheckHotKey ENDP
  2213.  
  2214.  
  2215.  ;* CheckRequest - Checks request flag and system status using the
  2216.  ;* following logic:
  2217.  ;*
  2218.  ;*         IF (TsrRequestFlag AND (NOT TsrActiveFlag)
  2219.  ;*             AND DosStatus AND HardwareStatus)
  2220.  ;*             return TRUE
  2221.  ;*         ELSE
  2222.  ;*             return FALSE
  2223.  ;*
  2224.  ;* Uses:   TsrRequestFlag and TsrActiveFlag
  2225.  ;*
  2226.  ;* Params: DS = Resident code segment
  2227.  ;*
  2228.  ;* Return: Carry flag set = TRUE; carry flag clear = FALSE
  2229.  
  2230.  CheckRequest PROC NEAR
  2231.  
  2232.          rol     TsrRequestFlag, 1       ; Rotate high bit into carry - set
  2233.                                          ;   if TRUE (-1), clear if FALSE (0)
  2234.          cmc                             ; NOT carry
  2235.  
  2236.          .IF     !carry?                 ; If TsrRequestFlag = TRUE:
  2237.          ror     TsrActiveFlag, 1        ; Rotate low bit into carry - set
  2238.                                          ;   if TRUE (-1), clear if FALSE (0)
  2239.          .IF     !carry?                 ; If TsrActiveFlag = FALSE:
  2240.          INVOKE  CheckDos                ; Is DOS in interruptable state?
  2241.  
  2242.          .IF     !carry?                 ; If so:
  2243.          INVOKE  CheckHardware           ; If hardware or BIOS unstable,
  2244.          .ENDIF                          ;  set carry and exit
  2245.          .ENDIF
  2246.          .ENDIF
  2247.          ret
  2248.  
  2249.  CheckRequest ENDP
  2250.  
  2251.  
  2252.  ;* CheckDos - Checks status of MS-DOS using the following logic:
  2253.  ;*
  2254.  ;*         IF (NOT CritErr) AND ((NOT InDos) OR (Idle AND InDos))
  2255.  ;*             return DosStatus = TRUE
  2256.  ;*         ELSE
  2257.  ;*             return DosStatus = FALSE
  2258.  ;*
  2259.  ;* Uses:   CritErrAddr, InDosAddr, and intIdle
  2260.  ;*
  2261.  ;* Params: DS = Resident code segment
  2262.  ;*
  2263.  ;* Return: Carry flag set if MS-DOS is busy
  2264.  
  2265.  CheckDos PROC   NEAR USES es bx ax
  2266.  
  2267.          les     bx, CritErrAddr
  2268.          mov     ah, es:[bx]             ; AH = value of CritErr flag
  2269.  
  2270.          les     bx, InDosAddr
  2271.          mov     al, es:[bx]             ; AL = value of InDos flag
  2272.  
  2273.          sub     bx, bx                  ; BH = 0, BL = 0
  2274.          cmp     bl, intIdle.Flag        ; Carry flag set if call is from
  2275.                                          ;   Interrupt 28h handler
  2276.          rcl     bl, 1                   ; Rotate carry into BL: TRUE if Idle
  2277.          cmp     bx, ax                  ; Carry flag clear if CritErr = 0
  2278.                                          ;   and InDos <= BL
  2279.          ret
  2280.  
  2281.  CheckDos ENDP
  2282.  
  2283.  
  2284.  ;* CheckHardware - Checks status of BIOS and hardware using the
  2285.  ;* following logic:
  2286.  ;*
  2287.  ;*         IF HardwareActive OR KeybrdActive OR VideoActive OR DiskIOActive
  2288.  ;*             return HardwareStatus = FALSE
  2289.  ;*         ELSE
  2290.  ;*             return HardwareStatus = TRUE
  2291.  ;*
  2292.  ;* Uses:   intKeybrd, intVideo, and intDiskIO
  2293.  ;*
  2294.  ;* Params: DS = Resident code segment
  2295.  ;*
  2296.  ;* Return: Carry flag set if hardware or BIOS is busy
  2297.  
  2298.  CheckHardware PROC NEAR USES ax
  2299.  
  2300.  ; Verify hardware interrupt status by interrogating Intel 8259A
  2301.  ; Programmable Interrupt Controller
  2302.  
  2303.          mov     ax, 00001011y           ; AL = 0CW3 for Intel 8259A
  2304.                                          ;   (RR = 1, RIS = 1)
  2305.          out     20h, al                 ; Request 8259A in-service register
  2306.          jmp     delay                   ; Wait a few cycles
  2307.  delay:
  2308.          in      al, 20h                 ; AL = hardware interrupts being
  2309.          cmp     ah, al                  ;   serviced (bit = 1 if in service)
  2310.  
  2311.          .IF     !carry?                 ; If no hard interrupts in service:
  2312.          sub     al, al                  ; Verify BIOS interrupts not active
  2313.          cmp     al, intKeybrd.Flag      ; Check Interrupt 09 handler
  2314.  
  2315.          .IF     !carry?                 ; If Int 09 not active:
  2316.          cmp     al, intVideo.Flag       ; Check Interrupt 10h handler
  2317.  
  2318.          .IF     !carry?                 ; If Int 10h not active:
  2319.          cmp     al, intDiskIO.Flag      ; Check Interrupt 13h handler
  2320.          .ENDIF                          ; Return with carry set if
  2321.          .ENDIF                          ;   Interrupt 09, 10h, or 13h
  2322.          .ENDIF                          ;   is active
  2323.  
  2324.          ret
  2325.  
  2326.  CheckHardware ENDP
  2327.  
  2328.  
  2329.  ;* Activate - Sets up for far call to TSR with the following steps:
  2330.  ;*
  2331.  ;*   1.  Stores stack pointer SS:SP and switches to new stack
  2332.  ;*   2.  Pushes registers onto new stack
  2333.  ;*   3.  Stores vectors for Interrupts 1Bh, 23h, and 23h, and
  2334.  ;*       replaces them with addresses of error-trapping handlers
  2335.  ;*   4.  Stores DOS Ctrl+C checking flag, then turns off checking
  2336.  ;*   5.  If required, stores DTA address and switches to new DTA
  2337.  ;*
  2338.  ;* When TSR returns, restores all the above.
  2339.  ;*
  2340.  ;* Uses:   Reads or writes the following globals:
  2341.  ;*         OldStackAddr, TrapArray, BreakCheckFlag, TsrRequestFlag
  2342.  ;*
  2343.  ;* Params: DS = Resident code segment
  2344.  ;*
  2345.  ;* Return: None
  2346.  
  2347.  Activate PROC   NEAR
  2348.  
  2349.  ; Step 1.  Set up a new stack
  2350.  
  2351.          mov     WORD PTR OldStackAddr[0], sp    ; Save current
  2352.          mov     WORD PTR OldStackAddr[2], ss    ;   stack pointer
  2353.  
  2354.          cli                                     ; Turn off interrupts while
  2355.          push    cs                              ;   changing stack
  2356.          pop     ss                              ; New stack begins
  2357.          mov     sp, OFFSET NewStack             ;   at LABEL NewStack
  2358.          sti
  2359.  
  2360.  ; Step 2.  Preserve registers (DS already saved in Clock or Idle)
  2361.  
  2362.          push    ax
  2363.          push    bx
  2364.          push    cx
  2365.          push    dx
  2366.          push    si
  2367.          push    di
  2368.          push    bp
  2369.          push    es
  2370.  
  2371.          cld                                     ; Clear direction flag
  2372.  
  2373.  ; Step 3.  Set up trapping handlers for keyboard breaks and DOS
  2374.  ; critical errors (Interrupts 1Bh, 23h, and 24h)
  2375.  
  2376.          mov     cx, CTRAP                       ; CX = number of handlers
  2377.          mov     si, OFFSET TrapArray            ; DS:SI points to trap array
  2378.  
  2379.          .REPEAT
  2380.          mov     al, [si]                        ; AL = interrupt number
  2381.          mov     ah, 35h                         ; Request DOS Function 35h
  2382.          int     21h                             ; Get Interrupt Vector (ES:BX
  2383.          mov     WORD PTR [si].INTR.OldHand[0], bx ; Save far address of
  2384.          mov     WORD PTR [si].INTR.OldHand[2], es ;   application's handler
  2385.          mov     dx, WORD PTR [si].INTR.NewHand[0] ; DS:DX points to TSR's han
  2386.          mov     ah, 25h                         ; Request DOS Function 25h
  2387.          int     21h                             ; Set Interrupt Vector
  2388.          add     si, SIZEOF INTR                 ; DS:SI points to next in lis
  2389.          .UNTILCXZ
  2390.  
  2391.  ; Step 4.  Disable MS-DOS break checking during disk I/O
  2392.  
  2393.          mov     ax, 3300h               ; Request DOS Function 33h
  2394.          int     21h                     ; Get Ctrl-Break Flag in DL
  2395.          mov     BreakCheckFlag, dl      ; Preserve it
  2396.  
  2397.          sub     dl, dl                  ; DL = 0 to disable I/O break checkin
  2398.          mov     ax, 3301h               ; Request DOS Function 33h
  2399.          int     21h                     ; Set Ctrl-Break Flag from DL
  2400.  
  2401.  ; Step 5.  If TSR requires a disk transfer area, store address of current
  2402.  ; DTA and switch buffer address to this segment. See Section 17.5.3 of
  2403.  ; Programmer's Guide for more information about the DTA.
  2404.  
  2405.          IFDEF   DTA_SIZ
  2406.          mov     ah, 2Fh                         ; Request DOS Function 2Fh
  2407.          int     21h                             ; Get DTA Address into ES:BX
  2408.          mov     WORD PTR OldDtaAddr[0], bx      ; Store address
  2409.          mov     WORD PTR OldDtaAddr[2], es
  2410.  
  2411.          mov     dx, OFFSET DtaBuff              ; DS:DX points to new DTA
  2412.          mov     ah, 1Ah                         ; Request DOS Function 1Ah
  2413.          int     21h                             ; Set DTA Address
  2414.          ENDIF
  2415.  
  2416.  ; Call main body of TSR.
  2417.  
  2418.          mov     ax, @data
  2419.          mov     ds, ax                          ; Initialize DS and ES
  2420.          mov     es, ax                          ;   to data segment
  2421.  
  2422.          call    cs:TsrAddr                      ; Call main part of TSR
  2423.  
  2424.          push    cs
  2425.          pop     ds                              ; Reset DS to this segment
  2426.  
  2427.  ; Undo step 5.  Restore previous DTA (if required)
  2428.  
  2429.          IFDEF   DTA_SIZ
  2430.          push    ds                      ; Preserve DS
  2431.          lds     dx, OldDtaAddr          ; DS:DX points to application's DTA
  2432.          mov     ah, 1Ah                 ; Request DOS Function 1Ah
  2433.          int     21h                     ; Set DTA Address
  2434.          pop     ds
  2435.          ENDIF
  2436.  
  2437.  ; Undo step 4.  Restore previous MS-DOS break checking
  2438.  
  2439.          mov     dl, BreakCheckFlag      ; DL = previous break state
  2440.          mov     ax, 3301h               ; Request DOS Function 33h
  2441.          int     21h                     ; Set Ctrl-Break Flag from DL
  2442.  
  2443.  ; Undo step 3.  Restore previous vectors for error-trapping handlers
  2444.  
  2445.          mov     cx, CTRAP
  2446.          mov     di, OFFSET TrapArray
  2447.          push    ds                      ; Preserve DS
  2448.          push    ds                      ; ES = resident code segment
  2449.          pop     es
  2450.  
  2451.          .REPEAT
  2452.          mov     al, es:[di]             ; AL = interrupt number
  2453.          lds     dx, es:[di].INTR.OldHand; DS:DX points to application's handl
  2454.          mov     ah, 25h                 ; Request DOS Function 25h
  2455.          int     21h                     ; Set Interrupt Vector from DS:DX
  2456.          add     di, SIZEOF INTR         ; ES:DI points to next in list
  2457.          .UNTILCXZ
  2458.          pop     ds
  2459.  
  2460.  ; Undo step 2.  Restore registers from stack
  2461.  
  2462.          pop     es
  2463.          pop     bp
  2464.          pop     di
  2465.          pop     si
  2466.          pop     dx
  2467.          pop     cx
  2468.          pop     bx
  2469.          pop     ax
  2470.  
  2471.  ; Undo step 1.  Restore address of original stack to SS:SP
  2472.  
  2473.          cli
  2474.          mov     sp, WORD PTR OldStackAddr[0]
  2475.          mov     ss, WORD PTR OldStackAddr[2]
  2476.          sti
  2477.  
  2478.  ; Clear request flag and return to caller (Clock or Idle procedure)
  2479.  
  2480.          mov     TsrRequestFlag, FALSE
  2481.          ret
  2482.  
  2483.  Activate ENDP
  2484.  
  2485.  
  2486.  
  2487.  ;* INSTALLATION SECTION - The following code is executed only during
  2488.  ;* the TSR's installation phase. When the program terminates through
  2489.  ;* Function 31h, the above code and data remain resident; memory
  2490.  ;* occupied by the following code segment is returned to the operating
  2491.  ;* system.
  2492.  
  2493.  DGROUP  GROUP INSTALLCODE
  2494.  
  2495.  INSTALLCODE SEGMENT PARA PUBLIC 'CODE2'
  2496.          ASSUME  ds:@code
  2497.  
  2498.  ;* Install - Prepares for installation of a TSR by chaining interrupt
  2499.  ;* handlers and initializing pointers to DOS flags. Install does not
  2500.  ;* call the Terminate-and-Stay-Resident function.
  2501.  ;*
  2502.  ;* This library of routines accomodates both keyboard-activated and
  2503.  ;* time-activated TSRs. The latter are TSRs that activate at a preset
  2504.  ;* time. If the first parameter (Param1) is a valid scan code, Install
  2505.  ;* assumes the TSR is activated from the keyboard and sets up a keyboard
  2506.  ;* handler to search for the hotkey. If Param1 is null, Install assumes
  2507.  ;* the next two parameters (Param2 and Param3) are respectively the hour
  2508.  ;* and minute at which the TSR is to activate. In this case, Install
  2509.  ;* calls GetTimeToElapse to initialize the variable CountDown and sets
  2510.  ;* up KeybrdMonitor as the keyboard handler. CountDown and the secondary
  2511.  ;* counter Tick91 serve here the same functions as they do for the
  2512.  ;* ALARM.ASM program presented in Section 17.3 of the Programmer's Guide.
  2513.  ;* Install is callable from a high-level language.
  2514.  ;*
  2515.  ;* Uses:   InDosAddr, CritErrAddr, CHAND,
  2516.  ;*         HandArray, CTRAP, TrapArray
  2517.  ;*
  2518.  ;*                  Keyboard-activated                 Time-activated
  2519.  ;*                  ------------------                 --------------
  2520.  ;* Params: Param1 - Scan code for hotkey               0
  2521.  ;*         Param2 - Bit value for shift hotkey         Hour to activate
  2522.  ;*         Param3 - Bit mask for shift hotkey          Minute to activate
  2523.  ;*         Param4 - Far address of main TSR procedure  (same)
  2524.  ;*
  2525.  ;* Return: AX = 0 if successful, or one of the following codes:
  2526.  ;*         IS_INSTALLED           FLAGS_NOT_FOUND        NO_IDNUM
  2527.  ;*         ALREADY_INSTALLED      WRONG_DOS
  2528.  
  2529.  Install PROC    FAR USES ds si di,
  2530.          Param1:WORD, Param2:WORD, Param3:WORD, Param4:FAR PTR FAR
  2531.  
  2532.          mov     ax, @code
  2533.          mov     ds, ax                          ; Point DS to code segment
  2534.  
  2535.  ; Get and store parameters passed from main program module
  2536.  
  2537.          mov     al, BYTE PTR Param1
  2538.          mov     HotScan, al                     ; Store hot key scan code
  2539.          mov     al, BYTE PTR Param2             ;   or flag for time-activate
  2540.          mov     HotShift, al                    ; Store hot key shift value
  2541.          mov     al, BYTE PTR Param3             ;   or hour value
  2542.          mov     HotMask, al                     ; Store hot key shift mask
  2543.                                                  ;   or minute value
  2544.          mov     ax, WORD PTR Param4[0]
  2545.          mov     bx, WORD PTR Param4[2]
  2546.          mov     WORD PTR TsrAddr[0], ax         ; Store segment:offset of
  2547.          mov     WORD PTR TsrAddr[2], bx         ;   TSR's main code
  2548.  
  2549.  ; Get addresses of DOS flags, then check for prior installation
  2550.  
  2551.          INVOKE  GetDosFlags             ; Find DOS service flags
  2552.          or      ax, ax
  2553.          jnz     exit                    ; If flags not found, quit
  2554.  
  2555.          sub     al, al                  ; Request multiplex function 0
  2556.          call    CallMultiplex           ; Invoke Interrupt 2Fh
  2557.          cmp     ax, NOT_INSTALLED       ; Check for presence of resident TSR
  2558.  
  2559.          .IF     !zero?                  ; If TSR is installed:
  2560.          cmp     ax, IS_INSTALLED        ; Return with appropriate
  2561.          jne     exit                    ;   error code
  2562.          mov     ax, ALREADY_INSTALLED
  2563.          jmp     exit
  2564.          .ENDIF
  2565.  
  2566.  ; Check if TSR is to activate at the hour:minute specified by Param2:Param3.
  2567.  ; If so, determine the number of 5-second intervals that must elapse before
  2568.  ; activation, then set up the code at the far LABEL KeybrdMonitor to serve
  2569.  ; as the keyboard handler.
  2570.  
  2571.          .IF     HotScan == 0            ; If valid scan code given:
  2572.          mov     ah, HotShift            ; AH = hour to activate
  2573.          mov     al, HotMask             ; AL = minute to activate
  2574.          call    GetTimeToElapse         ; Get number of 5-second intervals
  2575.          mov     CountDown, ax           ;   to elapse before activation
  2576.  
  2577.          .ELSE                           ; Force use of KeybrdMonitor as
  2578.                                          ;   keyboard handler
  2579.          cmp     Version, 031Eh          ; DOS Version 3.3 or higher?
  2580.          jb      setup                   ; No?  Skip next step
  2581.  
  2582.  ; Test for IBM PS/2 series. If not PS/2, use Keybrd and SkipMiscServ as
  2583.  ; handlers for Interrupts 09 and 15h respectively. If PS/2 system, set up
  2584.  ; KeybrdMonitor as the Interrupt 09 handler. Audit keystrokes with MiscServ
  2585.  ; handler, which searches for the hot key by handling calls to Interrupt 15h
  2586.  ; (Miscellaneous System Services). Refer to Section 17.2.1 of the Programmer'
  2587.  ; Guide for more information about keyboard handlers.
  2588.  
  2589.          mov     ax, 0C00h               ; Function 0Ch (Get System
  2590.          int     15h                     ;   Configuration Parameters)
  2591.          sti                             ; Compaq ROM may leave disabled
  2592.  
  2593.          jc      setup                   ; If carry set,
  2594.          or      ah, ah                  ;   or if AH not 0,
  2595.          jnz     setup                   ;   services are not supported
  2596.  
  2597.          test    BYTE PTR es:[bx+5], 00010000y   ; Test byte 4 to see if
  2598.          jz      setup                           ;   intercept is implemented
  2599.  
  2600.          mov     ax, OFFSET MiscServ             ; If so, set up MiscServ as
  2601.          mov     WORD PTR intMisc.NewHand, ax    ;   Interrupt 15h handler
  2602.          .ENDIF
  2603.  
  2604.          mov     ax, OFFSET KeybrdMonitor        ; Set up KeybrdMonitor as
  2605.          mov     WORD PTR intKeybrd.NewHand, ax  ;   Interrupt 09 handler
  2606.  
  2607.  ; Interrupt structure is now initialized for either PC/AT or PS/2 system.
  2608.  ; Get existing handler addresses from interrupt vector table, store in
  2609.  ; OldHand member, and replace with addresses of new handlers.
  2610.  
  2611.  setup:
  2612.          mov     cx, CHAND               ; CX = count of handlers
  2613.          mov     si, OFFSET HandArray    ; SI = offset of handler structures
  2614.  
  2615.          .REPEAT
  2616.          mov     ah, 35h                 ; Request DOS Function 35h
  2617.          mov     al, [si]                ; AL = interrupt number
  2618.          int     21h                     ; Get Interrupt Vector in ES:BX
  2619.          mov     WORD PTR [si].INTR.OldHand[0], bx ; Save far address
  2620.          mov     WORD PTR [si].INTR.OldHand[2], es ;  of current handler
  2621.          mov     dx, WORD PTR [si].INTR.NewHand[0] ; DS:DX points to TSR handl
  2622.          mov     ah, 25h                 ; Request DOS Function 25h
  2623.          int     21h                     ; Set Interrupt Vector from DS:DX
  2624.          add     si, SIZEOF INTR         ; DS:SI points to next in list
  2625.          .UNTILCXZ
  2626.  
  2627.          sub     ax, ax                  ; Clear return code
  2628.  exit:
  2629.          ret                             ; Return to caller
  2630.  
  2631.  Install ENDP
  2632.  
  2633.  
  2634.  ;* Deinstall - Prepares for deinstallation of a TSR. Deinstall is the
  2635.  ;* complement of the Install procedure. It restores to the vector table
  2636.  ;* the original addresses replaced during installation, thus unhooking
  2637.  ;* the TSR's handlers. Checks to see if another TSR has installed handlers
  2638.  ;* for the interrupts in array HandArray. If so, the procedure fails with
  2639.  ;* an appropriate error code. Callable from a high-level language.
  2640.  ;*
  2641.  ;* Params: None
  2642.  ;*
  2643.  ;* Return: AX = Segment address of resident portion's PSP or
  2644.  ;*              one of the following error codes:
  2645.  ;*              CANT_DEINSTALL           WRONG_DOS
  2646.  
  2647.  Deinstall PROC  FAR USES ds si di
  2648.  
  2649.          mov     ax, @code
  2650.          mov     ds, ax                  ; Point DS to code segment
  2651.  
  2652.          sub     al, al                  ; Request multiplex function 0
  2653.          call    CallMultiplex           ; Get resident code segment in ES
  2654.  
  2655.          cmp     ax, IS_INSTALLED        ; If not resident,
  2656.          jne     exit                    ;   exit with error
  2657.          push    es                      ; Else point DS to
  2658.          pop     ds                      ;   resident code segment
  2659.          mov     cx, CHAND               ; Count of handlers
  2660.          mov     si, OFFSET HandArray    ; SI points to handler structures
  2661.  
  2662.  ; Read current vectors for TSR's interrupt handlers and compare with far
  2663.  ; addresses. If mismatch, another TSR has installed new handlers and ours
  2664.  ; cannot be safely deinstalled.
  2665.  
  2666.          .REPEAT
  2667.          mov     al, [si]                ; AL = interrupt number
  2668.          mov     ah, 35h                 ; Request DOS Function 35h
  2669.          int     21h                     ; Get Interrupt Vector in ES:BX
  2670.          cmp     bx, WORD PTR [si].INTR.NewHand[0] ; If offset different,
  2671.          jne     e_exit                            ;   error
  2672.          mov     ax, es
  2673.          cmp     ax, WORD PTR [si].INTR.NewHand[2] ; If segment different,
  2674.          jne     e_exit                            ;   error
  2675.          add     si, SIZEOF INTR         ; DS:SI points to next in list
  2676.          .UNTILCXZ
  2677.  
  2678.  ; If no interrupts replaced, call TSR's multiplex handler to locate
  2679.  ; address of resident portion's PSP. Although the PSP is not required
  2680.  ; until memory is returned to DOS, the call must be done now before
  2681.  ; unhooking the multiplex handler.
  2682.  
  2683.          mov     al, 1                   ; Request multiplex function 1
  2684.          call    CallMultiplex           ; Get resident code's PSP in ES
  2685.          push    es                      ; Save it
  2686.  
  2687.  ; Unhook all handlers by restoring the original vectors to vector table.
  2688.  
  2689.          mov     cx, CHAND               ; Count of installed handlers
  2690.          mov     si, OFFSET HandArray    ; SI points to handler structures
  2691.  
  2692.          .REPEAT
  2693.          mov     al, [si]                ; AL = interrupt number
  2694.          push    ds                      ; Preserve DS segment
  2695.          lds     dx, [si].INTR.OldHand   ; Put vector in DS:DX
  2696.          mov     ah, 25h                 ; Request DOS Function 25h
  2697.          int     21h                     ; Set Interrupt Vector from DS:DX
  2698.          pop     ds
  2699.          add     si, SIZEOF INTR         ; DS:SI points to next in list
  2700.          .UNTILCXZ
  2701.  
  2702.          pop     ax                      ; Return address of resident PSP
  2703.          jmp     exit                    ;  to signal success
  2704.  e_exit:
  2705.          mov     ax, CANT_DEINSTALL
  2706.  exit:
  2707.          ret
  2708.  
  2709.  Deinstall ENDP
  2710.  
  2711.  
  2712.  ;* GetVersion - Gets the DOS version and stores it in a global variable as
  2713.  ;* well as returning it in AX.
  2714.  ;*
  2715.  ;* Uses:   Version
  2716.  ;*
  2717.  ;* Params: DS = Resident code segment
  2718.  ;*
  2719.  ;* Return: AH = Major version
  2720.  ;*         AL = Minor version
  2721.  
  2722.  GetVersion PROC NEAR
  2723.  
  2724.          mov     ax, 3000h               ; Request DOS Function 30h
  2725.          int     21h                     ; Get MS-DOS Version Number
  2726.          .IF     al < 2                  ; If Version 1.x:
  2727.          mov     ax, WRONG_DOS           ; Abort with WRONG_DOS as error code
  2728.          .ELSE
  2729.          xchg    ah, al                  ; AH = major, AL = minor version
  2730.          mov     Version, ax             ; Save in global
  2731.          .ENDIF
  2732.          ret
  2733.  
  2734.  GetVersion ENDP
  2735.  
  2736.  
  2737.  ;* GetDosFlags - Gets pointers to DOS's InDos and Critical Error flags.
  2738.  ;*
  2739.  ;* Params: DS = Resident code segment
  2740.  ;*
  2741.  ;* Return: 0 if successful, or the following error code:
  2742.  ;*         FLAGS_NOT_FOUND
  2743.  
  2744.  GetDosFlags PROC NEAR
  2745.  
  2746.  ; Get InDOS address from MS-DOS
  2747.  
  2748.          mov     ah, 34h                         ; Request DOS Function 34h
  2749.          int     21h                             ; Get Address of InDos Flag
  2750.          mov     WORD PTR InDosAddr[0], bx       ; Store address (ES:BX)
  2751.          mov     WORD PTR InDosAddr[2], es       ;   for later access
  2752.  
  2753.  ; Determine address of Critical Error Flag
  2754.  
  2755.          mov     ax, Version             ; AX = DOS version number
  2756.  
  2757.  ; If DOS 3.1 or greater and not OS/2 compatibility mode, Critical Error
  2758.  ; flag is in byte preceding InDOS flag
  2759.          .IF     (ah < 10) && (ah >= 3) && (al >= 10)
  2760.          dec     bx                      ; BX points to byte before InDos flag
  2761.  
  2762.          .ELSE
  2763.  ; For earlier versions, the only reliable method is to scan through
  2764.  ; DOS to find an INT 28h instruction in a specific context.
  2765.  
  2766.          mov     cx, 0FFFFh              ; Maximum bytes to scan
  2767.          sub     di, di                  ; ES:DI = start of DOS segment
  2768.  
  2769.  INT_28  EQU     028CDh
  2770.  
  2771.          .REPEAT
  2772.          mov     ax, INT_28              ; Load opcode for INT 28h
  2773.  
  2774.          .REPEAT
  2775.          repne   scasb                   ; Scan for first byte of opcode
  2776.  
  2777.          .IF     !zero?
  2778.          mov     ax, FLAGS_NOT_FOUND     ; Return error if not found
  2779.          jmp     exit
  2780.          .ENDIF
  2781.          .UNTIL  ah == es:[di]           ; For each matching first byte,
  2782.                                          ;  check the second byte until match
  2783.  
  2784.  ; See if INT 28h is in this context:
  2785.  ;                                       ;     (-7)    (-5)
  2786.  ;       CMP     ss:[CritErrFlag], 0     ;  36, 80, 3E,  ?,  ?,  0
  2787.  ;       JNE     NearLabel               ;  75,  ?
  2788.          int     28h                     ;  CD, 28
  2789.  ;                                       ;  (0) (1)
  2790.  CMP_SS    EQU   3E80h
  2791.  P_CMP_SS  EQU   8
  2792.  P_CMP_OP  EQU   6
  2793.  
  2794.          mov     ax, CMP_SS              ; Load and compare opcode to CMP
  2795.          .IF     ax == es:[di-P_CMP_SS]  ; If match:
  2796.          mov     bx, es:[di-P_CMP_OP]    ; BX = offset of
  2797.          jmp     exit                    ;   Critical Error Flag
  2798.          .ENDIF
  2799.  
  2800.  ; See if INT 28h is in this context:
  2801.  ;                                       ;     (-12)   (-10)
  2802.  ;       TEST    ?s:[CritErr], 0FFh      ;  ?6  F6, 06,  ?,  ?, FF
  2803.  ;       JNE     NearLabel               ;  75, ?
  2804.  ;       PUSH    ss:[CritErrFlag]        ;  36, FF, 36,  ?,  ?
  2805.          int     28h                     ;  CD, 28
  2806.  ;                                       ;  (0) (1)
  2807.  TEST_SS   EQU   06F6h
  2808.  P_TEST_SS EQU   13
  2809.  P_TEST_OP EQU   11
  2810.  
  2811.          mov     ax, TEST_SS             ; Load AX = opcode for TEST
  2812.          .UNTIL  ax == es:[di-P_TEST_SS] ; If not TEST, continue scan
  2813.  
  2814.          mov     bx, es:[di-P_TEST_OP]   ; Else load BX with offset of
  2815.          .ENDIF                          ;   Critical Error flag
  2816.  exit:
  2817.          mov     WORD PTR CritErrAddr[0], bx     ; Store address of
  2818.          mov     WORD PTR CritErrAddr[2], es     ;   Critical Error Flag
  2819.          sub     ax, ax                          ; Clear error code
  2820.          ret
  2821.  
  2822.  GetDosFlags ENDP
  2823.  
  2824.  
  2825.  ;* GetTimeToElapse - Determines number of 5-second intervals that
  2826.  ;* must elapse between specified hour:minute and current time.
  2827.  ;*
  2828.  ;* Params: AH = Hour
  2829.  ;*         AL = Minute
  2830.  ;*
  2831.  ;* Return: AX = Number of 5-second intervals
  2832.  
  2833.  GetTimeToElapse PROC NEAR
  2834.  
  2835.          push    ax                      ; Save hour:minute
  2836.          mov     ah, 2Ch                 ; Request DOS Function 2Ch
  2837.          int     21h                     ; Get Time (CH:CL = hour:minute)
  2838.          pop     bx                      ; Recover hour:minute
  2839.          mov     dl, dh
  2840.          sub     dh, dh
  2841.          push    dx                      ; Save DX = current seconds
  2842.  
  2843.          mov     al, 60                  ; 60 minutes/hour
  2844.          mul     bh                      ; Mutiply by specified hour
  2845.          sub     bh, bh
  2846.          add     bx, ax                  ; BX = minutes from midnight
  2847.                                          ;   to activation time
  2848.          mov     al, 60                  ; 60 minutes/hour
  2849.          mul     ch                      ; Multiply by current hour
  2850.          sub     ch, ch
  2851.          add     ax, cx                  ; AX = minutes from midnight
  2852.                                          ;   to current time
  2853.          sub     bx, ax                  ; BX = minutes to elapse before
  2854.          .IF     carry?                  ; If activation is tomorrow:
  2855.          add     bx, 24 * 60             ;  add number of minutes per day
  2856.          .ENDIF
  2857.  
  2858.          mov     ax, 60
  2859.          mul     bx                      ; DX:AX = minutes-to-elapse-times-60
  2860.          pop     bx                      ; Recover current seconds
  2861.          sub     ax, bx                  ; DX:AX = seconds to elapse before
  2862.          sbb     dx, 0                   ;   activation
  2863.          .IF     carry?                  ; If negative:
  2864.          mov     ax, 5                   ; Assume 5 seconds
  2865.          cwd
  2866.          .ENDIF
  2867.  
  2868.          mov     bx, 5                   ; Divide result by 5 seconds
  2869.          div     bx                      ; AX = number of 5-second intervals
  2870.          ret
  2871.  
  2872.  GetTimeToElapse ENDP
  2873.  
  2874.  
  2875.  ;* CallMultiplex - Calls the Multiplex Interrupt (Interrupt 2Fh).
  2876.  ;*
  2877.  ;* Uses:   IDstring
  2878.  ;*
  2879.  ;* Params: AL = Function number for multiplex handler
  2880.  ;*
  2881.  ;* Return: AX    = One of the following return codes:
  2882.  ;*                 NOT_INSTALLED      IS_INSTALLED       NO_IDNUM
  2883.  ;*         ES:DI = Resident code segment:identifier string (function 0)
  2884.  ;*         ES:DI = Resident PSP segment address (function 1)
  2885.  ;*         ES:DI = Far address of shared memory (function 2)
  2886.  
  2887.  CallMultiplex PROC FAR USES ds
  2888.  
  2889.          push    ax                      ; Save function number
  2890.          mov     ax, @code
  2891.          mov     ds, ax                  ; Point DS to code segment
  2892.  
  2893.  ; First, check 2Fh vector. DOS Version 2.x may leave the vector null
  2894.  ; if PRINT.COM is not installed. If vector is null, point it to IRET
  2895.  ; instruction at LABEL NoMultiplex. This allows the new multiplex
  2896.  ; handler to pass control, if necessary, to a proper existing routine.
  2897.  
  2898.          mov     ax, 352Fh               ; Request DOS Function 35h
  2899.          int     21h                     ; Get Interrupt Vector in ES:BX
  2900.          mov     ax, es
  2901.          or      ax, bx
  2902.          .IF     zero?                   ; If Null vector:
  2903.          mov     dx, OFFSET NoMultiplex  ; Set vector to IRET instruction
  2904.          mov     ax, 252Fh               ;   at LABEL NoMultiplex
  2905.          int     21h                     ; Set Interrupt Vector
  2906.          .ENDIF
  2907.  
  2908.  ; Second, call Interrupt 2Fh with function 0 (presence request). Cycle
  2909.  ; through allowable identity numbers (192 to 255) until TSR's multiplex
  2910.  ; handler returns ES:DI = IDstring to verify its presence or until call
  2911.  ; returns AL = 0, indicating the TSR is not installed.
  2912.  
  2913.          mov     dh, 192                 ; Start with identity number = 192
  2914.  
  2915.          .REPEAT
  2916.          mov     ah, dh                  ; Call Multiplex with AH = trial ID
  2917.          sub     al, al                  ;   and AL = function 0
  2918.          push    dx                      ; Save DH and DS in case call
  2919.          push    ds                      ;   destroys them
  2920.          int     2Fh                     ; Multiplex
  2921.          pop     ds                      ; Recover DS and
  2922.          pop     dx                      ;   current ID number in DH
  2923.          or      al, al                  ; Does a handler claim this ID number
  2924.          jz      no                      ; If not, stop search
  2925.  
  2926.          .IF     al == 0FFh              ; If handler ready to process calls:
  2927.          mov     si, OFFSET IDstring     ; Point DS:SI to ID string, compare
  2928.          mov     cx, IDstrlen            ;   with string at ES:DI returned
  2929.          repe    cmpsb                   ;   by multiplex handler
  2930.          je      yes                     ; If equal, TSR's handler is found
  2931.          .ENDIF
  2932.  
  2933.          inc     dh                      ; This handler is not the one
  2934.          .UNTIL  zero?                   ; Try next identity number up to 255
  2935.  
  2936.          mov     ax, NO_IDNUM            ; In the unlikely event that numbers
  2937.          jmp     e_exit                  ;   192-255 are all taken, quit
  2938.  
  2939.  ; Third, assuming handler is found and verified, process the multiplex
  2940.  ; call with the requested function number.
  2941.  
  2942.  yes:
  2943.          pop     ax                      ; AL = original function number
  2944.          mov     ah, dh                  ; AH = identity number
  2945.          int     2Fh                     ; Multiplex
  2946.          mov     ax, IS_INSTALLED        ; Signal that handler has been found
  2947.          jmp     exit                    ;   and quit
  2948.  
  2949.  ; Reaching this section means multiplex handler (and TSR) not installed.
  2950.  ; Since the value in DH is not claimed by any handler, it will be used as
  2951.  ; the resident TSR's identity number.  Save the number in resident code
  2952.  ; segment so multiplex handler can find it.
  2953.  
  2954.  no:
  2955.          mov     IDnumber, dh            ; Save multiplex identity number
  2956.          mov     ax, NOT_INSTALLED       ; Signal handler is not installed
  2957.  e_exit:
  2958.          pop     bx                      ; Remove function number from stack
  2959.  exit:
  2960.          ret
  2961.  
  2962.  CallMultiplex ENDP
  2963.  
  2964.  
  2965.  ;* InitTsr - Initializes DOS version variables and multiplex data with
  2966.  ;* following parameters. This procedure must execute before calling
  2967.  ;* either the Install, Deinstall, or CallMultiplex procedures. Callable
  2968.  ;* from a high-level language.
  2969.  ;*
  2970.  ;* Uses:   IDstring
  2971.  ;*
  2972.  ;* Params: PspParam - Segment address of PSP
  2973.  ;*         StrParam - Far address of TSR's identifier string
  2974.  ;*         ShrParam - Far address of shared memory
  2975.  ;*
  2976.  ;* Return: AX = WRONG_DOS if not DOS Version 2.0 or higher
  2977.  
  2978.  InitTsr PROC    FAR USES ds es si di,
  2979.          PspParam:WORD, StrParam:FPVOID, ShrParam:FPVOID
  2980.  
  2981.          mov     ax, @code
  2982.          mov     ds, ax                          ; Point DS and ES
  2983.          mov     es, ax                          ;   to code segment
  2984.  
  2985.  ; Get and store parameters passed from main program module
  2986.  
  2987.          mov     ax, PspParam
  2988.          mov     TsrPspSeg, ax                   ; Store PSP segment address
  2989.  
  2990.          mov     ax, WORD PTR ShrParam[0]
  2991.          mov     bx, WORD PTR ShrParam[2]
  2992.          mov     WORD PTR ShareAddr[0], ax       ; Store far address of
  2993.          mov     WORD PTR ShareAddr[2], bx       ;   shared memory
  2994.  
  2995.          push    ds
  2996.          mov     si, WORD PTR StrParam[0]        ; DS:SI points to multiplex
  2997.          mov     ax, WORD PTR StrParam[2]        ;   identifier string
  2998.          mov     ds, ax
  2999.          mov     di, OFFSET IDstring             ; Copy string to IDstring
  3000.          mov     cx, STR_LEN                     ;   at ES:DI so multiplex
  3001.                                                  ;   handler has a copy
  3002.          .REPEAT
  3003.          lodsb                                   ; Copy STR_LEN characters
  3004.          .BREAK .IF al == 0                      ;   or until null-terminator
  3005.          stosb                                   ;   found
  3006.          .UNTILCXZ
  3007.  
  3008.          pop     ds                              ; Recover DS = code segment
  3009.          mov     ax, STR_LEN
  3010.          sub     ax, cx
  3011.          mov     IDstrlen, ax                    ; Store string length
  3012.  
  3013.          INVOKE  GetVersion                      ; Return AX = version number
  3014.          ret                                     ;   or WRONG_DOS
  3015.  
  3016.  InitTsr ENDP
  3017.  
  3018.  
  3019.  INSTALLCODE ENDS
  3020.  
  3021.          END
  3022.  
  3023.  
  3024.  INSTALL.ASM
  3025.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\TSR\INSTALL.ASM
  3026.  
  3027.          .MODEL  small, pascal, os_dos
  3028.          INCLUDE tsr.inc
  3029.  
  3030.  ;* INSTALLATION SECTION - The following code and data are used only
  3031.  ;* during the TSR's installation phase. When the program terminates
  3032.  ;* through Function 31h, memory occupied by the following code and
  3033.  ;* data segments is returned to the operating system.
  3034.  
  3035.  DGROUP  GROUP INSTALLCODE, INSTALLDATA
  3036.  
  3037.  INSTALLDATA SEGMENT WORD PUBLIC 'DATA2' ; Data segment for installation phase
  3038.  
  3039.          PUBLIC  _MsgTbl
  3040.  
  3041.  _MsgTbl WORD    Msg0                    ; Deinstalled okay
  3042.          WORD    Msg1                    ; Installed okay
  3043.          WORD    Msg2                    ; Already installed
  3044.          WORD    Msg3                    ; Can't install
  3045.          WORD    Msg4                    ; Can't find flag
  3046.          WORD    Msg5                    ; Can't deinstall
  3047.          WORD    Msg6                    ; Requires DOS 2+
  3048.          WORD    Msg7                    ; MCB damaged
  3049.          WORD    Msg8                    ; Invalid ID
  3050.          WORD    Msg9                    ; Invalid memory block address
  3051.          WORD    Msg10                   ; Successful access
  3052.          WORD    Msg11                   ; Can't access
  3053.          WORD    Msg12                   ; Unrecognized option
  3054.  
  3055.  Msg0    BYTE    CR, LF, "TSR deinstalled", CR, LF, 0
  3056.  Msg1    BYTE    CR, LF, "TSR installed", CR, LF, 0
  3057.  Msg2    BYTE    CR, LF, "TSR already installed", CR, LF, 0
  3058.  Msg3    BYTE    CR, LF, "Can't install TSR", CR, LF, 0
  3059.  Msg4    BYTE    CR, LF, "Can't find MS-DOS Critical Error Flag", CR, LF, 0
  3060.  Msg5    BYTE    CR, LF, "Can't deinstall TSR", CR, LF, 0
  3061.  Msg6    BYTE    CR, LF, "Requires MS-DOS 2.0 or later", CR, LF, 0
  3062.  Msg7    BYTE    CR, LF, "Memory Control Block damaged", CR, LF, 0
  3063.  Msg8    BYTE    CR, LF, "No ID numbers available", CR, LF, 0
  3064.  Msg9    BYTE    CR, LF, "Can't free memory block:  invalid address", CR, LF,0
  3065.  Msg10   BYTE    CR, LF, "TSR successfully accessed", CR, LF, 0
  3066.  Msg11   BYTE    CR, LF, "Can't access:  TSR not installed", CR, LF, 0
  3067.  Msg12   BYTE    CR, LF, "Unrecognized option", CR, LF, 0
  3068.  
  3069.  INSTALLDATA ENDS
  3070.  
  3071.  
  3072.  INSTALLCODE SEGMENT PARA PUBLIC 'CODE2'
  3073.  
  3074.          ASSUME  ds:@data
  3075.  
  3076.  ;* GetOptions - Scans command line for argument of form /X or -X
  3077.  ;* where X = specified ASCII character. Presumes that argument is
  3078.  ;* preceded by either '/' or '-'. Comparisons are case-insensitive.
  3079.  ;* Designed to be callable only from an assembly language program.
  3080.  ;*
  3081.  ;* Params: ES = Segment address of Program Segment Prefix
  3082.  ;*         AL = Argument character for which to scan
  3083.  ;*
  3084.  ;* Return: AX    = One of the following codes:
  3085.  ;*                 NO_ARGUMENT  if empty command line
  3086.  ;*                 OK_ARGUMENT  if argument found
  3087.  ;*                 BAD_ARGUMENT if argument not as specified
  3088.  ;*         ES:DI = Pointer to found argument
  3089.  
  3090.  GetOptions PROC NEAR
  3091.  
  3092.          and     al, 11011111y           ; Make character upper-case
  3093.          mov     ah, NO_ARGUMENT         ; Assume no argument
  3094.          mov     di, 80h                 ; Point to command line
  3095.          sub     ch, ch
  3096.          mov     cl, BYTE PTR es:[di]    ; Command-line count
  3097.          jcxz    exit                    ; If none, quit
  3098.          sub     bx, bx                  ; Initialize flag
  3099.  
  3100.  ; Find start of argument
  3101.  
  3102.  loop1:
  3103.          inc     di                      ; Point to next character
  3104.          mov     dl, es:[di]             ; Get character from argument list
  3105.          cmp     dl, '/'                 ; Find option prefix '/'
  3106.          je      analyze
  3107.          cmp     dl, '-'                 ;   or option prefix '-'
  3108.          je      analyze
  3109.          .IF     (dl != ' ') && (dl != TAB ) ; If not white space:
  3110.          inc     bx                      ; Set flag if command line not empty
  3111.          .ENDIF
  3112.  
  3113.          loop    loop1
  3114.  
  3115.          or      bx, bx                  ; Empty command line?
  3116.          jz      exit                    ; Yes?  Normal exit
  3117.          jmp     SHORT e_exit            ; Error if no argument is preceded
  3118.                                          ;    by '-' or '/' prefixes
  3119.  
  3120.  ; '/' or '-' prefix found. Compare command-line character
  3121.  ; with character specified in AL.
  3122.  analyze:
  3123.          mov     ah, OK_ARGUMENT         ; Assume argument is okay
  3124.          inc     di
  3125.          mov     dl, es:[di]
  3126.          and     dl, 11011111y           ; Convert to upper-case
  3127.          cmp     dl, al                  ; Argument as specified?
  3128.          je      exit                    ; If so, normal exit
  3129.          mov     ah, BAD_ARGUMENT        ; Else signal bad argument,
  3130.          inc     bx                      ;   raise flag, and
  3131.          jmp     loop1                   ;   continue scan
  3132.  
  3133.  e_exit:
  3134.          mov     ah, BAD_ARGUMENT
  3135.  exit:
  3136.          mov     al, ah
  3137.          cbw                             ; AX = return code
  3138.          ret
  3139.  
  3140.  GetOptions ENDP
  3141.  
  3142.  
  3143.  ;* FatalError - Displays an error message and exits to DOS.
  3144.  ;* Callable from a high-level language.
  3145.  ;*
  3146.  ;* Params: Err = Error number
  3147.  ;*
  3148.  ;* Return: AL = Error number returned to DOS (except DOS 1.x)
  3149.  
  3150.  FatalError PROC FAR,
  3151.          Err:WORD
  3152.  
  3153.          mov     ax, Err
  3154.          push    ax
  3155.          mov     bx, @data
  3156.          mov     ds, bx                  ; DS points to DGROUP
  3157.          mov     bx, OFFSET _MsgTbl
  3158.          shl     ax, 1                   ; Double to get offset into _MsgTbl
  3159.          add     bx, ax                  ; BX = table index
  3160.          mov     si, [bx]                ; DS:SI points to message
  3161.          sub     bx, bx                  ; BH = page 0
  3162.          mov     ah, 0Eh                 ; Request video Function 0Eh
  3163.  
  3164.          .WHILE  1
  3165.          lodsb                           ; Get character from ASCIIZ string
  3166.          .BREAK .IF al == 0              ; Break if null terminator
  3167.          int     10h                     ; Display text, advance cursor
  3168.          .ENDW
  3169.  
  3170.          pop     ax                      ; Recover original error code
  3171.  
  3172.          .IF     ax == WRONG_DOS         ; If DOS error:
  3173.          int     20h                     ; Terminate Program (Version 1.x)
  3174.          .ELSE                           ; Else:
  3175.          mov     ah, 4Ch                 ; Request DOS Function 4Ch
  3176.          int     21h                     ; Terminate Program (2.x and later)
  3177.          .ENDIF
  3178.  
  3179.  FatalError ENDP
  3180.  
  3181.  
  3182.  ;* KeepTsr -  Calls Terminate-and-Stay-Resident function to
  3183.  ;* make TSR resident. Callable from a high-level language.
  3184.  ;*
  3185.  ;* Params:  ParaNum - Number of paragraphs in resident block
  3186.  ;*
  3187.  ;* Return:  DOS return code = 0
  3188.  
  3189.  KeepTsr PROC    FAR,
  3190.          ParaNum:WORD
  3191.  
  3192.          mov     ax, @data
  3193.          mov     ds, ax                  ; DS:SI points to "Program
  3194.          mov     si, OFFSET Msg1         ;   installed" message
  3195.          sub     bx, bx                  ; BH = page 0
  3196.          mov     ah, 0Eh                 ; Request video Function 0Eh
  3197.  
  3198.          .WHILE  1
  3199.          lodsb                           ; Get character from ASCIIZ string
  3200.          .BREAK .IF al == 0              ; Break if null terminator
  3201.          int     10h                     ; Display text, advance cursor
  3202.          .ENDW
  3203.  
  3204.          mov     dx, ParaNum             ; DX = number of paragraphs
  3205.          mov     ax, 3100h               ; Request Function 31h, err code = 0
  3206.          int     21h                     ; Terminate-and-Stay-Resident
  3207.          ret
  3208.  
  3209.  KeepTsr ENDP
  3210.  
  3211.  
  3212.  ;* FreeTsr - Deinstalls TSR by freeing its two memory blocks: program
  3213.  ;* block (located at PSP) and environment block (located from address
  3214.  ;* at offset 2Ch of PSP). Callable from a high-level language.
  3215.  ;*
  3216.  ;* Params:  PspSeg - Segment address of TSR's Program Segment Prefix
  3217.  ;*
  3218.  ;* Return:  AX = 0 if successful, or one of the following error codes:
  3219.  ;*          MCB_DESTROYED       if Memory Control Block damaged
  3220.  ;*          INVALID_ADDR        if invalid block address
  3221.  
  3222.  FreeTsr PROC    FAR,
  3223.          PspSeg:WORD
  3224.  
  3225.          mov     es, PspSeg              ; ES = address of resident PSP
  3226.          mov     ah, 49h                 ; Request DOS Function 49h
  3227.          int     21h                     ; Release Memory in program block
  3228.  
  3229.          .IF     !carry?                 ; If no error:
  3230.          mov     es, es:[2Ch]            ; ES = address of environment block
  3231.          mov     ah, 49h                 ; Request DOS Function 49h
  3232.          int     21h                     ; Release Memory in environment block
  3233.          .IF     !carry?                 ; If no error:
  3234.          sub     ax, ax                  ; Return AX = 0
  3235.          .ENDIF                          ; Else exit with AX = error code
  3236.          .ENDIF
  3237.  
  3238.          ret
  3239.  
  3240.  FreeTsr ENDP
  3241.  
  3242.  
  3243.  ;* CallMultiplexC - Interface for CallMultiplex procedure to make it
  3244.  ;* callable from a high-level language. Separating this ability from
  3245.  ;* the original CallMultiplex procedure keeps assembly-language calls
  3246.  ;* to CallMultiplex neater and more concise.
  3247.  ;*
  3248.  ;* Params: FuncNum - Function number for multiplex handler
  3249.  ;*         RecvPtr - Far address to recieve ES:DI pointer
  3250.  ;*
  3251.  ;* Return: One of the following return codes:
  3252.  ;*              NOT_INSTALLED      IS_INSTALLED       NO_IDNUM
  3253.  ;*         ES:DI pointer written to address in RecvPtr
  3254.  
  3255.  CallMultiplexC PROC FAR USES ds si di,
  3256.          FuncNum:WORD, RecvPtr:FPVOID
  3257.  
  3258.          mov     al, BYTE PTR FuncNum    ; AL = function number
  3259.          call    CallMultiplex           ; Multiplex
  3260.  
  3261.          lds     si, RecvPtr             ; DS:SI = far address of pointer
  3262.          mov     [si], di                ; Return ES:DI pointer for the
  3263.          mov     [si+2], es              ;   benefit of high-level callers
  3264.          ret
  3265.  
  3266.  CallMultiplexC ENDP
  3267.  
  3268.  
  3269.  ;* GetResidentSize - Returns the number of paragraphs between Program
  3270.  ;* Segment Prefix and beginning of INSTALLCODE. This routine allows
  3271.  ;* TSRs written in a high-level language to determine the size in
  3272.  ;* paragraphs required to make the program resident.
  3273.  ;*
  3274.  ;* Params: PspSeg - PSP segment address
  3275.  ;*
  3276.  ;* Return: AX = Number of paragraphs
  3277.  
  3278.  GetResidentSize PROC FAR,
  3279.          PspSeg:WORD
  3280.  
  3281.          mov     ax, INSTALLCODE         ; Bottom of resident section
  3282.          sub     ax, PspSeg              ; AX = number of paragraphs in
  3283.          ret                             ;   block to be made resident
  3284.  
  3285.  GetResidentSize ENDP
  3286.  
  3287.  
  3288.  INSTALLCODE ENDS
  3289.  
  3290.          END
  3291.  
  3292.  
  3293.  MATH.ASM
  3294.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\DEMOS\MATH.ASM
  3295.  
  3296.          .MODEL  small, pascal, os_dos
  3297.          INCLUDE demo.inc
  3298.          .CODE
  3299.  
  3300.  ;* AddLong - Adds two double-word (long) integers.
  3301.  ;*
  3302.  ;* Shows:   Instructions - add     adc
  3303.  ;*          Operator - PTR
  3304.  ;*
  3305.  ;* Params:  Long1 - First integer
  3306.  ;*          Long2 - Second integer
  3307.  ;*
  3308.  ;* Return:  Sum as long integer
  3309.  
  3310.  AddLong PROC,
  3311.          Long1:SDWORD, Long2:SDWORD
  3312.  
  3313.          mov     ax, WORD PTR Long1[0]   ; AX = low word, long1
  3314.          mov     dx, WORD PTR Long1[2]   ; DX = high word, long1
  3315.          add     ax, WORD PTR Long2[0]   ; Add low word, long2
  3316.          adc     dx, WORD PTR Long2[2]   ; Add high word, long2
  3317.          ret                             ; Result returned as DX:AX
  3318.  
  3319.  AddLong ENDP
  3320.  
  3321.  ;* SubLong - Subtracts a double-word (long) integer from another.
  3322.  ;*
  3323.  ;* Shows:   Instructions -  sub     sbb
  3324.  ;*
  3325.  ;* Params:  Long1 - First integer
  3326.  ;*          Long2 - Second integer
  3327.  ;*
  3328.  ;* Return:  Difference as long integer
  3329.  
  3330.  SubLong PROC,
  3331.          Long1:SDWORD, Long2:SDWORD
  3332.  
  3333.          mov     ax, WORD PTR Long1[0]   ; AX = low word, long1
  3334.          mov     dx, WORD PTR Long1[2]   ; DX = high word, long1
  3335.          sub     ax, WORD PTR Long2[0]   ; Subtract low word, long2
  3336.          sbb     dx, WORD PTR Long2[2]   ; Subtract high word, long2
  3337.          ret                             ; Result returned as DX:AX
  3338.  
  3339.  SubLong ENDP
  3340.  
  3341.  
  3342.  ;* MulLong - Multiplies two unsigned double-word (long) integers. The
  3343.  ;* procedure allows for a product of twice the length of the multipliers,
  3344.  ;* thus preventing overflows. The result is copied into a 4-word data area
  3345.  ;* and a pointer to the data area is returned.
  3346.  ;*
  3347.  ;* Shows:   Instruction - mul
  3348.  ;*          Predefined equate - @data
  3349.  ;*
  3350.  ;* Params:  Long1 - First integer (multiplicand)
  3351.  ;*          Long2 - Second integer (multiplier)
  3352.  ;*
  3353.  ;* Return:  Pointer to quadword result
  3354.  
  3355.          .DATA
  3356.          PUBLIC result
  3357.  result  QWORD   WORD PTR ?              ; Result from MulLong
  3358.  
  3359.          .CODE
  3360.  MulLong PROC,
  3361.          Long1:DWORD, Long2:DWORD
  3362.  
  3363.          mov     ax, WORD PTR Long2[2]   ; Multiply long2 high word
  3364.          mul     WORD PTR Long1[2]       ;   by long1 high word
  3365.          mov     WORD PTR result[4], ax
  3366.          mov     WORD PTR result[6], dx
  3367.  
  3368.          mov     ax, WORD PTR Long2[2]   ; Multiply long2 high word
  3369.          mul     WORD PTR Long1[0]       ;   by long1 low word
  3370.          mov     WORD PTR result[2], ax
  3371.          add     WORD PTR result[4], dx
  3372.          adc     WORD PTR result[6], 0   ; Add any remnant carry
  3373.  
  3374.          mov     ax, WORD PTR Long2[0]   ; Multiply long2 low word
  3375.          mul     WORD PTR Long1[2]       ;   by long1 high word
  3376.          add     WORD PTR result[2], ax
  3377.          adc     WORD PTR result[4], dx
  3378.          adc     WORD PTR result[6], 0   ; Add any remnant carry
  3379.  
  3380.          mov     ax, WORD PTR Long2[0]   ; Multiply long2 low word
  3381.          mul     WORD PTR Long1[0]       ;   by long1 low word
  3382.          mov     WORD PTR result[0], ax
  3383.          add     WORD PTR result[2], dx
  3384.          adc     WORD PTR result[4], 0   ; Add any remnant carry
  3385.  
  3386.          mov     ax, OFFSET result       ; Return pointer
  3387.          mov     dx, @data               ;   to result
  3388.          ret
  3389.  
  3390.  MulLong ENDP
  3391.  
  3392.  
  3393.  ;* ImulLong - Multiplies two signed double-word integers. Because the imul
  3394.  ;* instruction (illustrated here) treats each word as a signed number, its
  3395.  ;* use is impractical when multiplying multi-word values. Thus the technique
  3396.  ;* used in the MulLong procedure can't be adopted here. Instead, ImulLong
  3397.  ;* is broken into three sections arranged in ascending order of computational
  3398.  ;* overhead. The procedure tests the values of the two integers and selects
  3399.  ;* the section that involves the minimum required effort to multiply them.
  3400.  ;*
  3401.  ;* Shows:   Instruction - imul
  3402.  ;*
  3403.  ;* Params:  Long1 - First integer (multiplicand)
  3404.  ;*          Long2 - Second integer (multiplier)
  3405.  ;*
  3406.  ;* Return:  Result as long integer
  3407.  
  3408.  ImulLong PROC USES si,
  3409.          Long1:SDWORD, Long2:SDWORD
  3410.  
  3411.  ; Section 1 tests for integers in the range of 0 to 65,535. If both
  3412.  ; numbers are within these limits, they're treated as unsigned short
  3413.  ; integers.
  3414.  
  3415.          mov     ax, WORD PTR Long2[0]   ; AX = low word of long2
  3416.          mov     dx, WORD PTR Long2[2]   ; DX = high word of long2
  3417.          mov     bx, WORD PTR Long1[0]   ; BX = low word of long1
  3418.          mov     cx, WORD PTR Long1[2]   ; CX = high word of long1
  3419.          .IF     (dx == 0) && (cx == 0)  ; If both high words are zero:
  3420.          mul     bx                      ;   Multiply the low words
  3421.          jmp     exit                    ;   and exit section 1
  3422.          .ENDIF
  3423.  
  3424.  ; Section 2 tests for integers in the range of -32,768 to 32,767. If
  3425.  ; both numbers are within these limits, they're treated as signed short
  3426.  ; integers.
  3427.  
  3428.          push    ax                      ; Save long2 low word
  3429.          push    bx                      ; Save long1 low word
  3430.          or      dx, dx                  ; High word of long2 = 0?
  3431.          jnz     notzhi2                 ; No?  Test for negative
  3432.          test    ah, 80h                 ; Low word of long2 in range?
  3433.          jz      notnlo2                 ; Yes?  long2 ok, so test long1
  3434.          jmp     sect3                   ; No?  Go to section 3
  3435.  notzhi2:
  3436.          cmp     dx, 0FFFFh              ; Empty with sign flag set?
  3437.          jne     sect3                   ; No?  Go to section 3
  3438.          test    ah, 80h                 ; High bit set in low word?
  3439.          jz      sect3                   ; No?  Low word is too high
  3440.  notnlo2:
  3441.          or      cx, cx                  ; High word of long1 = 0?
  3442.          jnz     notzhi1                 ; No?  Test for negative
  3443.          test    bh, 80h                 ; Low word of long1 in range?
  3444.          jz      notnlo1                 ; Yes?  long1 ok, so use sect 2
  3445.          jmp     sect3                   ; No?  Go to section 3
  3446.  notzhi1:
  3447.          cmp     cx, 0FFFFh              ; Empty with sign flag set?
  3448.          jne     sect3                   ; No?  Go to section 3
  3449.          test    bh, 80h                 ; High bit set in low word?
  3450.          jz      sect3                   ; No?  Low word is too high
  3451.  notnlo1:
  3452.          imul    bx                      ; Multiply low words
  3453.          pop     bx                      ; Clean stack
  3454.          pop     bx
  3455.          jmp     exit                    ; Exit section 2
  3456.  
  3457.  ; Section 3 involves the most computational overhead. It treats the two
  3458.  ; numbers as signed long (double-word) integers.
  3459.  
  3460.  sect3:
  3461.          pop     bx                      ; Recover long1 low word
  3462.          pop     ax                      ; Recover long2 low word
  3463.          mov     si, dx                  ; SI = long2 high word
  3464.          push    ax                      ; Save long2 low word
  3465.          mul     cx                      ; long1 high word x long2 low word
  3466.          mov     cx, ax                  ; Accumulate products in CX
  3467.          mov     ax, bx                  ; AX = low word of long1
  3468.          mul     si                      ; Multiply by long2 high word
  3469.          add     cx, ax                  ; Add to previous product
  3470.          pop     ax                      ; Recover long2 low word
  3471.          mul     bx                      ; Multiply by long1 low word
  3472.          add     dx, cx                  ; Add to product high word
  3473.  exit:
  3474.          ret                             ; Return result as DX:AX
  3475.  
  3476.  ImulLong ENDP
  3477.  
  3478.  
  3479.  ;* DivLong - Divides an unsigned long integer by an unsigned short integer.
  3480.  ;* The procedure does not check for overflow or divide-by-zero.
  3481.  ;*
  3482.  ;* Shows:   Instruction -  div
  3483.  ;*
  3484.  ;* Params:  Long1 - First integer (dividend)
  3485.  ;*          Short2 - Second integer (divisor)
  3486.  ;*          Remn - Pointer to remainder
  3487.  ;*
  3488.  ;* Return:  Quotient as short integer
  3489.  
  3490.  DivLong PROC USES di,
  3491.          Long1:DWORD, Short2:WORD, Remn:PWORD
  3492.  
  3493.          mov     ax, WORD PTR Long1[0]   ; AX = low word of dividend
  3494.          mov     dx, WORD PTR Long1[2]   ; DX = high word of dividend
  3495.          div     Short2                  ; Divide by short integer
  3496.          LoadPtr es, di, Remn            ; Point ES:DI to remainder
  3497.          mov     es:[di], dx             ; Copy remainder
  3498.          ret                             ; Return with AX = quotient
  3499.  
  3500.  DivLong ENDP
  3501.  
  3502.  
  3503.  ;* IdivLong - Divides a signed long integer by a signed short integer.
  3504.  ;* The procedure does not check for overflow or divide-by-zero.
  3505.  ;*
  3506.  ;* Shows:   Instruction - idiv
  3507.  ;*
  3508.  ;* Params:  Long1 - First integer (dividend)
  3509.  ;*          Short2 - Second integer (divisor)
  3510.  ;*          Remn - Pointer to remainder
  3511.  ;*
  3512.  ;* Return:  Quotient as short integer
  3513.  
  3514.  IdivLong PROC USES di,
  3515.          Long1:SDWORD, Short2:SWORD, Remn:PSWORD
  3516.  
  3517.          mov     ax, WORD PTR Long1[0]   ; AX = low word of dividend
  3518.          mov     dx, WORD PTR Long1[2]   ; DX = high word of dividend
  3519.          idiv    Short2                  ; Divide by short integer
  3520.          LoadPtr es, di, Remn            ; ES:DI = remainder
  3521.          mov     es:[di], dx             ; Copy remainder
  3522.          ret                             ; Return with AX = quotient
  3523.  
  3524.  IdivLong ENDP
  3525.  
  3526.  
  3527.  ;* Quadratic - Solves for the roots of a quadratic equation of form
  3528.  ;*                        A*x*x + B*x + C = 0
  3529.  ;* using floating-point instructions. This procedure requires either a math
  3530.  ;* coprocessor or emulation code.
  3531.  ;*
  3532.  ;* Shows:   Instructions - sahf     fld1     fld     fadd     fmul
  3533.  ;*                         fxch     fsubr    fchs    fsubp    fstp
  3534.  ;*                         fst      fstsw    fdivr   fwait    ftst
  3535.  ;*
  3536.  ;* Params:  a - Constant for 2nd-order term
  3537.  ;*          b - Constant for 1st-order term
  3538.  ;*          c - Equation constant
  3539.  ;*          R1 - Pointer to 1st root
  3540.  ;*          R2 - Pointer to 2nd root
  3541.  ;*
  3542.  ;* Return:  Short integer with return code
  3543.  ;*          0 if both roots found
  3544.  ;*          1 if single root (placed in R1)
  3545.  ;*          2 if indeterminate
  3546.  
  3547.  Quadratic PROC USES ds di si,
  3548.          aa:DWORD, bb:DWORD, cc:DWORD, r1:PDWORD, r2:PDWORD
  3549.  
  3550.          LOCAL status:WORD               ; Intermediate status
  3551.  
  3552.          LoadPtr es, di, r1              ; ES:DI points to 1st root
  3553.          LoadPtr ds, si, r2              ; DS:SI points to 2nd root
  3554.          sub     bx, bx                  ; Clear error code
  3555.          fld1                            ; Load top of stack with 1
  3556.          fadd    st, st                  ; Double it to make 2
  3557.          fld     st                      ; Copy to next register
  3558.          fmul    aa                      ; ST register = 2a
  3559.          ftst                            ; Test current ST value
  3560.          fstsw   status                  ; Copy status to local word
  3561.          fwait                           ; Ensure coprocessor is done
  3562.          mov     ax, status              ; Copy status into AX
  3563.          sahf                            ; Load flag register
  3564.          jnz     notzero                 ; If C3 set, a = 0, in which case
  3565.                                          ;   solution is x = -c / b
  3566.          fld     cc                      ; Load c parameter
  3567.          fchs                            ; Reverse sign
  3568.          fld     bb                      ; Load b parameter
  3569.          ftst                            ; Test current ST value
  3570.          fstsw   status                  ; Copy status to local word
  3571.          fwait                           ; Ensure coprocessor is done
  3572.          mov     ax, status              ; Copy status into AX
  3573.          sahf                            ; Load flag register
  3574.          jz      exit2                   ; If C3 set, b = 0, in which case
  3575.                                          ; division by zero
  3576.          fdiv                            ; Divide by B
  3577.          fstp    DWORD PTR es:[di]       ; Copy result and pop stack
  3578.          fstp    st                      ; Clean up stack
  3579.          jmp     exit1                   ; Return with code = 1
  3580.  notzero:
  3581.          fmul    st(1), st               ; ST(1) register = 4a
  3582.          fxch                            ; Exchange ST and ST(1)
  3583.          fmul    cc                      ; ST register = 4ac
  3584.          ftst                            ; Test current ST value
  3585.          fstsw   status                  ; Copy status to local word
  3586.          fwait                           ; Ensure coprocessor is done
  3587.          mov     ax, status              ; Copy status into AX
  3588.          sahf                            ; Load flag register
  3589.          jp      exit2                   ; If C2 set, 4*a*c is infinite
  3590.  
  3591.          fld     bb                      ; Else load b parameter
  3592.          fmul    st, st                  ; Square it; ST register = b*b
  3593.          fsubr                           ; ST register = b*b - 4*a*c
  3594.          ftst                            ; Test current ST value
  3595.          fstsw   status                  ; Copy status to local word
  3596.          fwait                           ; Ensure coprocessor is done
  3597.          mov     ax, status              ; Copy status into AX
  3598.          sahf                            ; Load flag register
  3599.          jc      exit2                   ; If C0 set, b*b < 4ac
  3600.          jnz     tworoot                 ; If C3 set, b*b = 4ac, in which
  3601.          inc     bx                      ;   case only 1 root so set flag
  3602.  tworoot:
  3603.          fsqrt                           ; Get square root
  3604.          fld     bb                      ; Load b parameter
  3605.          fchs                            ; Reverse sign
  3606.          fxch                            ; Exchange ST and ST1
  3607.          fld     st                      ; Copy square root to next reg
  3608.          fadd    st, st(2)               ; ST = -b + sqrt(b*b - 4*a*c)
  3609.          fxch                            ; Exchange ST and ST1
  3610.          fsubp   st(2), st               ; ST = -b - sqrt(b*b - 4*a*c)
  3611.  
  3612.          fdiv    st, st(2)               ; Divide 1st dividend by 2*a
  3613.          fstp    DWORD PTR es:[di]       ; Copy result, pop stack
  3614.          fdivr                           ; Divide 2nd dividend by 2*a
  3615.          fstp    DWORD PTR ds:[si]       ; Copy result, pop stack
  3616.          jmp     exit                    ; Return with code
  3617.  exit2:
  3618.          inc     bx                      ; Error code = 2 for indeterminancy
  3619.          fstp    st                      ; Clean stack
  3620.  exit1:
  3621.          inc     bx                      ; Error code = 1 for single root
  3622.          fstp    st                      ; Clean stack
  3623.  exit:
  3624.          mov ax, bx
  3625.          ret
  3626.  
  3627.  Quadratic ENDP
  3628.  
  3629.          END
  3630.  
  3631.  
  3632.  MISC.ASM
  3633.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\DEMOS\MISC.ASM
  3634.  
  3635.          .modeL  small, pascal, os_dos
  3636.          INCLUDE demo.inc
  3637.  
  3638.          .DATA
  3639.  _psp    PSEG    ?               ; Segment of PSP
  3640.  _env    PSEG    ?               ; Segment of environment
  3641.  
  3642.          .CODE
  3643.  
  3644.  ;* WinOpen - Saves portion of screen to allocated memory, then opens a window
  3645.  ;* with specified fill attribute. See also the WinClose procedure.
  3646.  ;*
  3647.  ;* Shows:   DOS Function - 48h (Allocate Memory Block)
  3648.  ;*          Instructions - movsw      stosw     rep
  3649.  ;*
  3650.  ;* Uses:    vconfig - Video configuration structure (initialized
  3651.  ;*          by calling the GetVidConfig procedure)
  3652.  ;*
  3653.  ;* Params:  Row1 - Row at top of window
  3654.  ;*          Col1 - Column at left edge of window
  3655.  ;*          Row2 - Row at bottom of window
  3656.  ;*          Col2 - Column at right edge of window
  3657.  ;*          Attr - Fill attribute for window
  3658.  ;*
  3659.  ;* Return:  Short integer with segment address of allocated buffer, or
  3660.  ;*          0 if unable to allocate memory
  3661.  
  3662.  WinOpen PROC USES ds di si,
  3663.          Row1:WORD, Col1:WORD, Row2:WORD, Col2:WORD, Attr:WORD
  3664.  
  3665.          GetVidOffset Row1, Col1         ; Get offset in video segment
  3666.          mov     si, ax                  ; SI = video offset for window
  3667.          mov     bx, Row2
  3668.          sub     bx, Row1
  3669.          inc     bx                      ; BX = number of window rows
  3670.          mov     cx, Col2
  3671.          sub     cx, Col1
  3672.          inc     cx                      ; CX = number of columns
  3673.  
  3674.          mov     ax, cx                  ; Compute number of video
  3675.          mul     bl                      ;   cells in window
  3676.          add     ax, 3                   ; Plus 3 additional entries
  3677.          shr     ax, 1                   ; Shift right 3 times to
  3678.          shr     ax, 1                   ;   multiply by 2 bytes/cell,
  3679.          shr     ax, 1                   ;   divide by 16 bytes/para
  3680.          inc     ax                      ; Add a paragraph
  3681.          push    bx                      ; Save number of rows
  3682.          mov     bx, ax                  ; BX = number of paragraphs
  3683.          mov     ah, 48h                 ; Request DOS Function 48h
  3684.          int     21h                     ; Allocate Memory Block
  3685.          pop     bx
  3686.  
  3687.          .IF     carry?                  ; If unsuccessful:
  3688.          sub     ax, ax                  ; Return null pointer
  3689.          .ELSE
  3690.          mov     es, ax                  ; Point ES:DI to allocated
  3691.          sub     di, di                  ;   buffer
  3692.          mov     ax, si
  3693.          stosw                           ; Copy video offset to buffer
  3694.          mov     ax, bx
  3695.          stosw                           ; Number of rows to buffer
  3696.          mov     ax, cx
  3697.          stosw                           ; Number of cols to buffer
  3698.          mov     ax, 160                 ; Number of video cells/row
  3699.          mov     ds, vconfig.sgmnt       ; DS = video segment
  3700.  
  3701.          .REPEAT
  3702.          push    si                      ; Save ptr to start of line
  3703.          push    cx                      ;   and number of columns
  3704.  
  3705.  ; For CGA adapters, WinOpen avoids screen "snow" by disabling the video prior
  3706.  ; to block memory moves, then reenabling it. Although this technique can
  3707.  ; result in brief flickering, it demonstrates the fastest way to access a
  3708.  ; block in the CGA video buffer without causing display snow. See also the
  3709.  ; StrWrite procedure for another solution to the problem of CGA snow.
  3710.  
  3711.          .IF     vconfig.adapter == CGA  ; If not CGA adapter,
  3712.          INVOKE  DisableCga              ;   disable video
  3713.          .ENDIF
  3714.  
  3715.          rep     movsw                   ; Copy one row to buffer
  3716.  
  3717.          .IF     vconfig.adapter == CGA  ; If CGA adapter,
  3718.          INVOKE  EnableCga               ;   reenable CGA video
  3719.          .ENDIF
  3720.          pop     cx                      ; Recover number of columns
  3721.          pop     si                      ;   and start of line
  3722.          add     si, ax                  ; Point to start of next line
  3723.          dec     bx                      ; Decrement row counter
  3724.          .UNTIL  zero?                   ; Until no rows remain
  3725.  
  3726.  ; Screen contents (including display attributes) are now copied to buffer.
  3727.  ; Next open window, overwriting the screen portion just saved.
  3728.  
  3729.          mov     ax, 0600h               ; Scroll service
  3730.          mov     bh, BYTE PTR Attr       ; Fill attribute
  3731.          mov     cx, Col1                ; CX = row/col for upper left
  3732.          mov     ch, BYTE PTR Row1
  3733.          mov     dx, Col2                ; DX = row/col for lower right
  3734.          mov     dh, BYTE PTR Row2
  3735.          int     10h                     ; Blank window area on screen
  3736.          mov     ax, es                  ; Return address of allocated
  3737.          .ENDIF                          ;   segment
  3738.          ret
  3739.  
  3740.  WinOpen ENDP
  3741.  
  3742.  
  3743.  ;* WinClose - "Closes" a window previously opened by the WinOpen procedure.
  3744.  ;* See also the WinOpen procedure.
  3745.  ;*
  3746.  ;* Shows:   DOS Function - 49h (Release Memory Block)
  3747.  ;*          Instructions - lodsw
  3748.  ;*          Operators - : (segment override)     SEG
  3749.  ;*
  3750.  ;* Uses:    vconfig - Video configuration structure (initialized
  3751.  ;*          by calling the GetVidConfig procedure)
  3752.  ;*
  3753.  ;* Params:  Adr - Segment address of buffer that holds screen contents
  3754.  ;*                 saved in WinOpen procedure
  3755.  ;*
  3756.  ;* Return:  None
  3757.  
  3758.  WinClose PROC USES ds di si,
  3759.          Adr:WORD
  3760.  
  3761.          mov     ds, Adr                 ; DS:SI points to buffer
  3762.          sub     si, si
  3763.          lodsw
  3764.          mov     di, ax                  ; DI = video offset of window
  3765.          lodsw
  3766.          mov     bx, ax                  ; BX = number of window rows
  3767.          lodsw
  3768.          mov     cx, ax                  ; CX = number of columns
  3769.  
  3770.          mov     ax, SEG vconfig.sgmnt
  3771.          mov     es, ax                  ; Point ES to data segment
  3772.          push    es:vconfig.sgmnt
  3773.          pop     es                      ; ES = video segment
  3774.          mov     ax, 160                 ; Number of video cells/row
  3775.  
  3776.          .REPEAT
  3777.          push    di                      ; Save ptr to start of line
  3778.          push    cx                      ;   and number of columns
  3779.  
  3780.  ; Disable CGA video prior to memory move to avoid screen snow. (See the
  3781.  ; WinOpen and StrWrite procedures for further discussions on CGA snow.)
  3782.  
  3783.          .IF     vconfig.adapter == CGA  ; If CGA adapter,
  3784.          INVOKE  DisableCga              ;   disable video
  3785.          .ENDIF
  3786.  
  3787.          rep     movsw                   ; Copy one row to buffer
  3788.  
  3789.          .IF     vconfig.adapter == CGA  ; If CGA adapter,
  3790.          INVOKE  EnableCga               ;   reenable CGA video
  3791.          .ENDIF
  3792.          pop     cx                      ; Recover number of columns
  3793.          pop     di                      ;   and start of line
  3794.          add     di, ax                  ; Point to start of next line
  3795.          dec     bx                      ; Decrement row counter
  3796.          .UNTIL  zero?                   ;   until no rows remain
  3797.  
  3798.          mov     ah, 49h                 ; Request DOS Function 49h
  3799.          mov     es, Adr
  3800.          int     21h                     ; Release Memory Block
  3801.          ret
  3802.  
  3803.  WinClose ENDP
  3804.  
  3805.  
  3806.  ;* SetCurSize - Sets cursor size.
  3807.  ;*
  3808.  ;* Shows:   BIOS Interrupt - 10h, Function 1 (Set Cursor Type)
  3809.  ;*
  3810.  ;* Params:  Scan1 - Starting scan line
  3811.  ;*          Scan2 - Ending scan line
  3812.  ;*
  3813.  ;* Return:  None
  3814.  
  3815.  SetCurSize PROC,
  3816.          Scan1:WORD, Scan2:WORD
  3817.  
  3818.          mov     cx, Scan2               ; CL = ending scan line
  3819.          mov     ch, BYTE PTR Scan1      ; CH = starting scan line
  3820.          mov     ah, 1                   ; Function 1
  3821.          int     10h                     ; Set Cursor Type
  3822.          ret
  3823.  
  3824.  SetCurSize ENDP
  3825.  
  3826.  
  3827.  ;* GetCurSize - Gets current cursor size.
  3828.  ;*
  3829.  ;* Shows:   BIOS Interrupt - 10h, Function 3 (Get Cursor Position)
  3830.  ;*
  3831.  ;* Uses:    vconfig - Video configuration structure (initialized
  3832.  ;*          by calling the GetVidConfig procedure)
  3833.  ;*
  3834.  ;* Params:  None
  3835.  ;*
  3836.  ;* Return:  Short integer with high byte = top scan line,
  3837.  ;*                             low byte  = bottom scan line
  3838.  
  3839.  GetCurSize PROC
  3840.  
  3841.          mov     ah, 3                   ; Function 3
  3842.          mov     bh, vconfig.dpage
  3843.          int     10h                     ; Get Cursor Position
  3844.          mov     ax, cx                  ; Return cursor size
  3845.          ret
  3846.  
  3847.  GetCurSize ENDP
  3848.  
  3849.  
  3850.  ;* GetShift - Gets current shift status. Checks for extended keyboard,
  3851.  ;* and if available returns additional shift information.
  3852.  ;*
  3853.  ;* Shows:   BIOS Interrupt - 16h, Functions 2 and 12h (Get Keyboard Flags)
  3854.  ;*
  3855.  ;* Params:  None
  3856.  ;*
  3857.  ;* Return:  Long integer
  3858.  ;*          high word = 0 for non-extended keyboard
  3859.  ;*                      1 for extended keyboard
  3860.  ;*          low word has following bits set when indicated keys are pressed:
  3861.  ;*          0 - Right shift                   8 - Left Ctrl
  3862.  ;*          1 - Left shift                    9 - Left Alt
  3863.  ;*          2 - Ctrl                         10 - Right Ctrl
  3864.  ;*          3 - Alt                          11 - Right Alt
  3865.  ;*          4 - Scroll Lock active           12 - Scroll Lock pressed
  3866.  ;*          5 - Num Lock active              13 - Num Lock pressed
  3867.  ;*          6 - Caps Lock active             14 - Caps Lock pressed
  3868.  ;*          7 - Insert toggled               15 - Sys Req pressed
  3869.  
  3870.  GetShift PROC
  3871.  
  3872.          sub     dx, dx                  ; Assume non-extended keyboard
  3873.          mov     ah, 2                   ;   and use Function 2
  3874.          mov     es, dx                  ; Point ES to low memory
  3875.          .IF     BYTE PTR es:[496h] & 16 ; If extended keyboard installed,
  3876.          inc     dx                      ;   Set high word of return code
  3877.          mov     ah, 12h                 ;   and use Function 12h
  3878.          .ENDIF
  3879.          int     16h                     ; Get Keyboard Flags
  3880.          ret
  3881.  
  3882.  GetShift ENDP
  3883.  
  3884.  
  3885.  ;* GetKeyClock - Waits for keypress while updating time at specified location
  3886.  ;* on screen.
  3887.  ;*
  3888.  ;* Shows:   BIOS Interrupt - 16h, Functions 0 and 10h (Read Character)
  3889.  ;*                           16h, Functions 1 and 11h (Get Keyboard Status)
  3890.  ;*          DOS Functions - 2Ah (Get Date)
  3891.  ;*                          2Ch (Get Time)
  3892.  ;*
  3893.  ;* Uses:    vconfig - Video configuration structure (initialized
  3894.  ;*          by calling the GetVidConfig procedure)
  3895.  ;*
  3896.  ;* Params:  Row - Screen row for clock display
  3897.  ;*          Col - Screen column for clock display
  3898.  ;*
  3899.  ;* Return:  Short integer with key scan code in high byte and ASCII
  3900.  ;*          character code in low byte. Low byte is 0 for special
  3901.  ;*          keys (such as the "F" keys) which don't generate characters.
  3902.  
  3903.          .DATA
  3904.          PUBLIC  datestr
  3905.  datestr BYTE    "  -  -     :  :  ", 0  ; Date/time string
  3906.          .CODE
  3907.  
  3908.  GetKeyClock PROC,
  3909.          Row:WORD, Col:WORD
  3910.  
  3911.          LOCAL   service:BYTE
  3912.  
  3913.          INVOKE  GetShift                ; Check for extended keyboard
  3914.          mov     service, 11h            ; Assume Function 11h
  3915.          .IF     dx != 1                 ; If no extended keyboard:
  3916.          mov     service, 1              ; Use Function 1
  3917.          .ENDIF
  3918.  
  3919.          .WHILE  1
  3920.          mov     ah, service
  3921.          int     16h                     ; Get Keyboard Status
  3922.          .BREAK  .IF !zero?              ; If no key yet, update clock
  3923.  
  3924.  ; If not monochrome, color text, or black and white, skip clock update
  3925.  ; and poll keyboard again
  3926.  
  3927.          .CONTINUE .IF (vconfig.mode != 7) \
  3928.                     && (vconfig.mode != 3) \
  3929.                     && (vconfig.mode != 2)
  3930.  
  3931.  ; If 80-column text, get date and time from DOS before again
  3932.  ; polling keyboard, and display at upper right corner of screen.
  3933.  
  3934.          mov     ah, 2Ch                 ; Request time
  3935.          int     21h                     ; Get Time
  3936.          mov     dl, dh
  3937.          push    dx                      ; Save seconds,
  3938.          push    cx                      ;   minutes,
  3939.          mov     cl, ch                  ;   and
  3940.          push    cx                      ;   hours
  3941.          mov     ah, 2Ah                 ; Request date
  3942.          int     21h                     ; Get Date
  3943.          sub     cx, 1900                ; Subtract century, CL = year
  3944.          push    cx                      ; Save year,
  3945.          push    dx                      ;   day,
  3946.          mov     dl, dh                  ;   and
  3947.          push    dx                      ;   month
  3948.  
  3949.          mov     cx, 6
  3950.          sub     bx, bx
  3951.  
  3952.          .REPEAT
  3953.          pop     ax                      ; Recover all 6 numbers in AL
  3954.          aam                             ; Convert to unpacked BCD
  3955.          xchg    al, ah                  ; Switch bytes for word move
  3956.          or      ax, "00"                ; Make ASCII numerals
  3957.          mov     WORD PTR datestr[bx], ax; Copy to string
  3958.          add     bx, 3                   ;   at every third byte
  3959.          .UNTILCXZ
  3960.  
  3961.          INVOKE  StrWrite, Row, Col, ADDR datestr
  3962.          .ENDW                           ; Loop again for keypress
  3963.  
  3964.          mov     ah, service             ; 1 or 11h, depending on keybd
  3965.          dec     ah                      ; Set AH to 0 or 10h
  3966.          int     16h                     ; Get key to remove it from
  3967.          ret                             ;   keyboard buffer
  3968.  
  3969.  GetKeyClock ENDP
  3970.  
  3971.  
  3972.  ;* GetPSP - Gets address of Program Segment Prefix. For DOS 3.0 or higher.
  3973.  ;*
  3974.  ;* Shows:   DOS Function - 62h (Get PSP Address)
  3975.  ;*          Instruction - call
  3976.  ;*
  3977.  ;* Params:  None
  3978.  ;*
  3979.  ;* Return:  Short integer with PSP segment address
  3980.  ;*          or 0 if DOS version below 3.0
  3981.  
  3982.  GetPSP  PROC
  3983.  
  3984.          INVOKE  GetVer                  ; Get DOS version number
  3985.          .IF     ax >= 300               ; If DOS 3.0 or higher:
  3986.          mov     ah, 62h                 ; Query DOS for PSP
  3987.          int     21h                     ; Get PSP Address
  3988.          mov     ax, bx                  ; Return in AX
  3989.          .ELSE                           ; Else 2.0:
  3990.          sub     ax, ax                  ; For version 2, return 0
  3991.          .ENDIF
  3992.          ret
  3993.  
  3994.  GetPSP  ENDP
  3995.  
  3996.  
  3997.  ;* GetMem - Gets total size of memory and determines the largest amount of
  3998.  ;* unallocated memory available. GetMem invokes DOS Function 48h (Allocate
  3999.  ;* Memory) to request an impossibly large memory block. DOS denies the re-
  4000.  ;* quest, but returns instead the size of the largest block available. This
  4001.  ;* is the amount that GetMem returns to the calling program. See the WinOpen
  4002.  ;* procedure for an example of calling Function 48h to allocate unused memory
  4003.  ;*
  4004.  ;* Shows:   BIOS Interrupt - 12h (Get Conventional Memory Size)
  4005.  ;*          Instructions - push     pop      ret
  4006.  ;*
  4007.  ;* Params:  None
  4008.  ;*
  4009.  ;* Return:  Long integer, high word = total memory in kilobytes (KB)
  4010.  ;*                        low word  = largest block of available memory (KB)
  4011.  
  4012.  GetMem  PROC
  4013.  
  4014.          int     12h                     ; Get total memory in K
  4015.          push    ax                      ; Save size of memory
  4016.          mov     ah, 48h                 ; Request memory allocation
  4017.          mov     bx, 0FFFFh              ; Ensure request is denied for
  4018.                                          ;   impossibly large block
  4019.          int     21h                     ; Get largest available block in BX
  4020.          mov     ax, bx                  ; Copy to AX
  4021.          mov     cl, 6                   ; Convert paragraphs to kilobytes by
  4022.          shr     ax, cl                  ;   dividing by 64
  4023.          pop     dx                      ; Recover total in DX
  4024.          ret                             ; Return long integer DX:AX
  4025.  
  4026.  GetMem  ENDP
  4027.  
  4028.  
  4029.  ;* VeriPrint - Checks if LPT1 (PRN) is available.
  4030.  ;*
  4031.  ;* Shows:   BIOS Interrupt - 17h (Parallel Port Printer Driver)
  4032.  ;*
  4033.  ;* Params:  None
  4034.  ;*
  4035.  ;* Return:  Short integer, 1 for yes or 0 for no
  4036.  
  4037.  VeriPrint PROC
  4038.  
  4039.          mov     ah, 2                   ; Check printer status for
  4040.          sub     dx, dx                  ;   parallel printer (port 0)
  4041.          int     17h
  4042.          xchg    dx, ax                  ; Put 0 (for error) in AX
  4043.  
  4044.  ; If all error bits are off and both operation bits are on, return 1
  4045.  
  4046.          .IF     !(dh & 00101001y) && (dh & 10010000y)
  4047.          inc     ax                      ; Return 1
  4048.          .ENDIF
  4049.          ret
  4050.  
  4051.  VeriPrint ENDP
  4052.  
  4053.  
  4054.  ;* IntToAsc - Converts integer to ASCII string. This procedure is useful
  4055.  ;* only for assembly language, and is not intended to be C-callable.
  4056.  ;*
  4057.  ;* Shows:   Instructions - aam     xchg
  4058.  ;*
  4059.  ;* Entry:   AX = integer (9999 max)
  4060.  ;*
  4061.  ;* Return:  DX:AX = 4-digit ASCII number
  4062.  
  4063.  IntToAsc PROC
  4064.  
  4065.          cwd                             ; Zero DX register
  4066.          mov     cx, 100                 ; Divide AX by 100, yields
  4067.          div     cx                      ;   AX=quotient, DX=remainder
  4068.          aam                             ; Make digits unpacked BCD
  4069.          or      ax, "00"                ; Convert to ASCII
  4070.          xchg    ax, dx                  ; Do same thing for DX
  4071.          aam
  4072.          or      ax, "00"
  4073.          ret                             ; Return DX:AX = ASCII number
  4074.  
  4075.  IntToAsc ENDP
  4076.  
  4077.  
  4078.  ;* VeriAnsi - Checks for ANSI driver by writing ANSI sequence to report
  4079.  ;* cursor position. If report compares with position returned from
  4080.  ;* GetCurPos procedure, then ANSI driver is operating.
  4081.  ;*
  4082.  ;* Shows:   DOS Functions - 06h (Direct Console I/O)
  4083.  ;*                          0Ch (Flush Input Buffer and then Input)
  4084.  ;*
  4085.  ;* Params:  None
  4086.  ;*
  4087.  ;* Return:  Short integer, 1 for yes or 0 for no
  4088.  
  4089.          .DATA
  4090.          PUBLIC report
  4091.  report  DB      ESCAPE, "[6n$"          ; ANSI Report Cursor sequence
  4092.          .CODE
  4093.  
  4094.  VeriAnsi PROC
  4095.  
  4096.          ; Get cursor position from BIOS
  4097.          INVOKE  GetCurPos
  4098.          mov     cx, ax                  ; Save it in CX
  4099.          mov     dx, OFFSET report       ; ANSI string to get position
  4100.          mov     ah, 9                   ; Request DOS String Output
  4101.          int     21h                     ; Write ANSI escape sequence
  4102.  
  4103.          mov     ah, 6                   ; Skip Esc character in
  4104.          mov     dl, 0FFh                ;   keyboard buffer
  4105.          int     21h
  4106.          jz      e_exit                  ; If no key, ANSI not loaded
  4107.          mov     ah, 6                   ; Skip '[' character
  4108.          int     21h
  4109.          jz      e_exit                  ; If no key, ANSI not loaded
  4110.          mov     ah, 6                   ; Get 1st digit of cursor row
  4111.          int     21h
  4112.          jz      e_exit                  ; If no key, ANSI not loaded
  4113.          mov     bh, al                  ; Store in BH
  4114.          mov     ah, 6                   ; Get 2nd digit of cursor row
  4115.          int     21h
  4116.          jz      e_exit                  ; If no key, ANSI not loaded
  4117.          mov     bl, al                  ; Store in BL
  4118.          mov     al, ch                  ; Get original row # in AL
  4119.          cbw                             ; AX = row # from GetCurPos
  4120.          inc     ax                      ; Add 1 to it
  4121.          call    IntToAsc                ; Make ASCII digits
  4122.          cmp     ax, bx                  ; ANSI and BIOS reports match?
  4123.          jne     e_exit                  ; No?  Then ANSI not loaded
  4124.  
  4125.          mov     ax, 0C06h               ; Flush remaining ANSI keys
  4126.          mov     dl, 0FFh                ;   from buffer
  4127.          int     21h
  4128.          mov     ax, 1                   ; Set 1 for true
  4129.          jmp     exit                    ;   and exit
  4130.  e_exit:
  4131.          sub     ax, ax                  ; Set 0 return code if no
  4132.  exit:
  4133.          ret                             ;   ANSI driver installed
  4134.  
  4135.  VeriAnsi ENDP
  4136.  
  4137.  
  4138.  ;* VeriCop - Checks for coprocessor.
  4139.  ;*
  4140.  ;* Shows:   BIOS Interrupt - 11h (Get Equipment Configuration)
  4141.  ;*
  4142.  ;* Params:  None
  4143.  ;*
  4144.  ;* Return:  Short integer, 1 for yes or 0 for no
  4145.  
  4146.  VeriCop PROC
  4147.  
  4148.          int     11h                     ; Check peripherals
  4149.          test    al, 2                   ; Coprocessor?
  4150.          mov     ax, 0                   ; Assume no, don't alter flags
  4151.          .IF     !zero?
  4152.          inc     ax                      ; Set to 1
  4153.          .ENDIF
  4154.          ret
  4155.  
  4156.  VeriCop ENDP
  4157.  
  4158.  
  4159.  ;* SetLineMode - Sets line mode for EGA or VGA.
  4160.  ;*
  4161.  ;* Shows:   BIOS Interrupt - 10h, Function 11h (Character Generator Interface
  4162.  ;*                           10h, Function 12h (Video Subsystem Configuration
  4163.  ;*
  4164.  ;* Uses:    vconfig - Video configuration structure (initialized
  4165.  ;*          by calling the GetVidConfig procedure)
  4166.  ;*
  4167.  ;* Params:  Line - Requested line mode (25, 43, or 50)
  4168.  ;*
  4169.  ;* Return:  Short integer with error code
  4170.  ;*          0 if successful
  4171.  ;*          1 if error
  4172.  
  4173.  SetLineMode PROC,
  4174.          Line:WORD
  4175.  
  4176.          .IF     vconfig.adapter >= EGA  ; If EGA or VGA:
  4177.          mov     ax, Line                ; Check for valid parameter
  4178.          cmp     al, 25
  4179.          je      line25
  4180.          cmp     al, 43
  4181.          je      line43
  4182.          cmp     al, 50
  4183.          je      line50
  4184.          jmp     e_exit                  ; If not 25, 43, or 50, exit w/ error
  4185.  line25:
  4186.          mov     al, 11h                 ; Set for EGA 25-line mode
  4187.          cmp     vconfig.adapter, EGA    ; EGA?
  4188.          je      linemode                ; Yes?  Continue
  4189.          mov     ax, 1202h               ; No?  Function 12h for VGA
  4190.          mov     bl, 30h                 ; AL = 2 for 400 scan lines
  4191.          int     10h                     ; Reset to 400 scan lines
  4192.          mov     ax, 0003                ; Reset mode (Function 0)
  4193.          int     10h                     ;   to mode 3 (80-col text)
  4194.          mov     al, 14h                 ; Request 8x16 char matrix
  4195.          jmp     linemode
  4196.  line43:
  4197.          mov     al, 12h                 ; Set for EGA 43-line mode
  4198.          cmp     vconfig.adapter, EGA    ; EGA?
  4199.          je      linemode                ; Yes?  Continue
  4200.          mov     ax, 1201h               ; No?  Function 12h for VGA
  4201.          mov     bl, 30h                 ; AL = 1 for 350 scan lines
  4202.          int     10h                     ; Reset to 350 scan lines
  4203.          mov     ax, 0003                ; Reset mode (Function 0)
  4204.          int     10h                     ;   to mode 3 (80-col text)
  4205.          mov     al, 12h                 ; Request 8x8 character matrix
  4206.          jmp     linemode
  4207.  line50:
  4208.          cmp     vconfig.adapter, VGA    ; VGA?
  4209.          jne     e_exit                  ; No?  Exit with error
  4210.          mov     ax, 1202h               ; Yes?  Function 12h
  4211.          mov     bl, 30h                 ; AL = 2 for 400 scan lines
  4212.          int     10h                     ; Reset to 400 scan lines
  4213.          mov     ax, 0003                ; Reset mode (Function 0)
  4214.          int     10h                     ;   to mode 3 (80-col text)
  4215.          mov     al, 12h                 ; Request 8x8 character matrix
  4216.  linemode:
  4217.          sub     bl, bl                  ; Use table 0
  4218.          mov     ah, 11h                 ; Request Function 11h
  4219.          int     10h                     ; Set new line mode
  4220.  
  4221.          mov     ah, 12h                 ; Select alternate print
  4222.          mov     bl, 20h                 ;    screen for EGA and VGA
  4223.          int     10h
  4224.  
  4225.          cmp     vconfig.adapter, VGA    ; VGA?
  4226.          je      exit                    ; Yes?  Then exit
  4227.          cmp     Line, 12h               ; If EGA 43-line mode, set
  4228.          je      port                    ;   cursor through port to
  4229.                                          ;   avoid cursor emulation bug
  4230.  
  4231.          ; Set normal cursor size, pass top and bottom scan lines
  4232.          INVOKE  SetCurSize, 6, 7
  4233.          jmp     exit
  4234.  port:
  4235.          mov     dx, 03D4h               ; Video controller address
  4236.          mov     ax, 060Ah               ; Set AH = 06h (cursor start)
  4237.                                          ;     AL = 0Ah (register #)
  4238.          out     dx, ax                  ; Update port
  4239.          mov     ax, 000Bh               ; Set AH = 00h (cursor end)
  4240.                                          ;     AL = 0Bh (register #)
  4241.          out     dx, ax                  ; Update port
  4242.          jmp     exit                    ; Normal exit
  4243.          .ENDIF  ; EGA or VGA
  4244.  e_exit:
  4245.          mov     ax, 1                   ; Set error code
  4246.          jmp     exit2
  4247.  exit:
  4248.          sub     ax, ax                  ; Clear error code
  4249.  exit2:
  4250.          ret
  4251.  
  4252.  SetLineMode ENDP
  4253.  
  4254.  
  4255.  ;* Pause - Waits for specified number of clocks to elapse, then returns.
  4256.  ;*
  4257.  ;* Shows:   BIOS Interrupt - 1Ah, Function 0 (Real-Time Clock Driver)
  4258.  ;*          Operators - LOCAL     []
  4259.  ;*
  4260.  ;* Params:  Duration - Desired duration in clocks, where
  4261.  ;*                     18 clocks = approx 1 second
  4262.  ;*
  4263.  ;* Return:  None
  4264.  
  4265.  Pause   PROC,
  4266.          Duration:WORD
  4267.  
  4268.          LOCAL tick:DWORD
  4269.  
  4270.          sub     ah, ah
  4271.          int     1Ah                     ; Get Clock Count in CX:DX
  4272.          add     dx, Duration            ; Add pause time to it
  4273.          adc     cx, 0
  4274.          mov     WORD PTR tick[0], dx    ; Result is target time;
  4275.          mov     WORD PTR tick[2], cx    ;   keep in local variable
  4276.  
  4277.          .REPEAT
  4278.          int     1Ah                     ; Poll clock until target time
  4279.          .UNTIL  (dx >= WORD PTR tick[0]) || (cx >= WORD PTR fileinfo.time[2])
  4280.          ret
  4281.  
  4282.  Pause   ENDP
  4283.  
  4284.  
  4285.  ;* Sound - Sounds speaker with specified frequency and duration.
  4286.  ;*
  4287.  ;* Shows:   Instructions - in           out
  4288.  ;*
  4289.  ;* Params:  Freq - Desired frequency of sound in Hertz
  4290.  ;*          Duration - Desired duration in clocks, where
  4291.  ;*                     18 clocks = approx 1 second
  4292.  ;*
  4293.  ;* Return:  None
  4294.  
  4295.  Sound   PROC,
  4296.          Freq:WORD, Duration:WORD
  4297.  
  4298.          mov     al, 0B6h                ; Initialize channel 2 of
  4299.          out     43h, al                 ;   timer chip
  4300.          mov     dx, 12h                 ; Divide 1,193,182 Hertz
  4301.          mov     ax, 34DEh               ;   (clock frequency) by
  4302.          div     Freq                    ;   desired frequency
  4303.                                          ; Result is timer clock count
  4304.          out     42h, al                 ; Low byte of count to timer
  4305.          mov     al, ah
  4306.          out     42h, al                 ; High byte of count to timer
  4307.          in      al, 61h                 ; Read value from port 61h
  4308.          or      al, 3                   ; Set first two bits
  4309.          out     61h, al                 ; Turn speaker on
  4310.  
  4311.          ; Pause, pass duration of delay
  4312.          INVOKE  Pause, Duration
  4313.  
  4314.          in      al, 61h                 ; Get port value
  4315.          xor     al, 3                   ; Kill bits 0-1 to turn
  4316.          out     61h, al                 ;   speaker off
  4317.          ret
  4318.  
  4319.  Sound   ENDP
  4320.  
  4321.  
  4322.  ;* WriteTTY - Displays ASCIIZ string at cursor position, in either text
  4323.  ;* or graphics mode.
  4324.  ;*
  4325.  ;* Shows:   BIOS Interrupt - 10h, Function 0Eh (Write Character in TTY Mode)
  4326.  ;*
  4327.  ;* Uses:    vconfig - Video configuration structure (initialized
  4328.  ;*          by calling the GetVidConfig procedure)
  4329.  ;*
  4330.  ;* Params:  Sptr - Pointer to ASCIIZ string
  4331.  ;*          icolor - Color index (for graphics mode only)
  4332.  ;*
  4333.  ;* Return:  None
  4334.  
  4335.  WriteTTY PROC USES ds si,
  4336.          Sptr:PBYTE, icolor:WORD
  4337.  
  4338.          mov     bx, icolor              ; BL = color index
  4339.          mov     bh, vconfig.dpage       ; BH = current display page
  4340.          LoadPtr ds, si, Sptr
  4341.          mov     cx, -1                  ; Set loop counter to maximum
  4342.          mov     ah, 14                  ; Function 14
  4343.  
  4344.          .REPEAT
  4345.          lodsb                           ; Get character from string
  4346.          .BREAK .IF al == 0              ; Exit if NULL string terminator
  4347.          int     10h                     ; No?  Display, advance cursor
  4348.          .UNTILCXZ
  4349.  
  4350.          ret
  4351.  
  4352.  WriteTTY ENDP
  4353.  
  4354.  
  4355.  ;* Colors - Alters screen colors within a specified area by using bit
  4356.  ;* or move operations on display attribute bytes in video memory.
  4357.  ;*
  4358.  ;* Shows:   Instructions - not     rol     ror     and     xor     or
  4359.  ;*
  4360.  ;* Params:  Logic - Code number, 0 = NOT    2 = ROR     4 = XOR    6 = MOV
  4361.  ;*                               1 = ROL    3 = AND     5 = OR
  4362.  ;*          Attr - Attribute mask
  4363.  ;*          Row1 - Row at top of window
  4364.  ;*          Col1 - Column at left edge of window
  4365.  ;*          Row2 - Row at bottom of window
  4366.  ;*          Col2 - Column at right edge of window
  4367.  ;*
  4368.  ;* Return:  None
  4369.  
  4370.  Colors  PROC USES ds si,
  4371.          Logic:WORD, Attr:WORD, Row1:WORD, Col1:WORD, Row2:WORD, Col2:WORD
  4372.  
  4373.          GetVidOffset Row1, Col1         ; Get offset in video segment
  4374.          inc     ax
  4375.          mov     si, ax                  ; SI = offset for 1st attr byte
  4376.          mov     bx, Row2
  4377.          sub     bx, Row1
  4378.          inc     bx                      ; BX = number of window rows
  4379.          mov     cx, Col2
  4380.          sub     cx, Col1
  4381.          inc     cx                      ; CX = number of columns
  4382.  
  4383.          mov     ds, vconfig.sgmnt       ; DS = video segment
  4384.          mov     ax, Attr                ; AL = mask for and, xor, and or
  4385.  
  4386.          .REPEAT
  4387.          push    si                      ; Save ptr to start of line
  4388.          push    cx                      ;   and number of columns
  4389.  
  4390.  ; Disable CGA video prior to memory access to avoid screen snow. (See the
  4391.  ; WinOpen and StrWrite procedures for further discussions on CGA snow.)
  4392.  
  4393.          .IF     vconfig.adapter == CGA  ; If CGA adapter:
  4394.          INVOKE  DisableCga              ; Yes?  Disable video
  4395.          .ENDIF
  4396.  
  4397.          cmp     Logic, 1                ; Rotate left?
  4398.          jl      c_not                   ; If less, do NOT
  4399.          je      c_rol                   ; If equal, do ROL
  4400.          cmp     Logic, 3                ; And?
  4401.          jl      c_ror                   ; If less, do ROR
  4402.          je      c_and                   ; If equal, do AND
  4403.          cmp     Logic, 5                ; Or?
  4404.          jl      c_xor                   ; If less, do XOR
  4405.          je      c_or                    ; If equal, do OR
  4406.  c_mov:
  4407.          mov     BYTE PTR [si], al       ; MOV attr parameter
  4408.          add     si, 2                   ;   into attribute byte
  4409.          loop    c_mov
  4410.          jmp     c_done
  4411.  c_or:
  4412.          or      BYTE PTR [si], al       ; OR with attr parameter
  4413.          add     si, 2
  4414.          loop    c_or
  4415.          jmp     c_done
  4416.  c_xor:
  4417.          xor     BYTE PTR [si], al       ; XOR with attr parameter
  4418.          add     si, 2
  4419.          loop    c_xor
  4420.          jmp     c_done
  4421.  c_and:
  4422.          and     BYTE PTR [si], al       ; AND with attr parameter
  4423.          add     si, 2
  4424.          loop    c_and
  4425.          jmp     c_done
  4426.  c_ror:
  4427.          ror     BYTE PTR [si], 1        ; Rotate right 1 bit
  4428.          add     si, 2
  4429.          loop    c_ror
  4430.          jmp     c_done
  4431.  c_rol:
  4432.          rol     BYTE PTR [si], 1        ; Rotate left 1 bit
  4433.          add     si, 2
  4434.          loop    c_rol
  4435.          jmp     c_done
  4436.  c_not:
  4437.          not     BYTE PTR [si]           ; Flip bits
  4438.          add     si, 2
  4439.          loop    c_not
  4440.  c_done:
  4441.          .IF     vconfig.adapter == CGA  ; If CGA:
  4442.          INVOKE  EnableCga               ; Reenable CGA video
  4443.          .ENDIF
  4444.  
  4445.          pop     cx                      ; Recover number of columns
  4446.          pop     si                      ; Recover offset for start of line
  4447.          add     si, 160                 ; Point to start of next line
  4448.          dec     bx                      ; Decrement row counter
  4449.          .UNTIL  zero?                   ; Loop while rows remain
  4450.          ret
  4451.  
  4452.  Colors  ENDP
  4453.  
  4454.  
  4455.  ;* Exec - Executes a child process.  Exec handles the usual chores associated
  4456.  ;* with spawning a process:  (1) parsing the command line tail and loading th
  4457.  ;* FCBs with the first two arguments; (2) setting and restoring the vectors
  4458.  ;* for Interrupts 1Bh, 23h, and 24h; and (3) querying DOS for the child's
  4459.  ;* return code.
  4460.  ;*
  4461.  ;* Shows:   DOS Functions - 29h (Parse Filename)
  4462.  ;*                          25h (Set Interrupt Vector)
  4463.  ;*                          35h (Get Interrupt Vector)
  4464.  ;*                          4Bh (Execute Program)
  4465.  ;*                          4Dh (Get Return Code)
  4466.  ;*
  4467.  ;* Params:  Spec - Pointer to ASCIIZ specification for program file
  4468.  ;*                 (must include .COM or .EXE extension)
  4469.  ;*          Block - Pointer to parameter block structure
  4470.  ;*          CtrBrk - Pointer to new Ctrl+Break (Interrupt 1Bh) handler
  4471.  ;*          CtrlC - Pointer to new Ctrl+C (Interrupt 23h) handler
  4472.  ;*          Criterr - Pointer to new Critical Error (Interrupt 24h) handler
  4473.  ;*
  4474.  ;* Return:  Short integer with child return code, or -1 for EXEC error
  4475.  
  4476.  Exec    PROC USES ds si di,
  4477.          Spec:PBYTE, Block:PPARMBLK, CtrBrk:PTR FAR,
  4478.          CtrlC:PTR FAR, Criterr:PTR FAR
  4479.  
  4480.          Vector 1Bh, Old1Bh, CtrBrk      ; Save, replace Int 1Bh vector
  4481.          Vector 23h, Old23h, CtrlC       ; Save, replace Int 23h vector
  4482.          Vector 24h, Old24h, Criterr     ; Save, replace Int 24h vector
  4483.  
  4484.          LoadPtr ds, bx, Block           ; Point DS:BX to parameter block
  4485.          push    ds                      ; Save segment address
  4486.          les     di, (PARMBLK PTR [bx]).fcb1    ; Point ES:DI to first FCB
  4487.          lds     si, (PARMBLK PTR [bx]).taddr   ; Point DS:SI to command line
  4488.          inc     si                      ; Skip over count byte
  4489.  
  4490.          mov     ax, 2901h               ; Set AH to request Function 29h
  4491.                                          ; AL = flag to skip leading blanks
  4492.          int     21h                     ; Parse command-line into first FCB
  4493.          pop     es                      ; Recover seg addr of parameter block
  4494.          les     di, (PARMBLK PTR es:[bx]).fcb2   ; Point ES:DI to second FCB
  4495.          mov     ax, 2901h               ; Request DOS Function #29h again
  4496.          int     21h                     ; Parse command-line into second FCB
  4497.  
  4498.          push    bp                      ; Save only important register
  4499.          mov     WORD PTR cs:OldStk[0], sp
  4500.          mov     WORD PTR cs:OldStk[2], ss
  4501.          LoadPtr es, bx, Block           ; ES:BX points to param block
  4502.          LoadPtr ds, dx, Spec            ; DS:DX points to path spec
  4503.          mov     ax, 4B00h               ; AH = DOS Function 4Bh
  4504.                                          ; AL = 0 for load and execute
  4505.          int     21h                     ; Execute Program
  4506.          mov     sp, WORD PTR cs:OldStk[0] ; Reset stack pointers
  4507.          mov     ss, WORD PTR cs:OldStk[2]
  4508.          pop     bp                      ; Recover saved register
  4509.  
  4510.  ; Restore vectors for Interrupts 1Bh, 23h, and 24h.
  4511.  
  4512.          mov     ax, 251Bh               ; AH = DOS Function 25h
  4513.                                          ; AL = interrupt number
  4514.          lds     dx, cs:Old1Bh           ; DS:DX = original vector
  4515.          int     21h                     ; Set Interrupt 1Bh Vector
  4516.          mov     al, 23h                 ; AL = interrupt number
  4517.          lds     dx, cs:Old23h           ; DS:DX = original vector
  4518.          int     21h                     ; Set Interrupt 23h Vector
  4519.          mov     al, 24h                 ; AL = interrupt number
  4520.          lds     dx, cs:Old24h           ; DS:DX = original vector
  4521.          int     21h                     ; Set Interrupt 24h Vector
  4522.  
  4523.          mov     ax, -1                  ; Set error code
  4524.          .IF     !carry?                 ; If no EXEC error:
  4525.          mov     ah, 4Dh                 ; Request child's code
  4526.          int     21h                     ; Get Return Code
  4527.          sub     ah, ah                  ; Make short integer
  4528.          .ENDIF
  4529.          ret
  4530.  
  4531.  Old1Bh  FPVOID  ?                       ; Keep vectors for Interrupts
  4532.  Old23h  FPVOID  ?                       ;   1Bh, 23h, and 24h in code
  4533.  Old24h  FPVOID  ?                       ;   segment, but non-executable
  4534.  OldStk  FPVOID  ?                       ; Keep stack pointer
  4535.  
  4536.  Exec    ENDP
  4537.  
  4538.  
  4539.  ;* BinToHex - Converts binary word to 6-byte hexadecimal number in
  4540.  ;* ASCIIZ string. String is right-justified and includes "h" radix.
  4541.  ;*
  4542.  ;* Shows:   Instruction - xlat
  4543.  ;*
  4544.  ;* Params:  Num - Number to convert to hex string
  4545.  ;*          Sptr - Pointer to 6-byte string
  4546.  ;*
  4547.  ;* Return:  None
  4548.  
  4549.          .DATA
  4550.  hex     BYTE    "0123456789ABCDEF"      ; String of hex numbers
  4551.  
  4552.          .CODE
  4553.  BinToHex PROC USES di,
  4554.          Num:WORD, Sptr:PBYTE
  4555.  
  4556.          LoadPtr es, di, Sptr            ; Point ES:DI to 6-byte string
  4557.          mov     bx, OFFSET hex          ; Point DS:BX to hex numbers
  4558.          mov     ax, Num                 ; Number in AX
  4559.          mov     cx, 2                   ; Loop twice for two bytes
  4560.  
  4561.          .REPEAT
  4562.          xchg    ah, al                  ; Switch bytes
  4563.          push    ax                      ; Save number
  4564.          shr     al, 1                   ; Shift high nibble to low
  4565.          shr     al, 1
  4566.          shr     al, 1
  4567.          shr     al, 1
  4568.          xlat                            ; Get equivalent ASCII number in AL
  4569.          stosb                           ; Copy to 6-byte string, increment DI
  4570.          pop     ax                      ; Recover number
  4571.          push    ax                      ; Save it again
  4572.          and     al, 00001111y           ; Mask out high nibble
  4573.          xlat                            ; Get equivalent ASCII number in AL
  4574.          stosb                           ; Copy to 6-byte string, increment DI
  4575.          pop     ax                      ; Recover number
  4576.          .UNTILCXZ                       ; Do next byte
  4577.          mov     ax, 'h'                 ; Put null, 'h' radix in AX
  4578.          stosw                           ; Copy to last two bytes in string
  4579.          ret
  4580.  
  4581.  BinToHex ENDP
  4582.  
  4583.  
  4584.  ;* NewBlockSize - Adjusts size of allocated memory block.
  4585.  ;*
  4586.  ;* Shows:   DOS Function - 4Ah (Resize Memory Block)
  4587.  ;*
  4588.  ;* Params:  Adr - Segment address of block
  4589.  ;*          Resize - Requested block size in paragraphs
  4590.  ;*
  4591.  ;* Return:  Short integer error code
  4592.  ;*          0 if successful
  4593.  ;*          1 if error
  4594.  
  4595.  NewBlockSize PROC,
  4596.          Adr:WORD, Resize:WORD
  4597.  
  4598.          mov     ax, Adr                 ; Get block address
  4599.          mov     es, ax                  ; Point ES to block
  4600.          mov     bx, Resize              ; New block size
  4601.          mov     ah, 4Ah                 ; Function number
  4602.          int     21h                     ; Resize Memory Block
  4603.          ret
  4604.  
  4605.  NewBlockSize ENDP
  4606.  
  4607.  
  4608.  ;* Initialize - Initializes global variables _psp and _env, which are defined
  4609.  ;* in the DEMO.INC include file. If used with a DOS version less than 3.0,
  4610.  ;* this procedure will not produce valid results unless it is called before
  4611.  ;* changing the ES register. This is because at program entry ES points to
  4612.  ;* the Program Segment Prefix (PSP).
  4613.  ;*
  4614.  ;* Params:  None
  4615.  ;*
  4616.  ;* Return:  None
  4617.  
  4618.  Initialize PROC
  4619.  
  4620.          INVOKE  GetPSP                  ; Get segment address of PSP
  4621.          .IF     ax == 0                 ; If less than DOS 3.0:
  4622.          mov     es, ax                  ; Reload ES with PSP address
  4623.          .ENDIF
  4624.  
  4625.          mov     _psp, es                ; Initialize variable with PSP addres
  4626.          mov     ax, es:[2Ch]            ; Get environment seg from PSP
  4627.          mov     _env, ax                ; Store it
  4628.          ret
  4629.  
  4630.  Initialize ENDP
  4631.  
  4632.          END
  4633.  
  4634.  
  4635.  MISCDEMO.ASM
  4636.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\DEMOS\MISCDEMO.ASM
  4637.  
  4638.  ;* MISCDEMO - Invokes many of the assembly example procedures, most of them
  4639.  ;* demonstrating assembly language instructions and calls to the system BIOS.
  4640.  ;* MISCDEMO demonstrates how to:
  4641.  ;*
  4642.  ;*         -   determine hardware information
  4643.  ;*         -   display time and date while waiting for keystrokes
  4644.  ;*         -   play notes of any frequency on the speaker
  4645.  ;*         -   change the line mode for EGA or VGA systems
  4646.  ;*         -   create non-destructive pop-up windows
  4647.  ;*         -   execute another program as a child process
  4648.  ;*         -   create primitive handlers for Interrupts 1Bh, 23h, and 24h
  4649.  ;*         -   use C-callable procedures in assembly programs
  4650.  ;*         -   use simplified segment directives
  4651.  ;*         -   write model-independent procedures
  4652.  ;*         -   declare and initialize data with DUP, BYTE, WORD, and DWORD
  4653.  ;*         -   create structures with the STRUCT directive
  4654.  ;*         -   declare macros
  4655.  ;*         -   set up a dispatch table
  4656.  ;*
  4657.  ;* MISCDEMO.EXE is built from the following files:
  4658.  ;*    MISCDEMO.ASM - Main program
  4659.  ;*    MISC.ASM     - Assembly procedures for MISCDEMO
  4660.  ;*    COMMON.ASM   - Assembly procedures shared by other example programs
  4661.  ;*    DEMO.INC     - Include file with macros, structure declarations
  4662.  ;*
  4663.  ;* Procedures:  GetVidConfig    GetCurPos       VeriPrint       GetPSP
  4664.  ;*              WinOpen         VeriAnsi        VeriCop         GetVer
  4665.  ;*              WinClose        StrWrite        SetLineMode     NewBlockSize
  4666.  ;*              SetCurSize      GetKeyClock     BinToHex        IntToAsc
  4667.  ;*              SetCurPos       GetShift        Sound           Colors
  4668.  ;*              GetCurSize      GetMem          Pause           Exec
  4669.  ;*              WriteTTY        Initialize
  4670.  
  4671.          .DOSSEG
  4672.          .MODEL  small, pascal, os_dos
  4673.          INCLUDE demo.inc
  4674.  
  4675.  NewBreak        PROTO   FAR
  4676.  NewCtrlC        PROTO   FAR
  4677.  NewCritErr      PROTO   FAR
  4678.  DispMenu        PROTO   NEAR
  4679.  Press           PROTO   NEAR
  4680.  GetVidinfo      PROTO   NEAR
  4681.  GetMemInfo      PROTO   NEAR
  4682.  CheckPrinter    PROTO   NEAR
  4683.  CheckAnsi       PROTO   NEAR
  4684.  CheckCoproc     PROTO   NEAR
  4685.  GetConfig       PROTO   NEAR
  4686.  Speaker         PROTO   NEAR
  4687.  SetLines        PROTO   NEAR
  4688.  PopWindows      PROTO   NEAR
  4689.  SetAttrs        PROTO   NEAR
  4690.  ExecPgm         PROTO   NEAR
  4691.  
  4692.          .STACK
  4693.          .DATA
  4694.  
  4695.  PGMSIZE EQU     500h                    ; Maximum program size in paragraphs
  4696.  F1      EQU     59                      ; Extended code for first option key
  4697.  F7      EQU     65                      ; Extended code for last option key
  4698.  CLKROW  EQU     0                       ; Row for on-screen clock
  4699.  CLKCOL  EQU     62                      ; Column for on-screen clock
  4700.  
  4701.  ;* Box - Macro to color portion of screen for effect. Not to be confused with
  4702.  ;* the WinOpen procedure, which is far more capable.
  4703.  ;*
  4704.  ;* Params:  Row1 - Screen row at top of box
  4705.  ;*          Col1 - Screen column at left side of box
  4706.  ;*          Row2 - Screen row at bottom of box
  4707.  ;*          Col2 - Screen column at right side of box
  4708.  
  4709.  Box MACRO Row1, Col1, Row2, Col2
  4710.      LOCAL sk
  4711.      mov ax, 0600h                       ;; Scroll service
  4712.      mov bh, Filmono                     ;; Fill attribute
  4713.      .IF vconfig.adapter != MDA          ;; If color:
  4714.      mov bh, Filcolr                     ;; Use color fill attribute
  4715.      .ENDIF
  4716.      mov ch, Row1
  4717.      mov cl, Col1                        ;; CX = row/col for upper left
  4718.      mov dh, Row2
  4719.      mov dl, Col2                        ;; DX = row/col for lower right
  4720.      int 10h                             ;; Blank window area on screen
  4721.  ENDM
  4722.  
  4723.  OldMode BYTE    ?                       ; Original video mode
  4724.  OldCurs WORD    ?                       ; Original cursor coordinates
  4725.  KeepSeg PSEG    ?                       ; Segment addr, orig screen
  4726.  Filcolr BYTE    1Fh, 20h, 3Bh, 4Eh      ; Color fill attributes
  4727.  Filmono BYTE    70h, 89h, 78h, 1        ; Monochrome fill attributes
  4728.  Fill    BYTE    7                       ; Default attribute for menu
  4729.  Filsub  BYTE    ?                       ; Fore/background colors in submenu
  4730.  
  4731.  PresMsg BYTE    ". . . press a key to continue", 0
  4732.  yes     BYTE    "yes"
  4733.  no      BYTE    "no "
  4734.  
  4735.  ; Main menu text
  4736.  
  4737.  Menu1   BYTE    "***  MISC Demonstration Program  ***", 0
  4738.  Menu2   BYTE    "F1  System Configuration", 0
  4739.  Menu3   BYTE    "F2  Speaker Test", 0
  4740.  Menu4   BYTE    "F3  Toggle Line Mode", 0
  4741.  Menu5   BYTE    "F4  Windows", 0
  4742.  Menu6   BYTE    "F5  Screen Colors", 0
  4743.  Menu7   BYTE    "F6  Exec Program", 0
  4744.  Menu8   BYTE    "Select an option, or press ESC to quit:", 0
  4745.  
  4746.  ; Option F1 - System Configuration
  4747.  
  4748.  MonoStr BYTE    "monochrome"
  4749.  ClrStr  BYTE    "color     "
  4750.  AdapStr BYTE    "MDA CGA MCGAEGA VGA "
  4751.  VidMsg1 BYTE    "Adapter:                 xxxx", 0
  4752.  VidMsg2 BYTE    "Display:                 xxxxxxxxxx", 0
  4753.  VidMsg3 BYTE    "Mode:                    xx", 0
  4754.  VidMsg4 BYTE    "Rows:                    xx", 0
  4755.  MemMsg1 BYTE    "Total memory:            xxxx Kb", 0
  4756.  MemMsg2 BYTE    "Available memory:        xxxx Kb", 0
  4757.  PrnMsg  BYTE    "Printer ready:           xxx", 0
  4758.  AnsiMsg BYTE    "ANSI driver installed:   xxx", 0
  4759.  CopMsg  BYTE    "Coprocessor installed:   xxx", 0
  4760.  LEN1    EQU     LENGTHOF CopMsg - 4
  4761.  
  4762.  ; Option F3 - Toggle Line Mode
  4763.  
  4764.  LineMsg BYTE    "Line mode reset available only for EGA or VGA", 0
  4765.  
  4766.  ; Option F4 - Windows
  4767.  
  4768.  WinMsg  BYTE    "WINDOW x", 0
  4769.  LEN3    EQU     LENGTHOF WinMsg - 2
  4770.  
  4771.  ; Option F5  Screen Colors
  4772.  
  4773.  CMsg1   BYTE    "Toggle                   Step", 0
  4774.  CMsg2   BYTE    "────────────────         ──────────────────", 0
  4775.  CMsg3   BYTE    "B  blink                 ", 27, 26, "  foreground", 0
  4776.  CMsg4   BYTE    "I  intensity             ", 24, 25, "  background", 0
  4777.  CMsg5   BYTE    "Foreground:  press F, then color number 0-7", 0
  4778.  CMsg6   BYTE    "Background:  press A, then color number 0-7", 0
  4779.  CMsg7   BYTE    "Color Numbers", 0
  4780.  CMsg8   BYTE    "───────────────────────────────────────────", 0
  4781.  CMsg9   BYTE    "0  black                     4  red", 0
  4782.  CMsg10  BYTE    "1  blue                      5  magenta", 0
  4783.  CMsg11  BYTE    "2  green                     6  brown", 0
  4784.  CMsg12  BYTE    "3  cyan                      7  white", 0
  4785.  CMsg13  BYTE    "Toggle", 0
  4786.  CMsg14  BYTE    "───────────────", 0
  4787.  CMsg15  BYTE    "B  blink", 0
  4788.  CMsg16  BYTE    "I  intensity", 0
  4789.  CMsg17  BYTE    "U  underline", 0
  4790.  CMsg18  BYTE    "R  reverse", 0
  4791.  
  4792.  ; Option F6 - Exec Program
  4793.  
  4794.  RetMsg  BYTE    "Return code:  "
  4795.  Recode  BYTE    6 DUP (?)               ; ASCII string for return code
  4796.  ExecMsg BYTE    "Enter program file spec (including .COM or .EXE):", 0
  4797.  TailMsg BYTE    "Enter command-line argument(s):", 0
  4798.  Fspec   BYTE    50, 50 DUP (?)          ; File specification (max length = 50
  4799.  Tail    BYTE    50, 50 DUP (?)          ; Command-line tail (max length = 50)
  4800.  Fcblk1  BYTE    0                       ; Allocate space for 1st FCB
  4801.          BYTE    11 DUP (0)
  4802.          BYTE    25 DUP (0)
  4803.  Fcblk2  BYTE    0                       ; Allocate space for 2nd FCB
  4804.          BYTE    11 DUP (0)
  4805.          BYTE    25 DUP (0)
  4806.  pb      PARMBLK <>                      ; Parameter block structure
  4807.  
  4808.  ; Initialize dispatch table with offsets for internal procedures.
  4809.  
  4810.  TPROC   TYPEDEF PROTO           ; Procedure type
  4811.  PPROC   TYPEDEF PTR TPROC       ; Pointer to procedure with no arguments
  4812.  
  4813.  ; Table of procedures
  4814.  DispTbl PPROC   GetConfig, Speaker, SetLines,
  4815.                  PopWindows, SetAttrs, ExecPgm
  4816.  
  4817.          .CODE
  4818.          .STARTUP
  4819.  
  4820.          ; Initialize _psp and _env variables
  4821.          INVOKE  Initialize
  4822.  
  4823.          ; Return unused memory to DOS
  4824.          ; Pass PSP segment address and memory block allocated to program
  4825.          INVOKE  NewBlockSize, _psp, PGMSIZE
  4826.  
  4827.          ; Initialize global configuration data
  4828.          INVOKE  GetVidConfig
  4829.  
  4830.          mov     al, vconfig.rows
  4831.          mov     OldMode, al             ; Preserve original line mode
  4832.  
  4833.          ; Get current cursor position
  4834.          INVOKE  GetCurPos
  4835.  
  4836.          mov     OldCurs, ax             ; Store it
  4837.  
  4838.          ; Preserve original screen and put up window
  4839.          ; Pass top, left, bottom, right, and attribute
  4840.          INVOKE  WinOpen, 0, 0, vconfig.rows, 79, 07h
  4841.  
  4842.          mov     KeepSeg, ax             ; Keep segment address
  4843.          .IF     AX == 0                 ; If window not opened successfully:
  4844.          .EXIT   1                       ; Exit with return code = 1
  4845.          .ENDIF
  4846.  
  4847.          .WHILE 1
  4848.  
  4849.          ; Display main menu
  4850.          INVOKE  DispMenu
  4851.  
  4852.          ; Highlight on-screen clock with macro
  4853.          Box CLKROW, CLKCOL-1, CLKROW, CLKCOL + 17
  4854.  
  4855.          ; Poll for keyboard selection while updating time
  4856.          ; Pass row and column
  4857.          INVOKE  GetKeyClock, CLKROW, CLKCOL
  4858.  
  4859.          .BREAK .IF al == ESCAPE         ; Quit loop if Esc key
  4860.  
  4861.          .CONTINUE .IF (ah < F1) || (ah > F7) ; Ignore if not a function
  4862.                                               ;   key between F1 and F7?
  4863.  
  4864.          xchg    al, ah                  ; Yes?  Make AX = AH
  4865.          sub     al, F1                  ; Normalize to 0
  4866.          shl     al, 1                   ; Double to make word index
  4867.          mov     bx, ax                  ; BX = index to table
  4868.  
  4869.          ; Call the current procedure from call table
  4870.          INVOKE  DispTbl[bx]
  4871.  
  4872.          .ENDW                           ; Loop for another key
  4873.  
  4874.          mov     al, OldMode             ; Get original line mode
  4875.          .IF     al != vconfig.rows      ; If not same as current mode:
  4876.  
  4877.          inc     ax                      ; Increment to 25/43/50
  4878.  
  4879.          ; Restore line mode, pass lines
  4880.          INVOKE  SetLineMode, ax
  4881.  
  4882.          .ENDIF
  4883.  
  4884.          ; Restore original screen, pass segment of screen contents
  4885.          INVOKE  WinClose, KeepSeg
  4886.  
  4887.          mov     ax, OldCurs
  4888.  
  4889.          ; Restore cursor to original place
  4890.          ; Pass row and column
  4891.          INVOKE  SetCurPos, BYTE PTR OldCurs[1], BYTE PTR OldCurs[0]
  4892.  
  4893.          .EXIT   0                       ; Exit wih return code 0
  4894.  
  4895.  
  4896.  ;* DispMenu - Displays main menu.
  4897.  ;*
  4898.  ;* Uses:    vconfig - Video configuration structure (initialized
  4899.  ;*          by calling the GetVidConfig procedure)
  4900.  ;*
  4901.  ;* Return:  None
  4902.  
  4903.  DispMenu PROC NEAR
  4904.  
  4905.          mov     ax, 0600h               ; Scroll screen service
  4906.          mov     bh, Fill                ; Menu display attribute
  4907.          sub     cx, cx                  ; From row 0, col 0
  4908.          mov     dh, vconfig.rows        ;   to bottom row,
  4909.          mov     dl, 79                  ;   rightmost column
  4910.          int     10h                     ; Clear entire screen
  4911.  
  4912.          ; Display menu
  4913.          ; For each line pass row, column, and string address
  4914.          INVOKE StrWrite,  4, 21, ADDR Menu1
  4915.          INVOKE StrWrite,  8, 28, ADDR Menu2
  4916.          INVOKE StrWrite,  9, 28, ADDR Menu3
  4917.          INVOKE StrWrite, 10, 28, ADDR Menu4
  4918.          INVOKE StrWrite, 11, 28, ADDR Menu5
  4919.          INVOKE StrWrite, 12, 28, ADDR Menu6
  4920.          INVOKE StrWrite, 13, 28, ADDR Menu7
  4921.          INVOKE StrWrite, 17, 18, ADDR Menu8
  4922.  
  4923.          ; Park cursor at prompt, pass row and column
  4924.          INVOKE  SetCurPos, 17, 18 + (LENGTHOF Menu8) + 2
  4925.  
  4926.          ret
  4927.  
  4928.  DispMenu ENDP
  4929.  
  4930.  
  4931.  
  4932.  ;* Press - Displays a prompt, then waits for a key press.
  4933.  ;*
  4934.  ;* Uses:    vconfig - Video configuration structure (initialized
  4935.  ;*          by calling the GetVidConfig procedure)
  4936.  ;*
  4937.  ;* Return:  None
  4938.  
  4939.  Press   PROC NEAR
  4940.  
  4941.          ; Write string, pass row, column, and string address
  4942.          INVOKE StrWrite, vconfig.rows, 50, ADDR PresMsg
  4943.  
  4944.          ; Park cursor at prompt, pass row and column
  4945.          INVOKE  SetCurPos, vconfig.rows, 48
  4946.  
  4947.          ; Poll for keyboard selection while updating time
  4948.          ; Pass row and column
  4949.          INVOKE  GetKeyClock, CLKROW, CLKCOL
  4950.  
  4951.          ret
  4952.  
  4953.  Press   ENDP
  4954.  
  4955.  
  4956.  
  4957.  ;* GetVidinfo - Initializes video configuration message for display.
  4958.  ;*
  4959.  ;* Uses:    vconfig - Video configuration structure (initialized
  4960.  ;*          by calling the GetVidConfig procedure)
  4961.  ;*
  4962.  ;* Return:  None
  4963.  
  4964.  GetVidinfo PROC NEAR
  4965.  
  4966.          push    ds
  4967.          pop     es                      ; Point ES to data segment
  4968.          mov     al, 4                   ; Find index to 4-character
  4969.          mul     vconfig.adapter         ;   group in string
  4970.          add     ax, OFFSET AdapStr      ; Point AX to proper group
  4971.          mov     si, ax                  ; Put pointer in SI
  4972.          lea     di, VidMsg1[LEN1]       ; Point to 1st line of message
  4973.          mov     cx, 2                   ; Copy 4 letters (adapter
  4974.          rep     movsw                   ;   designation) to message
  4975.  
  4976.          mov     si, OFFSET MonoStr      ; Assume display is monochrome
  4977.          .IF     vconfig.display != MONO ; I color display:
  4978.          mov     si, OFFSET ClrStr       ; Point to "color" string
  4979.          .ENDIF
  4980.          lea     di, VidMsg2[LEN1]       ; Point to 2nd line of message
  4981.          mov     cx, 5                   ; Copy 10 chars ("monochrome"
  4982.          rep     movsw                   ;   or "color     ") to msg
  4983.  
  4984.          ; Note that IntToAsc can't be invoked because of its
  4985.          ; register calling convention
  4986.          mov     al, vconfig.mode
  4987.          cbw                             ; AX = video mode
  4988.          call    IntToAsc                ; Convert AX to ASCII
  4989.          xchg    ah, al                  ; Flip bytes for word write
  4990.          mov     WORD PTR VidMsg3[LEN1], ax  ; Insert in message string
  4991.  
  4992.          mov     al, vconfig.rows
  4993.          cbw
  4994.          inc     ax                      ; AX = number of screen rows
  4995.          call    IntToAsc                ; Convert to ASCII
  4996.          xchg    ah, al                  ; Flip bytes for word write
  4997.          mov     WORD PTR VidMsg4[LEN1], ax  ; Insert in message string
  4998.          ret
  4999.  
  5000.  GetVidinfo ENDP
  5001.  
  5002.  
  5003.  
  5004.  ;* GetMemInfo - Initializes memory information message.
  5005.  ;*
  5006.  ;* Return:  None
  5007.  
  5008.  GetMemInfo PROC NEAR
  5009.  
  5010.          ; Get total memory in DX, available memory in AX
  5011.          INVOKE  GetMem
  5012.  
  5013.          push    ax
  5014.          mov     ax, dx
  5015.          call    IntToAsc                ; Convert AX to ASCII
  5016.          xchg    dh, dl                  ; Flip bytes for word write
  5017.          xchg    ah, al
  5018.          mov     WORD PTR MemMsg1[LEN1], dx      ; Insert in message
  5019.          mov     WORD PTR MemMsg1[LEN1+2], ax    ;   string
  5020.          pop     ax                              ; Recover avail memory #
  5021.          call    IntToAsc                        ; Convert to ASCII
  5022.          xchg    dh, dl                          ; Flip bytes for word write
  5023.          xchg    ah, al
  5024.          mov     WORD PTR MemMsg2[LEN1], dx      ; Insert in message
  5025.          mov     WORD PTR MemMsg2[LEN1+2], ax    ;   string
  5026.          ret
  5027.  
  5028.  GetMemInfo ENDP
  5029.  
  5030.  
  5031.  ;* CheckPrinter - Initializes printer status message.
  5032.  ;*
  5033.  ;* Shows:   Instruction - movsb
  5034.  ;*
  5035.  ;* Return:  None
  5036.  
  5037.  CheckPrinter PROC NEAR
  5038.  
  5039.          push    ds
  5040.          pop     es                      ; Point ES to data segment
  5041.          mov     si, OFFSET yes          ; Assume answer is "yes"
  5042.  
  5043.          ; Check if printer ready
  5044.          INVOKE  VeriPrint
  5045.  
  5046.          .IF     al == 0                 ; If not ready
  5047.          mov     si, OFFSET no           ; Point to "no" answer
  5048.          .ENDIF
  5049.          lea     di, PrnMsg[LEN1]        ; Point to print message
  5050.          mov     cx, 3                   ; Copy 3 letters (either "yes"
  5051.          rep     movsb                   ;   or "no ") to message
  5052.          ret
  5053.  
  5054.  CheckPrinter ENDP
  5055.  
  5056.  
  5057.  
  5058.  ;* CheckAnsi - Initializes status message for ANSI driver.
  5059.  ;*
  5060.  ;* Return:  None
  5061.  
  5062.  CheckAnsi PROC NEAR
  5063.  
  5064.          push    ds
  5065.          pop     es                      ; Point ES to data segment
  5066.          mov     si, OFFSET yes          ; Assume answer is "yes"
  5067.  
  5068.          ; Check if ANSI driver is installed
  5069.          INVOKE  VeriAnsi
  5070.  
  5071.          .IF     al == 0                 ; If not installed:
  5072.          mov     si, OFFSET no           ; Point to "no" answer
  5073.          .ENDIF
  5074.          lea     di, AnsiMsg[LEN1]       ; Point to ansi message
  5075.          mov     cx, 3                   ; Copy 3 letters (either "yes"
  5076.          rep     movsb                   ;   or "no ") to message
  5077.          ret
  5078.  
  5079.  CheckAnsi ENDP
  5080.  
  5081.  
  5082.  
  5083.  ;* CheckCoproc - Initializes coprocessor status message.
  5084.  ;*
  5085.  ;* Return:  None
  5086.  
  5087.  CheckCoproc PROC NEAR
  5088.  
  5089.          push    ds
  5090.          pop     es                      ; Point ES to data segment
  5091.          mov     si, OFFSET yes          ; Assume answer is "yes"
  5092.  
  5093.          ; Check for coprocessor
  5094.          INVOKE  VeriCop
  5095.  
  5096.          .IF     al == 0                 ; If not installed:
  5097.          mov     si, OFFSET no           ; Point to "no" answer
  5098.          .ENDIF
  5099.          lea     di, CopMsg[LEN1]        ; Point to coprocessor message
  5100.          mov     cx, 3                   ; Copy 3 letters (either "yes"
  5101.          rep     movsb                   ;   or "no ") to message
  5102.          ret
  5103.  
  5104.  CheckCoproc ENDP
  5105.  
  5106.  
  5107.  ;* GetConfig - Displays system configuration information.
  5108.  
  5109.  GetConfig PROC NEAR
  5110.  
  5111.          INVOKE  GetVidinfo              ; Initialize video message
  5112.          INVOKE  GetMemInfo              ; Initialize memory message
  5113.          INVOKE  CheckPrinter            ; Initialize printer message
  5114.          INVOKE  CheckAnsi               ; Initialize ANSI driver msg
  5115.          INVOKE  CheckCoproc             ; Initialize coprocessor msg
  5116.  
  5117.          Box 4, 13, 20, 67               ; Clear screen with box
  5118.  
  5119.          ; Display configuration information
  5120.          ; For each line, pass row, column, and string address
  5121.          INVOKE  StrWrite,  6, 23, ADDR VidMsg1
  5122.          INVOKE  StrWrite,  7, 23, ADDR VidMsg2
  5123.          INVOKE  StrWrite,  8, 23, ADDR VidMsg3
  5124.          INVOKE  StrWrite,  9, 23, ADDR VidMsg4
  5125.          INVOKE  StrWrite, 11, 23, ADDR MemMsg1
  5126.          INVOKE  StrWrite, 12, 23, ADDR MemMsg2
  5127.          INVOKE  StrWrite, 14, 23, ADDR PrnMsg
  5128.          INVOKE  StrWrite, 16, 23, ADDR AnsiMsg
  5129.          INVOKE  StrWrite, 18, 23, ADDR CopMsg
  5130.  
  5131.          ; Prompt for keypress
  5132.          INVOKE  Press
  5133.  
  5134.          ret
  5135.  
  5136.  GetConfig ENDP
  5137.  
  5138.  
  5139.  
  5140.  ;* Speaker - Sounds speaker with ascending frequencies.
  5141.  ;*
  5142.  ;* Return:  None
  5143.  
  5144.  Speaker PROC NEAR
  5145.  
  5146.          sub     ax, ax
  5147.          .REPEAT
  5148.          add     ax, 100                 ; Start with frequency 100
  5149.          push    ax                      ; Save frequency
  5150.  
  5151.          ; Beep speaker, pass frequency and duration
  5152.          INVOKE  Sound, ax, 1
  5153.  
  5154.          pop     ax                      ; Recover frequency
  5155.          .UNTIL  ax > 3000               ; Continue to frequency 3000
  5156.          ret
  5157.  
  5158.  Speaker ENDP
  5159.  
  5160.  
  5161.  
  5162.  ;* SetLines - Toggles between 25/43-line mode for EGA or 25/43/50-line mode
  5163.  ;* for VGA.
  5164.  ;*
  5165.  ;* Uses:    vconfig - Video configuration structure (initialized
  5166.  ;*          by calling the GetVidConfig procedure)
  5167.  ;*
  5168.  ;* Return:  None
  5169.  
  5170.  SetLines PROC NEAR
  5171.  
  5172.          mov     al, 25                  ; Assume toggle to 25 line
  5173.          cmp     vconfig.rows, 49        ; Current mode 50 lines?
  5174.          je      toggle25                ; Yes?  Toggle VGA to 25-line
  5175.          cmp     vconfig.rows, 42        ; Current mode 43 lines?
  5176.          jne     toggle43                ; No?  Must be 25
  5177.          cmp     vconfig.adapter, EGA    ; Yes?  And is adapter EGA?
  5178.          je      toggle25                ; Yes?  Then toggle to 25 line
  5179.          mov     al, 50                  ; No?  Toggle VGA to 50 line
  5180.          jmp     toggle25
  5181.  toggle43:
  5182.          mov     al, 43                  ; If currently 25 lines, make
  5183.                                          ;   either EGA or VGA 43 lines
  5184.  toggle25:
  5185.          ; Change line mode, pass lines
  5186.          INVOKE  SetLineMode, ax
  5187.  
  5188.          .IF     al == 0                 ; If no error:
  5189.          INVOKE  GetVidConfig            ; Update configuration structure
  5190.          .ELSE                           ; Else:
  5191.          Box 16, 13, 20, 67              ; Display error message
  5192.  
  5193.          ; Write line message, pass row, column, and string address
  5194.          INVOKE  StrWrite, 18, 17, ADDR LineMsg
  5195.  
  5196.          INVOKE  Press
  5197.          .ENDIF
  5198.  
  5199.          ret
  5200.  
  5201.  SetLines ENDP
  5202.  
  5203.  
  5204.  
  5205.  ;* PopWindows - Demonstrates windowing with the WinOpen and WinClose
  5206.  ;* procedures.
  5207.  ;*
  5208.  ;* Uses:    vconfig - Video configuration structure (initialized
  5209.  ;*          by calling the GetVidConfig procedure)
  5210.  ;*
  5211.  ;* Return:  None
  5212.  
  5213.  PopWindows PROC NEAR
  5214.  
  5215.          LOCAL Row1:WORD, Col1:WORD, Row2:WORD, Col2:WORD
  5216.          LOCAL Index:BYTE, Adr[4]:WORD, Csize:WORD
  5217.  
  5218.          ; Get current cursor size
  5219.          INVOKE  GetCurSize
  5220.  
  5221.          mov     Csize, ax               ; Store it
  5222.          or      al, 100000y             ; Set 5th bit for cursor off
  5223.          mov     bl, al
  5224.  
  5225.          ; Set cursor size
  5226.          ; Pass arbitrary top and bottom lines with visibility bit off
  5227.          INVOKE  SetCurSize, BYTE PTR Csize[1], bl
  5228.  
  5229.          mov     WinMsg[LEN3], "0"       ; Initialize window message
  5230.          mov     Row1, 4                 ; Initialize window coords
  5231.          mov     Col1, 10
  5232.          mov     Row2, 20
  5233.          mov     Col2, 34
  5234.          mov     Index, 0
  5235.          mov     cx, 4                   ; Open 4 windows
  5236.          .REPEAT
  5237.          push    cx                      ; Save loop counter
  5238.          mov     al, Index
  5239.          mov     bx, OFFSET Filmono      ; BX points to fill attributes
  5240.          .IF     vconfig.display != MONO ; If not monochrome:
  5241.          mov     bx, OFFSET Filcolr      ; Repoint to color attributes
  5242.          .ENDIF
  5243.          xlat                            ; Get attributes in succession
  5244.  
  5245.          ; Save old window and open new
  5246.          ; Pass top, left, bottom, right, and attribute in AX
  5247.          INVOKE  WinOpen, Row1, Col1, Row2, Col2, ax
  5248.  
  5249.          pop     di                      ; Recover counter in DI
  5250.          push    di                      ;   and save it again
  5251.          dec     di
  5252.          shl     di, 1                   ; Make DI a word index
  5253.          mov     Adr[di], ax             ; Save address of allocated
  5254.                                          ;   block returned by WinOpen
  5255.          inc     WinMsg[LEN3]            ; Increment window number
  5256.          mov     bx, Row1
  5257.          add     bl, 2                   ; Message row
  5258.          mov     cx, Col1
  5259.          add     cl, 9                   ; Message column
  5260.  
  5261.          ; Write window message, pass row, column, and string address
  5262.          INVOKE StrWrite, bx, cx, ADDR WinMsg
  5263.  
  5264.          ; Pause, pass 18 ticks (about 1 second)
  5265.          INVOKE  Pause, 18
  5266.  
  5267.          add     Row1, 2                 ; Adjust coordinates for
  5268.          add     Col1, 13                ;   next window
  5269.          sub     Row2, 2
  5270.          add     Col2, 13
  5271.          inc     Index
  5272.          pop     cx                      ; Recover counter
  5273.          .UNTILCXZ
  5274.  
  5275.          mov     cx, 4                   ; Close 4 windows
  5276.          sub     di, di                  ; DI = index to addresses
  5277.  
  5278.          .REPEAT
  5279.          push    cx                      ; Save loop counter
  5280.  
  5281.          ; Close a window, pass address of the window
  5282.          INVOKE  WinClose, Adr[di]
  5283.  
  5284.          ; Pause, pass 18 ticks (about 1 second)
  5285.          INVOKE  Pause, 18
  5286.  
  5287.          add     di, 2                   ; Point to next address
  5288.          pop     cx                      ; Recover counter
  5289.          .UNTILCXZ                       ; Close another window
  5290.  
  5291.          mov     ax, Csize               ; Get original cursor size
  5292.  
  5293.          ; Set cursor size, pass top and bottom lines
  5294.          INVOKE  SetCurSize, BYTE PTR Csize[1], BYTE PTR Csize[0]
  5295.  
  5296.          ret
  5297.  
  5298.  PopWindows ENDP
  5299.  
  5300.  
  5301.  
  5302.  ;* SetAttrs - Changes display attributes for the main menu.
  5303.  ;*
  5304.  ;* Uses:    vconfig - Video configuration structure (initialized
  5305.  ;*          by calling the GetVidConfig procedure)
  5306.  ;*
  5307.  ;* Return:  None
  5308.  
  5309.  SetAttrs PROC NEAR
  5310.  
  5311.          Box 3, 12, 23, 68
  5312.          .IF     vconfig.adapter == MDA  ; If monochrome?
  5313.  
  5314.          ; Write monochrome menu
  5315.          ; For each line, pass row, column, and string address
  5316.          INVOKE StrWrite,  8, 32, ADDR CMsg13
  5317.          INVOKE StrWrite,  9, 32, ADDR CMsg14
  5318.          INVOKE StrWrite, 10, 36, ADDR CMsg15
  5319.          INVOKE StrWrite, 11, 36, ADDR CMsg16
  5320.          INVOKE StrWrite, 12, 36, ADDR CMsg17
  5321.          INVOKE StrWrite, 13, 36, ADDR CMsg18
  5322.  
  5323.          mov     al, Filmono             ; Initialize Filsub variable
  5324.          mov     Filsub, al              ;   for monochrome
  5325.  
  5326.          .ELSE
  5327.  
  5328.          ; Write color menu
  5329.          ; For each line, pass row, column, and string address
  5330.          INVOKE StrWrite,  4, 18, ADDR CMsg1
  5331.          INVOKE StrWrite,  5, 18, ADDR CMsg2
  5332.          INVOKE StrWrite,  6, 22, ADDR CMsg3
  5333.          INVOKE StrWrite,  7, 22, ADDR CMsg4
  5334.          INVOKE StrWrite, 10, 18, ADDR CMsg5
  5335.          INVOKE StrWrite, 11, 18, ADDR CMsg6
  5336.          INVOKE StrWrite, 14, 18, ADDR CMsg7
  5337.          INVOKE StrWrite, 15, 18, ADDR CMsg8
  5338.          INVOKE StrWrite, 16, 22, ADDR CMsg9
  5339.          INVOKE StrWrite, 17, 22, ADDR CMsg10
  5340.          INVOKE StrWrite, 18, 22, ADDR CMsg11
  5341.          INVOKE StrWrite, 19, 22, ADDR CMsg12
  5342.  
  5343.          mov     al, Filcolr             ; Initialize Filsub variable
  5344.          mov     Filsub, al              ;   for color
  5345.          .ENDIF
  5346.  
  5347.          ; Write menu message
  5348.          INVOKE StrWrite, 22, 15, ADDR Menu8
  5349.  
  5350.          ; Park cursor at prompt, pass row and column
  5351.          INVOKE  SetCurPos, 22, 56
  5352.  
  5353.          .WHILE   1
  5354.  
  5355.          ; Poll for keyboard selection while updating time
  5356.          ; Pass row and column
  5357.          INVOKE  GetKeyClock, CLKROW, CLKCOL
  5358.  
  5359.          .BREAK .IF al == ESCAPE         ; Quit if Esc key
  5360.  
  5361.          .IF (al >= 'a') && (al <= 'z')  ; Convert letters to uppercase
  5362.          and     al, 5Fh                 ;   to make comparisons easier
  5363.          .ENDIF
  5364.  
  5365.          cmp     al, 'B'                 ; Request blink toggle?
  5366.          je      blink
  5367.          cmp     al, 'I'                 ; Request intensity toggle?
  5368.          je      intense
  5369.          mov     bl, Filsub              ; Get window display attribute
  5370.          cmp     vconfig.adapter, MDA    ; Monochrome?
  5371.          jne     iscolor                 ; No?  Jump to color selections
  5372.          cmp     al, 'U'                 ; Request underline toggle?
  5373.          je      underline
  5374.          .CONTINUE .IF al != 'R'         ; If not reverse toggle:
  5375.                                          ; Skip invalid key
  5376.  
  5377.  ; What with cross-toggling between reverse, normal, and underline, three
  5378.  ; bit settings can exist in monochrome:  x111x000 for reverse, x000x111 for
  5379.  ; normal, and x000x001 for underline. Changing between the three involves
  5380.  ; more than simply XOR-ing the current attribute; each condition must check
  5381.  ; for the other two.
  5382.  
  5383.  reverse:
  5384.          .IF     bl & 1                  ; If reverse video off:
  5385.          or      bl, 00000111y           ; Ensure normal bits are on
  5386.          .ENDIF
  5387.  
  5388.          xor     bl, 01110111y           ; Toggle for reverse/normal
  5389.          mov     cl, 6                   ; Set code for MOV
  5390.          jmp     switch
  5391.  
  5392.  underline:
  5393.          .IF     bl & 1                  ; If reverse video on:
  5394.          and     bl, 10001111y           ; Clear bits 4-6
  5395.          or      bl, 00000111y           ;   and set bits 0-2
  5396.          .ENDIF
  5397.  
  5398.          xor     bl, 00000110y           ; Toggle bits 1-2 for underline
  5399.          mov     cl, 6                   ; Set code for MOV
  5400.          jmp     switch
  5401.  
  5402.  ; Blink and intensity use the same bits for color and monochrome.
  5403.  
  5404.  blink:
  5405.          mov     bl, 10000000y           ; Set bit 7 for blink
  5406.          mov     cl, 4                   ; Set code for XOR
  5407.          jmp     switch
  5408.  
  5409.  intense:
  5410.          mov     bl, 00001000y           ; Set bit 3 for intensity
  5411.          mov     cl, 4                   ; Set code for XOR
  5412.          jmp     switch
  5413.  
  5414.  ; Enter this section only for color displays. First check for arrow keys,
  5415.  ; which increment or decrement the foreground or background bits of the
  5416.  ; current attribute stored in the variable Filsub. If arrow keys are not
  5417.  ; pressed, check for the F or A keys, which request specific colors for the
  5418.  ; foreground or background colors.
  5419.  
  5420.  iscolor:
  5421.          mov     ch, bl                  ; Copy current attribute to CH
  5422.          .IF     ah == 72                ; If up arrow:
  5423.          mov     cl, 4                   ; Increment bits 4-6
  5424.          shr     ch, cl                  ;   to next background color
  5425.          inc     ch
  5426.          and     ch, 00000111y
  5427.          shl     ch, cl
  5428.          mov     dl, 10001111y           ; Set background mask
  5429.          jmp     step
  5430.          .ENDIF
  5431.  
  5432.          .IF     ah == 75                ; If left arrow:
  5433.          inc     ch                      ; Increment bits 0-2
  5434.          and     ch, 00000111y           ;   to next foreground color
  5435.          mov     dl, 11111000y           ; Set foreground mask
  5436.          jmp     step
  5437.          .ENDIF
  5438.  
  5439.          .IF     ah == 77                ; If right arrow
  5440.          dec     ch                      ; Decrement bits 0-2
  5441.          and     ch, 00000111y           ;   to previous foreground color
  5442.          mov     dl, 11111000y           ; Set foreground mask
  5443.          jmp     step
  5444.          .ENDIF
  5445.  
  5446.          .IF     ah == 80                ; If down arrow:
  5447.          mov     cl, 4                   ; Decrement bits 4-6
  5448.          shr     ch, cl                  ;   to previous background color
  5449.          dec     ch
  5450.          and     ch, 00000111y
  5451.          shl     ch, cl
  5452.          mov     dl, 10001111y           ; Set background mask
  5453.  step:
  5454.          and     bl, dl                  ; Mask out fore or back bits
  5455.          or      bl, ch                  ; Copy into original attribute
  5456.          mov     Filsub, bl              ; Store the new submenu color
  5457.          mov     cl, 6                   ; Request move operation in
  5458.          jmp     switch                  ;   Colors procedure
  5459.          .ENDIF
  5460.  
  5461.  ; This section checks for the F or A keys; if found it checks again for
  5462.  ; a number key between 0 and 7, then inserts the correct foreground or
  5463.  ; background bit pattern into the current fill attribute.
  5464.  
  5465.          sub     cx, cx                  ; Clear flag for foreground request
  5466.          .IF     al == 'A'               ; If background request:
  5467.          inc     cx                      ; Set flag for background request
  5468.          .CONTINUE .IF al != 'F'         ; If not foreground request, continue
  5469.          .ENDIF
  5470.  
  5471.          push    ax
  5472.  
  5473.          ; Poll for keyboard selection while updating time
  5474.          ; Pass row and column
  5475.          INVOKE  GetKeyClock, CLKROW, CLKCOL
  5476.  
  5477.          pop     cx                      ; Recover flag
  5478.  
  5479.          .CONTINUE .IF (al < '0') && (al > '7') ; Ignore invalid key
  5480.  
  5481.          xor     al, '0'                 ; Convert ASCII numeral into binary
  5482.          mov     dl, 11111000y           ; Set foreground mask
  5483.          .IF     cx != 0                 ; Skip if foreground request
  5484.          mov     cl, 4                   ; Otherwise shift bits 0-2
  5485.          shl     al, cl                  ;   to positions 4-6
  5486.          mov     dl, 10001111y           ; Set background mask
  5487.          .ENDIF
  5488.  
  5489.          mov     bl, Filsub
  5490.          and     bl, dl                  ; Mask out fore or back bits
  5491.          or      bl, al                  ; Insert number into fore or back bit
  5492.          mov     Filsub, bl              ; Store the new submenu color
  5493.          mov     cl, 6                   ; Request move
  5494.  switch:
  5495.  
  5496.          ; Set new attributes in a window
  5497.          ; Pass logic code (CX), attribute (BX), top, left, bottom, right
  5498.          INVOKE  Colors, cx, bx, 3, 12, 23, 68
  5499.  
  5500.          mov     ah, 8                   ; Function 8, get char/attribute
  5501.          mov     bh, vconfig.dpage
  5502.          int     10h                     ; Get attribute in AH
  5503.          mov     Fill, ah                ; New fill variable for main menu
  5504.          mov     Filsub, ah              ;   and for submenu
  5505.          .ENDW
  5506.          ret
  5507.  
  5508.  SetAttrs ENDP
  5509.  
  5510.  
  5511.  
  5512.  ;* ExecPgm - Executes a specified program as a child process.
  5513.  ;*
  5514.  ;* Uses:    vconfig - Video configuration structure (initialized
  5515.  ;*          by calling the GetVidConfig procedure)
  5516.  ;*          pb - Parameter block structure, declared in the DEMO.INC file
  5517.  ;*
  5518.  ;* Return:  None
  5519.  
  5520.  ExecPgm PROC NEAR
  5521.  
  5522.          Box 16, 13, 20, 67
  5523.  
  5524.          ; Display prompt for file spec, pass row, column, and string address
  5525.          INVOKE StrWrite, 17, 16, ADDR ExecMsg
  5526.  
  5527.          ; Set cursor position below prompt, pass row and column
  5528.          INVOKE  SetCurPos, 19, 16
  5529.  
  5530.          mov     ah, 0Ah                 ; Request DOS to read keyboard
  5531.          mov     dx, OFFSET Fspec        ;   input into Fspec string
  5532.          int     21h                     ; Read Buffered Keyboard Input
  5533.  
  5534.          Box 16, 13, 20, 67
  5535.  
  5536.          ; Display prompt for command tail
  5537.          INVOKE StrWrite, 17, 16, ADDR TailMsg
  5538.  
  5539.          ; Set cursor position below prompt, pass row and column
  5540.          INVOKE  SetCurPos, 19, 16
  5541.  
  5542.          mov     ah, 0Ah                 ; Request DOS to read keyboard
  5543.          mov     dx, OFFSET Tail         ;   input into tail string
  5544.          int     21h                     ; Read Buffered Keyboard Input
  5545.  
  5546.          sub     bh, bh                  ; Clear BH
  5547.          mov     si, OFFSET Fspec        ; DS:SI points to file spec string
  5548.          mov     bl, [si+1]              ; BL = number of chars in spec
  5549.          mov     BYTE PTR [si+bx+2], 0   ; Terminate string with 0
  5550.  
  5551.          mov     ax, _env                ; Get segment address of environment
  5552.          mov     pb.env, ax              ; Copy it to parameter block
  5553.          mov     ax, @data               ; AX points to data segment
  5554.          lea     bx, Tail[1]             ; BX points to command-line tail
  5555.          mov     WORD PTR pb.taddr[0], bx; Copy address of command-line tail
  5556.          mov     WORD PTR pb.taddr[2], ax;   to parameter block
  5557.  
  5558.          mov     bx, OFFSET Fcblk1       ; BX points to first FCB
  5559.          mov     WORD PTR pb.fcb1[0], bx ; Copy address of first FCB
  5560.          mov     WORD PTR pb.fcb1[2], ax ;   to parameter block
  5561.          mov     bx, OFFSET Fcblk2       ; BX points to second FCB
  5562.          mov     WORD PTR pb.fcb2[0], bx ; Copy address of second FCB
  5563.          mov     WORD PTR pb.fcb2[2], ax ;   to parameter block
  5564.  
  5565.  ; At this point, the program file is specified, the command line tail is set,
  5566.  ; and the parameter block is properly initialized. The Exec procedure will
  5567.  ; take care of loading the FCBs with command-line arguments and resetting
  5568.  ; interrupt vectors. Now blank the screen in preparation for executing the
  5569.  ; process and pass the five pointers to the Exec procedure.
  5570.  
  5571.          mov     ax, 0600h               ; AH = scroll service, AL = 0
  5572.          mov     bh, 7                   ; Blank with normal attribute
  5573.          sub     cx, cx                  ; From row 0, col 0
  5574.          mov     dh, vconfig.rows        ;   to bottom row
  5575.          mov     dl, 79                  ;   and rightmost column
  5576.          int     10h                     ; Blank screen
  5577.  
  5578.          ; Set cursor at top of screen, pass row and column
  5579.          INVOKE  SetCurPos, 0, 0
  5580.  
  5581.  
  5582.          ; Exec specified program
  5583.          INVOKE  Exec,
  5584.                  ADDR Fspec[2],          ; File spec
  5585.                  ADDR pb,                ; Parameter block structure
  5586.                  NewBreak,               ; New handlers for CTRL+BREAK,
  5587.                  NewCtrlC,               ;   CTRL+C
  5588.                  NewCritErr              ;   and Critical Error
  5589.  
  5590.  
  5591.          .IF     ax != -1                ; If successful:
  5592.  
  5593.          ; Convert return code to string
  5594.          ; Pass return code (AX) and address of string buffer
  5595.          INVOKE  BinToHex, ax, ADDR Recode
  5596.  
  5597.          ; Update video structure
  5598.          INVOKE  GetVidConfig
  5599.  
  5600.          Box CLKROW, CLKCOL-1, CLKROW, CLKCOL+17 ; Highlight on-screen clock
  5601.          Box vconfig.rows, 0, vconfig.rows, 79   ; Highlight bottom row
  5602.          mov     dl, vconfig.rows
  5603.  
  5604.          ; Display return code at bottom
  5605.          INVOKE StrWrite, dx, 0, ADDR RetMsg
  5606.  
  5607.          ; Wait for keypress
  5608.          INVOKE  Press
  5609.          .ELSE
  5610.          mov     ax, 0E07h               ; Write ASCII 7 character
  5611.          int     10h                     ;   (bell) to console
  5612.          .ENDIF
  5613.  
  5614.          ret
  5615.  
  5616.  ExecPgm ENDP
  5617.  
  5618.  
  5619.  
  5620.  ;* The following three procedures are primitive handlers for Interrupt 1Bh
  5621.  ;* (Ctrl-Break), Interrupt 23h (Ctrl-C), and Interrupt 24h (Critical Error).
  5622.  ;* The purpose of an interrupt handler in this context is to prevent termina-
  5623.  ;* tion of both parent and child processes when the interrupt is invoked.
  5624.  ;* Such handlers often set flags to signal a process that the interrupt has
  5625.  ;* been called.
  5626.  
  5627.  ;* NewBreak - Handler for Interrupt 1Bh.
  5628.  
  5629.  NewBreak PROC   FAR
  5630.  
  5631.          sti                             ; Reenable interrupts
  5632.          push    ax                      ; Preserve AX register
  5633.          mov     al, 20h                 ; Send end-of-interrupt signal
  5634.          out     20h, al                 ;   to interrupt controller
  5635.          pop     ax                      ; Recover AX register
  5636.          iret                            ; Return from handler
  5637.                                          ;   without taking action
  5638.  NewBreak ENDP
  5639.  
  5640.  
  5641.  ;* NewCtrlC - Handler for Interrupt 23h.
  5642.  
  5643.  NewCtrlC PROC   FAR
  5644.  
  5645.          iret                            ; Return from handler
  5646.                                          ;   without taking action
  5647.  NewCtrlC ENDP
  5648.  
  5649.  
  5650.  ;* NewCritErr - Handler for Interrupt 24h.
  5651.  
  5652.  NewCritErr PROC FAR
  5653.  
  5654.          sub     al, al                  ; Tell DOS to ignore error
  5655.          iret                            ; Return from handler
  5656.                                          ;   without taking action
  5657.  NewCritErr ENDP
  5658.  
  5659.          END
  5660.  
  5661.  
  5662.  
  5663.  PAGERP.ASM
  5664.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\SHOW\PAGERP.ASM
  5665.  
  5666.  ;* PAGERP.ASM - Module containing routines for paging through a file and
  5667.  ;* writing text to the screen buffer. Works with main module SHOWP.ASM.
  5668.  
  5669.          TITLE   Pager
  5670.          .MODEL  small, pascal, os_os2
  5671.          .286
  5672.  
  5673.  INCL_NOCOMMON   EQU 1         ; Enable call groups
  5674.  INCL_VIO        EQU 1
  5675.  
  5676.          INCLUDE os2.inc
  5677.          INCLUDE show.inc
  5678.  
  5679.          .CODE
  5680.  
  5681.  ;* Pager - Displays status line and all the text lines for a screen.
  5682.  ;*
  5683.  ;* Params: cLines - lines to scroll (negative up, positive down)
  5684.  ;*
  5685.  ;* Uses:   Global variables: segBuf, offBuf, yCur
  5686.  ;*
  5687.  ;* Return: None
  5688.  
  5689.  Pager   PROC,
  5690.          cLines:SWORD
  5691.  
  5692.          mov     es, segBuf              ; Initialize buffer position
  5693.          mov     di, offBuf
  5694.  
  5695.          mov     cx, cLines              ; Get line count
  5696.          mov     ax, 10                  ; Search for linefeed
  5697.  
  5698.          or      cx, cx                  ; Argument 0?
  5699.          jl      backward                ; If below, backward
  5700.          jg      foreward                ; If above, forward
  5701.          jmp     showit                  ; If equal, done
  5702.  
  5703.  backward:
  5704.          call    GoBack                  ; Adjust backward
  5705.          jmp     showit                  ; Show screen
  5706.  
  5707.  foreward:
  5708.          call    GoForeward              ; Adjust forward
  5709.  
  5710.  ; Write line number to status line
  5711.  
  5712.  showit:
  5713.          cld                             ; Forward
  5714.          push    di                      ; Save
  5715.          push    ds                      ; ES = DS
  5716.          pop     es
  5717.  
  5718.          INVOKE  BinToStr,               ; Write line number as string
  5719.                  yCur,
  5720.                  ADDR stLine[LINE_POS]
  5721.  
  5722.  ; Fill in status line
  5723.  
  5724.          mov     cx, 6                   ; Six spaces to fill
  5725.          sub     cx, ax                  ; Subtract those already done
  5726.          mov     al, ' '                 ; Fill with space
  5727.          rep     stosb
  5728.  
  5729.          INVOKE  VioWrtCharStrAtt,       ; Write to screen
  5730.                  ADDR stLine,
  5731.                  X_MAX,
  5732.                  0,
  5733.                  0,
  5734.                  ADDR atSta,
  5735.                  0
  5736.  
  5737.          pop     di                      ; Update position
  5738.          mov     si, di
  5739.          mov     cx, yMax                ; Lines per screen
  5740.  
  5741.          .REPEAT
  5742.          mov     bx, yMax                ; Lines per screen
  5743.          inc     bx                      ; Adjust for 0
  5744.          sub     bx, cx                  ; Calculate current row
  5745.          push    cx                      ; Save line number
  5746.          mov     es, segBuf              ; Reload
  5747.  
  5748.          INVOKE  ShowLine,               ; Write line to screen
  5749.                  es::si,                 ; Pointer to current position
  5750.                  bx,                     ; Line number
  5751.                  cbBuf,                  ; File length (for bounds check)
  5752.                  ADDR atScr              ; Attribute
  5753.  
  5754.          pop     cx                      ; Restore line number
  5755.          mov     si, ax                  ; Get returned position
  5756.  
  5757.          dec     cx                      ; Count the line
  5758.          .UNTIL  (ax >= cbBuf) || !cx    ; Continue if more lines and not
  5759.          jcxz    exit                    ; Done if more lines,
  5760.                                          ;   else fill screen with spaces
  5761.          mov     ax, X_MAX               ; Columns times remaining lines
  5762.          mul     cl
  5763.          mov     dx, ax                  ; INVOKE uses AX, so use DX
  5764.          sub     cx, yMax                ; Calculate starting line
  5765.          neg     cx
  5766.          inc     cx
  5767.  
  5768.          INVOKE  VioWrtNCell,            ; Write space cells
  5769.                  ADDR celScr,            ; Cell of space and attribute
  5770.                  dx,                     ; Number of cells to fill
  5771.                  cx,                     ; Line to start fill
  5772.                  0,                      ; Column 0
  5773.                  0                       ; Console handle
  5774.  exit:
  5775.          ret
  5776.  
  5777.  Pager   ENDP
  5778.  
  5779.  
  5780.  ;* ShowLine - Writes a line of text to the screen.
  5781.  ;*
  5782.  ;* Params: pchIn - Far pointer to input text
  5783.  ;*         y - Line number
  5784.  ;*         cbMax - Maximum number of characters (file length)
  5785.  ;*         pcelAtrib - Far pointer to attribute
  5786.  ;*
  5787.  ;* Return: None
  5788.  
  5789.  ShowLine PROC USES si di,
  5790.          pchIn:PBYTE,
  5791.          y:WORD,
  5792.          cbMax:WORD,
  5793.          pcelAtrib:PBYTE
  5794.  
  5795.          LOCAL   achOut[X_MAX]:BYTE
  5796.  
  5797.          push    ds                      ; Save
  5798.          push    ss                      ; ES = SS
  5799.          pop     es
  5800.          lea     di, achOut              ; Destination line
  5801.          lds     si, pchIn               ; Source line
  5802.          mov     cx, X_MAX               ; Cells per row
  5803.          mov     bx, di                  ; Save copy of start for tab calc
  5804.  loop1:
  5805.          lodsb                           ; Get character
  5806.          cmp     al, 9                   ; Tab?
  5807.          je      filltab                 ; Space out tab
  5808.          cmp     al, 13                  ; CR?
  5809.          je      filleol                 ; Fill rest of line with spaces
  5810.          stosb                           ; Copy out
  5811.          cmp     si, cbMax               ; Check for end of file
  5812.          ja      filleol
  5813.          loop    loop1
  5814.  loop2:
  5815.          lodsb                           ; Throw away rest of line to truncate
  5816.          cmp     si, cbMax               ; Check for end of file
  5817.          ja      exit
  5818.          cmp     al, 13                  ; Check for end of line
  5819.          jne     loop2
  5820.          inc     si                      ; Throw away line feed
  5821.  
  5822.          jmp     exit                    ; Done
  5823.  filltab:
  5824.          push    bx                      ; Fill tab with spaces
  5825.          push    cx
  5826.  
  5827.          sub     bx, di                  ; Get current position in line
  5828.          neg     bx
  5829.  
  5830.          mov     cx, 8                   ; Default count 8
  5831.          and     bx, 7                   ; Get modulus
  5832.          sub     cx, bx                  ; Subtract
  5833.          mov     bx, cx                  ; Save modulus
  5834.  
  5835.          mov     al, ' '                 ; Write spaces
  5836.          rep     stosb
  5837.  
  5838.          pop     cx
  5839.          sub     cx, bx                  ; Adjust count
  5840.          .IF     sign?
  5841.          sub     cx, cx                  ; Make negative count 0
  5842.          .ENDIF
  5843.  
  5844.          pop     bx
  5845.          jcxz    loop2                   ; If beyond limit done
  5846.          jmp     loop1
  5847.  filleol:
  5848.          inc     si                      ; After CR, throw away LF
  5849.          mov     al, ' '                 ; Fill rest of line
  5850.          rep     stosb
  5851.  exit:
  5852.          pop     ds
  5853.          INVOKE  VioWrtCharStrAtt,
  5854.                  ADDR achOut,
  5855.                  X_MAX,
  5856.                  y,
  5857.                  0,
  5858.                  pcelAtrib,
  5859.                  0
  5860.  
  5861.          mov     ax, si                  ; Return position
  5862.          ret
  5863.  
  5864.  ShowLine ENDP
  5865.  
  5866.  
  5867.          END
  5868.  
  5869.  
  5870.  PAGERR.ASM
  5871.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\SHOW\PAGERR.ASM
  5872.  
  5873.  ;* PAGERR.ASM - Module containing routines for paging through a file and
  5874.  ;* writing text to the screen buffer. Works with main module SHOWR.ASM.
  5875.  
  5876.  
  5877.          .MODEL  small, pascal, os_dos   ; This code also works in tiny model
  5878.  
  5879.          INCLUDE show.inc
  5880.  
  5881.          .CODE
  5882.  
  5883.  ;* Pager - Displays status line and all the text lines for a screen.
  5884.  ;*
  5885.  ;* Params: cLines - lines to scroll (negative up, positive down)
  5886.  ;*
  5887.  ;* Uses:   Global variables: segBuf, offBuf, yCur
  5888.  ;*
  5889.  ;* Return: None
  5890.  
  5891.  Pager   PROC,
  5892.          cLines:SWORD
  5893.  
  5894.          mov     es, segBuf              ; Initialize buffer position
  5895.          mov     di, offBuf
  5896.  
  5897.          mov     cx, cLines              ; Get line count
  5898.          mov     ax, 10                  ; Search for linefeed
  5899.  
  5900.          or      cx, cx                  ; Argument 0?
  5901.          jg      forward                 ; If above, forward
  5902.          jl      backward                ; If below, backward
  5903.          jmp     showit                  ; If equal, done
  5904.  backward:
  5905.          call    GoBack                  ; Adjust backward
  5906.          jmp     showit                  ; Show screen
  5907.  forward:
  5908.          call    GoForeward              ; Adjust forward
  5909.  
  5910.  ; Write        line number to status line
  5911.  
  5912.  showit:
  5913.          cld                             ; Forward
  5914.          push    di                      ; Save
  5915.          push    ds                      ; ES = DS
  5916.          pop     es
  5917.  
  5918.          INVOKE  BinToStr,               ; Write line number as string
  5919.                  yCur,
  5920.                  ADDR stLine[LINE_POS]
  5921.  
  5922.  ; Fill in status line
  5923.  
  5924.          mov     cx, 6                   ; Seven spaces to fill
  5925.          sub     cx, ax                  ; Subtract those already done
  5926.          mov     al, ' '                 ; Fill with space
  5927.          rep     stosb
  5928.  
  5929.          INVOKE  ShowLine,               ; Write to screen
  5930.                  ADDR stLine,            ; Far pointer to line
  5931.                  0,                      ; Line number
  5932.                  atSta                   ; Atttribute
  5933.  
  5934.          pop     di
  5935.          mov     si, di                  ; Update position
  5936.          mov     cx, yMax                ; Lines per screen
  5937.  
  5938.          .REPEAT
  5939.          mov     bx, yMax                ; Lines per screen
  5940.          inc     bx                      ; Adjust for 0
  5941.          sub     bx, cx                  ; Calculate current row
  5942.          push    cx                      ; Save line number
  5943.          mov     es, segBuf              ; Reload
  5944.  
  5945.          INVOKE  ShowLine,               ; Write line to screen
  5946.                  es::si,                 ; Far pointer to text
  5947.                  bx,                     ; Line number
  5948.                  atScr                   ; Attribute
  5949.  
  5950.          pop     cx                      ; Restore line number
  5951.          mov     si, ax                  ; Get returned position
  5952.  
  5953.          dec     cx                      ; Count the line
  5954.          .UNTIL  (ax >= cbBuf) || !cx    ; Continue if more lines and not
  5955.          jcxz    exit                    ; Done if more lines,
  5956.                                          ;   else fill screen with spaces
  5957.          mov     al, cl                  ; Columns * remaining lines
  5958.          mov     dl, X_MAX               ;   is count of cells to fill
  5959.          mul     dl
  5960.          mov     dx, ax                  ; Save in DX (INVOKE uses AX)
  5961.  
  5962.          sub     cx, yMax                ; Calculate starting line
  5963.          neg     cx
  5964.          inc     cx
  5965.  
  5966.          INVOKE  CellFill,               ; Write space cells
  5967.                  cx,                     ; Starting line
  5968.                  dx,                     ; Cells to write
  5969.                  celScr                  ; Cell to write
  5970.  exit:
  5971.          ret
  5972.  
  5973.  Pager   ENDP
  5974.  
  5975.  
  5976.  ;* WriteNCell - Macro to write a cell one or more times. For CGA, the
  5977.  ;* macro writes during horizontal retrace. Note that this is a macro
  5978.  ;* even though it may result in more code than if it were a procedure.
  5979.  ;* This is because writes to the screen buffer are a speed bottleneck
  5980.  ;* that only occurs at a few key points in the program. The extra
  5981.  ;* size cost is worth paying.
  5982.  ;*
  5983.  ;* Uses:   ES:DI has screen buffer position
  5984.  ;*         AX has cell
  5985.  ;*         DX should have port number for rescan check if CGA
  5986.  ;*
  5987.  ;* Params: isCGA - One of the following:
  5988.                  CGA     EQU     1
  5989.                  NoCGA   EQU     0
  5990.  ;*
  5991.  ;*         count - If blank, write cell in AX once. If count given, write
  5992.  ;*         cell in AX count times. Note that the count is optimized for a
  5993.  ;*         CX argument. The argument should normally be blank or CX.
  5994.  
  5995.  WriteNCell MACRO isCGA:REQ, count:=<1>
  5996.  
  5997.      IF isCGA EQ 0                       ; First handle non-CGA
  5998.          IFIDNI <count>, <1>             ; Special case one cell
  5999.              stosw
  6000.          ELSE
  6001.              IFDIFI <count>, <cx>        ; Load count if necessary
  6002.                  mov  cx, count
  6003.              ENDIF
  6004.              rep  stosw                  ; Do repeated sequence
  6005.          ENDIF
  6006.      ELSE
  6007.          IFIDNI <count>, <1>             ; Special case one cell
  6008.              push    ax                  ; Save character
  6009.              .REPEAT
  6010.              in      al, dx              ; Look in the port
  6011.              shr     al, 1               ;   until it goes low
  6012.              .UNTIL  !carry?
  6013.              cli
  6014.              .REPEAT
  6015.              in      al, dx              ; Look in the port
  6016.              shr     al, 1               ;   until it goes high
  6017.              .UNTIL  carry?
  6018.              pop     ax                  ; Restore and write it
  6019.              stosw
  6020.              sti
  6021.          ELSE
  6022.              IFDIFI <count>, <cx>        ; Load count if necessary
  6023.                  mov  cx, count
  6024.              ENDIF
  6025.              .REPEAT
  6026.              push    ax                  ; Save character
  6027.              .REPEAT
  6028.              in      al, dx              ; Look in the port
  6029.              shr     al, 1               ;   until it goes low
  6030.              .UNTIL  !carry?
  6031.              cli
  6032.              .REPEAT
  6033.              in      al, dx              ; Look in the port
  6034.              shr     al, 1               ;   until it goes high
  6035.              .UNTIL  carry?
  6036.              pop     ax                  ; Restore and write it
  6037.              stosw
  6038.              sti
  6039.              .UNTILCXZ
  6040.          ENDIF
  6041.      ENDIF
  6042.  ENDM
  6043.  
  6044.  ;* ShowLine - Writes a line to the screen buffer.
  6045.  ;*
  6046.  ;* Params: fpBuffer - Far pointer to line to write
  6047.  ;*         y - Line number
  6048.  ;*         attr - Attribute
  6049.  ;*
  6050.  ;* Return: None
  6051.  
  6052.  ShowLine PROC USES si di ds,
  6053.          fpBuffer:FAR PTR BYTE,
  6054.          y:WORD,
  6055.          attr:BYTE
  6056.  
  6057.          sub     dx, dx                  ; Zero
  6058.          .IF     fCGA                    ; User port number as CGA flag
  6059.          mov     dx, 03DAh               ; Load port #
  6060.          .ENDIF
  6061.          mov     es, segVid              ; Load screen buffer segment
  6062.          lds     si, fpBuffer            ; Buffer segment
  6063.          mov     cx, X_MAX               ; Cells per row
  6064.          mov     ax, y                   ; Starting row
  6065.          mov     bx, X_MAX * 2           ; Bytes per row
  6066.          mul     bl                      ; Figure columns per row
  6067.          mov     di, ax                  ; Load as destination
  6068.          mov     bx, di                  ; Save start for tab calculation
  6069.          mov     ah, attr                ; Attribute
  6070.  movechar:
  6071.          lodsb                           ; Get character
  6072.          cmp     al, 13                  ; CR?
  6073.          je      fillspc
  6074.          cmp     al, 9                   ; Tab?
  6075.          jne     notab
  6076.          call    FillTab                 ; Yes? fill with spaces
  6077.          jcxz    nextline                ; If beyond limit done
  6078.          jmp     movechar
  6079.  notab:
  6080.          or      dx, dx                  ; CGA?
  6081.          je      notab2
  6082.          WriteNCell CGA                  ; Yes? Write during retrace
  6083.          loop    movechar                ; Duplicate code here and below
  6084.          jmp     nextline                ;   is worth cost in tight loop
  6085.  notab2:
  6086.          WriteNCell NoCGA                ; Write
  6087.          loop    movechar
  6088.          jmp     nextline                ; Done
  6089.  fillspc:
  6090.          mov     al, ' '                 ; Fill with space
  6091.  
  6092.          .IF     dx != 0                 ; CGA?
  6093.          WriteNCell CGA, cx
  6094.          inc     si                      ; Adjust
  6095.          jmp     exit                    ; Done
  6096.          .ENDIF
  6097.          WriteNCell NoCGA, cx
  6098.          inc     si                      ; Adjust for LF
  6099.          jmp     exit                    ; Done
  6100.  nextline:
  6101.          mov     ah, 10                  ; Search for next line feed
  6102.          .REPEAT
  6103.          lodsb                           ; Load and compare
  6104.          .UNTILCXZ al == ah
  6105.  exit:
  6106.          mov     ax, si                  ; Return position
  6107.          ret
  6108.  
  6109.  ShowLine ENDP
  6110.  
  6111.  
  6112.  ;* CellFill - Fills a portion of the screen with a specified
  6113.  ;* character/attribute cell.
  6114.  ;*
  6115.  ;* Params: yStart - Starting line
  6116.  ;*         cbCell - Number of cells
  6117.  ;*         celFill - Attribute and character
  6118.  ;*
  6119.  ;* Return: None
  6120.  
  6121.  CellFill PROC,
  6122.          yStart:WORD,
  6123.          cbCell:WORD,
  6124.          celFill:WORD
  6125.  
  6126.          mov     dx, 03DAh               ; Load port #
  6127.          mov     cx, yStart              ; Starting line
  6128.          mov     al, X_MAX * 2           ; Convert line to starting offset
  6129.          mul     cl
  6130.          mov     di, ax                  ; Make it the target
  6131.          mov     es, segVid              ; Load screen buffer segment
  6132.          mov     cx, cbCell              ; Characters to fill
  6133.          mov     ax, celFill             ; Attribute
  6134.          .IF     fCGA                    ; Write cells
  6135.          WriteNCell CGA, cx
  6136.          .ELSE
  6137.          WriteNCell NoCGA, cx
  6138.          .ENDIF
  6139.  
  6140.          ret
  6141.  
  6142.  CellFill ENDP
  6143.  
  6144.  
  6145.  ;* FillTab - Writes spaces for tab to screen.
  6146.  ;*
  6147.  ;* Input:  BX points to start of line
  6148.  ;*         DI points to current position
  6149.  ;*
  6150.  ;* Return: None
  6151.  
  6152.  FillTab PROC
  6153.  
  6154.          push    bx
  6155.          push    cx
  6156.  
  6157.          sub     bx, di                  ; Get current position in line
  6158.          neg     bx
  6159.          shr     bx, 1                   ; Divide by 2 bytes per character
  6160.  
  6161.          mov     cx, 8                   ; Default count 8
  6162.          and     bx, 7                   ; Get modulus
  6163.          sub     cx, bx                  ; Subtract
  6164.          mov     bx, cx                  ; Save modulus
  6165.  
  6166.          mov     al, ' '                 ; Spaces
  6167.          .IF     dx != 0                 ; Write cells
  6168.          WriteNCell CGA, cx
  6169.          .ELSE
  6170.          WriteNCell NoCGA, cx
  6171.          .ENDIF
  6172.          pop     cx
  6173.          sub     cx, bx                  ; Adjust count
  6174.          .IF     sign?
  6175.          sub     cx, cx                  ; Make negative count 0
  6176.          .ENDIF
  6177.          pop     bx
  6178.          ret
  6179.  
  6180.  FillTab ENDP
  6181.  
  6182.  
  6183.  ;* IsEGA - Determines if the current adapter can handle more than 25
  6184.  ;* lines per screen (usually an EGA or VGA).
  6185.  ;*
  6186.  ;* Params: None
  6187.  ;*
  6188.  ;* Return: 0 if no CGA or MONO, lines per screen if EGA/VGA
  6189.  
  6190.  IsEGA   PROC
  6191.  
  6192.          mov     ah, 12h                 ; Call EGA status function
  6193.          mov     bl, 10h
  6194.          sub     cx, cx                  ; Clear status bits
  6195.          int     10h
  6196.          sub     ax, ax                  ; Segment 0 and assume no EGA
  6197.          jcxz    noega                   ; If status still clear, no EGA
  6198.  
  6199.          mov     es, ax                  ; ES=0
  6200.          test    BYTE PTR es:[487h], 1000y; Test active bit
  6201.          jnz     noega                   ; If set, not active
  6202.          mov     ax, 1130h               ; Get EGA information
  6203.          int     10h
  6204.          mov     al, dl                  ; Return lines per screen
  6205.          cbw
  6206.  noega:
  6207.          ret
  6208.  
  6209.  IsEGA   ENDP
  6210.  
  6211.  
  6212.          END
  6213.  
  6214.  
  6215.  PASCAL.ASM
  6216.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\MIXED\PASCAL.ASM
  6217.  
  6218.  ; Assemble with ML /c PASCAL.ASM
  6219.  ; Called by PASMAIN.PAS
  6220.  
  6221.          .MODEL  medium, PASCAL
  6222.          .386
  6223.  Power2  PROTO PASCAL  factor:WORD, power:WORD
  6224.          .CODE
  6225.  
  6226.  Power2  PROC    factor:WORD, power:WORD
  6227.  
  6228.          mov     ax, factor    ; Load Factor into AX
  6229.          mov     cx, power     ; Load Power into CX
  6230.          shl     ax, cl        ; AX = AX * (2 to power of CX)
  6231.          ret                   ; Leave return value in AX
  6232.  
  6233.  Power2  ENDP
  6234.          END
  6235.  
  6236.  
  6237.  QPEX.ASM
  6238.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\MIXED\QPEX.ASM
  6239.  
  6240.  ; Assemble with ML /c QPEX.ASM
  6241.  
  6242.  Power2  PROTO PASCAL  factor:WORD, power:WORD
  6243.  
  6244.  CODE        SEGMENT WORD PUBLIC
  6245.          ASSUME  CS:CODE
  6246.  
  6247.  
  6248.  Power2  PROC PASCAL   factor:WORD, power:WORD
  6249.  
  6250.          mov     ax, factor        ; Load factor into AX
  6251.          mov     cx, power         ; Load power into CX
  6252.          shl        ax, cl                  ; AX = AX * (2 to power of CX)
  6253.                                    ; Leave return value in AX
  6254.          ret
  6255.  Power2  ENDP
  6256.  
  6257.  CODE    ENDS
  6258.          END
  6259.  
  6260.  
  6261.  SHOWP.ASM
  6262.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\SHOW\SHOWP.ASM
  6263.  
  6264.  ;* SHOWP.ASM - Text file displayer for OS/2 (protect mode).
  6265.  
  6266.          TITLE   Show
  6267.          .MODEL  small, pascal, os_os2
  6268.          .DOSSEG
  6269.          .286
  6270.  
  6271.  INCL_NOCOMMON   EQU 1         ; Enable call groups
  6272.  INCL_DOSFILEMGR EQU 1
  6273.  INCL_DOSMEMMGR  EQU 1
  6274.  INCL_KBD        EQU 1
  6275.  INCL_VIO        EQU 1
  6276.  
  6277.          INCLUDE os2.inc
  6278.          INCLUDE show.inc
  6279.          INCLUDELIB os2.lib
  6280.  
  6281.          .STACK
  6282.  
  6283.          .DATA
  6284.  
  6285.  ; Status line
  6286.  
  6287.  stLine  BYTE    "Line: 12345 "
  6288.  stFile  BYTE    "File: 12345678.123  "
  6289.          BYTE    "Quit: Q  Next: ESC  Move:   PGUP PGDN HOME END"
  6290.  
  6291.  ; Variables for screen and cursor handling
  6292.  
  6293.  yCur    WORD    1               ; Current line number
  6294.  yMax    WORD    ?               ; Lines per screen
  6295.  vmiMode VIOMODEINFO < SIZE VIOMODEINFO > ; Structure for video data
  6296.                                  ; First field initialized to size
  6297.  vciCsr  VIOCURSORINFO <>        ; Structure for cursor data
  6298.  atCsr   WORD    -1              ; Cursor attribute (initized to hidden)
  6299.  bCsrSta BYTE    0               ; 0 = cursor visible, position unchanged
  6300.                                  ; 1 = cursor invisible, position unchanged
  6301.                                  ; 2 = cursor invisible, position changed
  6302.  
  6303.  atSta   BYTE    STAT_CLR        ; Status line color
  6304.  celScr  LABEL   WORD            ; Cell (character and attribute)
  6305.  chScr   BYTE    " "             ; Initialize to space
  6306.  atScr   BYTE    SCRN_CLR        ; Screen color
  6307.  chInit  BYTE    0               ; Cell to restore when finished
  6308.  atInit  BYTE    0
  6309.  
  6310.  ; Buffer variables
  6311.  
  6312.  fpBuf   LABEL   PBYTE
  6313.  offBuf  WORD    0               ; Position in buffer (offset)
  6314.  segBuf  SEL     ?               ; Base of buffer (segment selector)
  6315.  cbBuf   WORD    ?               ; Count in bytes of buffer
  6316.  
  6317.  ; File information
  6318.  
  6319.  hFileIn HFILE   ?               ; Holds file handle on open
  6320.  usAct   WORD    ?               ; Result of open
  6321.  usMode  WORD    OPEN_ACCESS_READONLY OR OPEN_SHARE_DENYNONE
  6322.  cbRead  WORD    ?               ; Bytes read from file
  6323.  
  6324.  ; Directory information for file name search
  6325.  
  6326.  stFiles BYTE    NAME_MAX DUP ("w")
  6327.  hFiles  WORD    HDIR_CREATE     ; Directory handle
  6328.  fiFiles FILEFINDBUF <>          ; Structure for results
  6329.  usCount WORD    1               ; Find one file at a time
  6330.  
  6331.  ; Buffer for file name
  6332.  
  6333.  kkiChar KBDKEYINFO <>           ; Structure for character input
  6334.  sibStr  STRINGINBUF < NAME_MAX >; Structure for string input
  6335.  
  6336.  ; Messages
  6337.  
  6338.  stMsg1  BYTE    13, 10, "Enter filename: "
  6339.  stMsg2  BYTE    13, 10, "File problem. Try again? "
  6340.  stMsg3  BYTE    13, 10, "File too large: "
  6341.  stMsg4  BYTE    13, 10, "Memory problem.",13,10
  6342.  
  6343.  ; Call table
  6344.  
  6345.  achKeys BYTE    71, 72, 73, 79, 80, 81, 'q', 'Q'; Key table
  6346.  afnKeys WORD    HomeKey                         ; Corresponding procedures
  6347.          WORD    UpKey
  6348.          WORD    PgUpKey
  6349.          WORD    EndKey
  6350.          WORD    DownKey
  6351.          WORD    PgDnKey
  6352.          WORD    Quit
  6353.          WORD    Quit
  6354.          WORD    UnknownKey
  6355.  
  6356.          .CODE
  6357.          .STARTUP
  6358.  
  6359.  ; Load environment segment
  6360.  
  6361.          mov     es, ax                  ; AX points to environment segment
  6362.          mov     di, bx                  ; BX points to command line offset
  6363.  
  6364.  ; Throw away .EXE name
  6365.  
  6366.          sub     ax, ax                  ; Find null at end of program name
  6367.          repne   scasb
  6368.          cmp     BYTE PTR es:[di], 0     ; If double zero, there's no name
  6369.          je      Prompter                ;   so get from prompt
  6370.  
  6371.          .IF     BYTE PTR es:[di] == ' '
  6372.          inc     di                      ; Skip leading space
  6373.          .ENDIF
  6374.  
  6375.  ; Copy command line to file name buffer
  6376.  
  6377.          mov     si, di                  ; Filename source
  6378.          mov     di, OFFSET stFiles      ; Name buffer destination
  6379.          mov     bx, ds                  ; Save segment registers
  6380.          mov     dx, es
  6381.          mov     ds, dx                  ; DS = ES
  6382.          mov     es, bx                  ; ES = DS
  6383.          mov     cx, NAME_MAX            ; Count = max file name allowed
  6384.  
  6385.          .REPEAT
  6386.          lodsb                           ; Copy characters
  6387.          .BREAK .IF (al == ' ') || (al == 0) ; Stop at space or null
  6388.          stosb
  6389.          .UNTILCXZ                       ; Until name exceeds max
  6390.  
  6391.          mov     ds, bx                  ; Restore DS
  6392.          mov     BYTE PTR [di], 0
  6393.          jmp     FindFile
  6394.  
  6395.  ; Prompt for file
  6396.  
  6397.  NoFile:
  6398.          INVOKE  VioWrtTTy,              ; Write message
  6399.                  ADDR stMsg2,
  6400.                  LENGTHOF stMsg2,
  6401.                  0
  6402.  
  6403.          INVOKE  KbdCharIn,
  6404.                  ADDR kkiChar,
  6405.                  IO_WAIT,
  6406.                  0
  6407.  
  6408.          and     kkiChar.chChar_, 11011111y ; Convert to uppercase
  6409.          cmp     kkiChar.chChar_, "Y"
  6410.  
  6411.          mov     hFiles, -1
  6412.          mov     usCount, 1
  6413.          .IF     !zero?
  6414.          jmp     Quit                    ; Quit if not yes
  6415.          .ENDIF
  6416.  Prompter:
  6417.          INVOKE  VioWrtTTy,              ; Else prompt for file name
  6418.                  ADDR stMsg1,
  6419.                  LENGTHOF stMsg1,
  6420.                  0
  6421.  
  6422.          INVOKE  KbdStringIn,
  6423.                  ADDR stFiles,
  6424.                  ADDR sibStr,
  6425.                  IO_WAIT,
  6426.                  0
  6427.  
  6428.          mov     di, sibStr.cchIn_       ; Null terminate
  6429.          mov     stFiles[di], 0
  6430.  
  6431.  ; Find first (or only) file in filespec
  6432.  
  6433.  FindFile:
  6434.          INVOKE  DosFindFirst,
  6435.                  ADDR stFiles,
  6436.                  ADDR hFiles,
  6437.                  0,
  6438.                  ADDR fiFiles,
  6439.                  SIZE fiFiles,
  6440.                  ADDR usCount,
  6441.                  0
  6442.  
  6443.          or      ax, ax
  6444.          jnz     NoFile
  6445.  
  6446.          INVOKE  GetVid                  ; Adjust for current mode and
  6447.                                          ;  video adapter and hide cursor
  6448.  
  6449.  ; Main program loop to process files
  6450.  
  6451.          .REPEAT
  6452.  
  6453.  ; Copy file name to file spec
  6454.  
  6455.          mov     bCsrSta, 2              ; Cursor hidden, position unchanged
  6456.          INVOKE  GetNamePos,             ; Get file name position in file spec
  6457.                  ADDR stFiles
  6458.  
  6459.          mov     si, OFFSET fiFiles.achName_; Load source name
  6460.          mov     di, ax                  ; Load adjusted destination address
  6461.                                          ;   from return value
  6462.          sub     cx, cx                  ; Load file name length
  6463.          mov     cl, fiFiles.cchName_
  6464.          rep     movsb                   ; Copy to spec
  6465.          mov     BYTE PTR es:[di], 0     ; Null terminate
  6466.  
  6467.  ; Copy file name to status line
  6468.  
  6469.          sub     cx, cx                  ; Load file length
  6470.          mov     cl, fiFiles.cchName_
  6471.          mov     bx, 12                  ; Calculate blank spaces to fill
  6472.          sub     bx, cx
  6473.          push    ds                      ; ES=DS
  6474.          pop     es
  6475.          mov     si, OFFSET fiFiles.achName_; File name as source
  6476.          mov     di, OFFSET stFile[FILE_POS]; Status line as destination
  6477.          rep     movsb
  6478.          mov     al, " "                 ; Fill rest of name space with blanks
  6479.          mov     cx, bx
  6480.          rep     stosb
  6481.  
  6482.  ; Skip any file that is larger than 64K
  6483.  
  6484.          .IF     WORD PTR fiFiles.cbFile_[2] != 0
  6485.  
  6486.          INVOKE  VioWrtTTy,
  6487.                  ADDR stMsg3,
  6488.                  LENGTHOF stMsg3,
  6489.                  0
  6490.  
  6491.          INVOKE  VioWrtTTy,
  6492.                  ADDR fiFiles.achName_,
  6493.                  fiFiles.cchName_,
  6494.                  0
  6495.  
  6496.          .IF     usCount <= 0            ; Get key if there's another file
  6497.          INVOKE  KbdCharIn,
  6498.                  ADDR kkiChar,
  6499.                  IO_WAIT,
  6500.                  0
  6501.          .ENDIF
  6502.          .ENDIF
  6503.  
  6504.  ; Allocate file Buffer
  6505.  
  6506.          mov     ax, WORD PTR fiFiles.cbFile_[0] ; Save size
  6507.          mov     cbBuf, ax
  6508.          mov     offBuf, 0
  6509.          INVOKE  DosAllocSeg,
  6510.                  ax,
  6511.                  ADDR segBuf,
  6512.                  0
  6513.  
  6514.          .IF     ax != 0
  6515.          mov     bCsrSta, 1              ; Cursor hidden, position unchanged
  6516.          INVOKE  VioWrtTTy,
  6517.                  ADDR stMsg4,
  6518.                  LENGTHOF stMsg4,
  6519.                  0
  6520.  
  6521.          jmp     Quit
  6522.          .ENDIF
  6523.  
  6524.  ; Open file and read contents into buffer
  6525.  
  6526.          INVOKE  DosOpen,
  6527.                  ADDR stFiles,
  6528.                  ADDR hFileIn,
  6529.                  ADDR usAct,
  6530.                  0,
  6531.                  FILE_NORMAL,
  6532.                  FILE_OPEN,
  6533.                  usMode,
  6534.                  0
  6535.  
  6536.          .IF     ax != 0
  6537.          jmp     NoFile
  6538.          .ENDIF
  6539.  
  6540.          INVOKE  DosRead,
  6541.                  hFileIn,
  6542.                  fpBuf,
  6543.                  cbBuf,
  6544.                  ADDR cbRead
  6545.  
  6546.          .IF     ax != 0
  6547.          jmp     NoFile
  6548.          .ENDIF
  6549.  
  6550.  ; Search back for EOF marker and adjust if necessary
  6551.  
  6552.          mov     di, cbRead              ; Load file length
  6553.          dec     di                      ;   and adjust
  6554.          mov     es, segBuf              ; Save ES and load buffer segment
  6555.          std                             ; Look backward for 255 characters
  6556.          mov     cx, 0FFh
  6557.          .IF     cx >= di
  6558.          mov     cx, di
  6559.          .ENDIF
  6560.  
  6561.          mov     al, 1Ah                 ; Search for EOF marker
  6562.          repne   scasb
  6563.          cld
  6564.          .IF     cx != 0                 ; If found:
  6565.          inc     di                      ; Adjust and save file size
  6566.          mov     cbBuf, di
  6567.          .ENDIF
  6568.  
  6569.  ; Show a screen of text and allow commands
  6570.  
  6571.          INVOKE  Show
  6572.  
  6573.          INVOKE  DosClose,               ; Close file
  6574.                  hFileIn
  6575.  
  6576.          INVOKE  DosFreeSeg,             ; Free memofy
  6577.                  segBuf
  6578.  
  6579.          INVOKE  DosFindNext,            ; Get next file
  6580.                  hFiles,
  6581.                  ADDR fiFiles,
  6582.                  SIZE fiFiles,
  6583.                  ADDR usCount
  6584.  
  6585.          .UNTIL  ax != 0                 ; Fall through to Quit if
  6586.                                          ;  this is the last file
  6587.  Quit    PROC
  6588.  
  6589.          cmp     bCsrSta, 1              ; Check cursor status
  6590.          jg      csrvislast              ; 2 - Make cursor visible on last lin
  6591.          je      csrvis                  ; 1 - Make cursor visible
  6592.          jmp     csrasis                 ; 0 - Leave cursor as is
  6593.  
  6594.  csrvislast:
  6595.          INVOKE  VioSetCurPos,           ; Restore cursor on last line
  6596.                  yMax,
  6597.                  0,
  6598.                  0
  6599.          INVOKE  VioScrollDn,
  6600.                  yMax,
  6601.                  0,
  6602.                  yMax,
  6603.                  79,
  6604.                  1,
  6605.                  ADDR chInit,
  6606.                  0
  6607.  csrvis:                                 ; Fall through
  6608.          mov     ax, atCsr               ; Restore cursor attribute
  6609.          mov     vciCsr.attr_, ax
  6610.          INVOKE  VioSetCurType,
  6611.                  ADDR vciCsr,
  6612.                  0
  6613.  csrasis:                                ; Fall through
  6614.          .EXIT   0
  6615.  
  6616.  Quit    ENDP
  6617.  
  6618.  
  6619.  Show    PROC
  6620.  
  6621.  ; Display first page
  6622.  
  6623.          mov     yCur, 1
  6624.          INVOKE  Pager,                  ; Start at 0
  6625.                  0
  6626.  
  6627.  ; Handle keys
  6628.  
  6629.          .REPEAT
  6630.          INVOKE  KbdCharIn,              ; Get a key and load to register
  6631.                  ADDR kkiChar,
  6632.                  IO_WAIT,
  6633.                  0
  6634.  
  6635.          mov     al, kkiChar.chChar_
  6636.  
  6637.          .BREAK .IF al == 27             ; If ESCAPE get out for next file
  6638.  
  6639.          ; If null or E0 (for extended keyboard), it's an extended key
  6640.          .IF     (al == 0) || (al == 0E0h)
  6641.          mov     al, kkiChar.chScan_      ; Load scan code
  6642.          .ENDIF
  6643.  
  6644.          push    ds                      ; ES = DS
  6645.          pop     es
  6646.          mov     di, OFFSET achKeys      ; Load address and length of key list
  6647.          mov     cx, LENGTHOF achKeys + 1
  6648.          repne   scasb                   ; Find position and point to key
  6649.          sub     di, OFFSET achKeys + 1
  6650.          shl     di, 1                   ; Adjust pointer for word addresses
  6651.          call    afnKeys[di]             ; Call procedure
  6652.          .UNTIL  0
  6653.  
  6654.          ret
  6655.  Show    ENDP
  6656.  
  6657.  HomeKey:
  6658.          mov     offBuf, 0               ; HOME - set position to 0
  6659.          mov     yCur, 1
  6660.          INVOKE  Pager, offBuf
  6661.          retn
  6662.  
  6663.  UpKey:
  6664.          INVOKE  Pager, -1               ; UP - scroll back 1 line
  6665.          retn
  6666.  
  6667.  PgUpKey:
  6668.          mov     ax, yMax                ; PGUP - Page back
  6669.          neg     ax
  6670.          INVOKE  Pager, ax
  6671.          retn
  6672.  
  6673.  EndKey:
  6674.          mov     ax, cbBuf               ; END - Get last byte of file
  6675.          dec     ax                      ; Zero adjust
  6676.          mov     offBuf, ax              ; Make it the file position
  6677.          mov     yCur, -1                ; Set illegal line number as flag
  6678.          mov     ax, yMax                ; Page back
  6679.          neg     ax
  6680.          INVOKE  Pager, ax
  6681.          retn
  6682.  
  6683.  DownKey:
  6684.          INVOKE  Pager, 1                ; DOWN - scroll forward 1 line
  6685.          retn
  6686.  
  6687.  PgDnKey:
  6688.          INVOKE  Pager, yMax             ; PGDN - page forward
  6689.          retn
  6690.  
  6691.  UnknownKey:
  6692.          retn                            ; Ignore unknown key
  6693.  
  6694.  
  6695.  ;* GetVid - Gets the video mode and sets related global variables.
  6696.  ;*
  6697.  ;* Params: None
  6698.  ;*
  6699.  ;* Return: Number of lines in current mode (25, 43, or 50)
  6700.  
  6701.  GetVid  PROC
  6702.  
  6703.          LOCAL   x:USHORT, y:USHORT, cb:USHORT
  6704.  
  6705.  
  6706.          INVOKE  VioGetMode,             ; Get video mode
  6707.                  ADDR vmiMode,
  6708.                  0
  6709.  
  6710.          sub     ax, ax                  ; Clear AH
  6711.          mov     al, vmiMode.fbType_     ; Put type in register
  6712.  
  6713.          ; If monochrome or color burst off:
  6714.          .IF     (al & VGMT_GRAPHICS) || (al & VGMT_DISABLEBURST)
  6715.          mov     atSta, STAT_BW          ; Set B&W defaults for status line
  6716.          mov     atScr, SCRN_BW          ;   and screen background
  6717.          .ENDIF
  6718.  
  6719.          INVOKE  VioGetCurPos,           ; Get cursor position (for cell read)
  6720.                  ADDR y,                 ; Row
  6721.                  ADDR x,                 ; Column
  6722.                  0                       ; Console handle
  6723.  
  6724.          mov     cb, 1                   ; One cell
  6725.          INVOKE  VioReadCellStr,         ; Read cell to get current attribute
  6726.                  ADDR chInit,            ; Address to receive cell
  6727.                  ADDR cb,                ; Address of length
  6728.                  y,                      ; Row
  6729.                  x,                      ; Column
  6730.                  0                       ; Console handle
  6731.          mov     chInit, ' '             ; Make sure character is space
  6732.  
  6733.          INVOKE  VioGetCurType,          ; Get cursor mode
  6734.                  ADDR vciCsr,
  6735.                  0
  6736.          mov     ax, vciCsr.attr_        ; Save cursor attribute
  6737.          xchg    atCsr, ax
  6738.          mov     vciCsr.attr_, ax        ; Set hidden cursor attribute
  6739.          mov     ax, vmiMode.row_        ; Get number of rows and adjust
  6740.          dec     ax
  6741.          mov     yMax, ax
  6742.  
  6743.          INVOKE  VioSetCurType,          ; Hide cursor
  6744.                  ADDR vciCsr,
  6745.                  0
  6746.  
  6747.          ret
  6748.  
  6749.  GetVid  ENDP
  6750.  
  6751.  
  6752.          END
  6753.  
  6754.  
  6755.  SHOWR.ASM
  6756.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\SHOW\SHOWR.ASM
  6757.  
  6758.  ;* SHOWR.ASM - Text file displayer for DOS (real mode).
  6759.  
  6760.          TITLE   Show
  6761.          .MODEL  small, pascal, os_dos   ; This code also works in tiny model
  6762.          .DOSSEG
  6763.  
  6764.          INCLUDE show.inc
  6765.          INCLUDE dos.inc
  6766.          INCLUDE bios.inc
  6767.  
  6768.          .STACK
  6769.  
  6770.          .DATA
  6771.  
  6772.  ; Status line
  6773.  
  6774.  stLine  BYTE    "Line: 12345 "
  6775.  stFile  BYTE    "File: 12345678.123  "
  6776.          BYTE    "Quit: Q  Next: ESC  Move:   PGUP PGDN HOME END"
  6777.  
  6778.  ; Variables for        screen handling
  6779.  
  6780.  yCur    WORD    1
  6781.  yMax    WORD    24              ; Number of rows - status line takes one more
  6782.  iMode   BYTE    0               ; Initial mode
  6783.  iPage   BYTE    0               ; Initial display page
  6784.  atInit  BYTE    0               ; Initial attribute
  6785.  shCsr   WORD    0               ; Initial cursor shape
  6786.  bCsrSta BYTE    0               ; 0 = cursor visible, position unchanged
  6787.                                  ; 1 = cursor invisible, position unchanged
  6788.                                  ; 2 = cursor invisible, position changed
  6789.  
  6790.  fNewVid BYTE    0               ; Video change flag
  6791.  fCGA    BYTE    1               ; CGA flag - default yes
  6792.  
  6793.  segVid  WORD    SEG_CLR         ; Video buffer address - default color
  6794.  
  6795.  atSta   BYTE    STAT_CLR        ; Status line color
  6796.  celScr  LABEL   WORD            ; Cell (character and attribute)
  6797.  chScr   BYTE    ' '             ; Initialize to space
  6798.  atScr   BYTE    SCRN_CLR        ; Screen color
  6799.  
  6800.  ; Buffer variables
  6801.  
  6802.  fpBuf   LABEL   FAR PTR
  6803.  offBuf  WORD    0               ; Position in buffer (offset)
  6804.  segBuf  WORD    0               ; Base of buffer (segment)
  6805.  cbBuf   WORD    0               ; Length of buffer
  6806.  
  6807.  ; File information
  6808.  
  6809.  hFileIn WORD    0               ; Holds file handle on open
  6810.  
  6811.  ; Buffer for file spec and structure for file info
  6812.  
  6813.  achBuf  BYTE    NAME_MAX, ?     ; Buffer format for string input
  6814.  stFiles BYTE    NAME_MAX DUP (0); File spec string
  6815.  fiFiles FILE_INFO <>            ; Wild card entry structure
  6816.  cFiles  WORD    0               ; Count of 1 or 0 files remaining
  6817.  
  6818.  ; Messages
  6819.  
  6820.  stMsg1  BYTE    13, 10, 13, 10, "Enter filename: $"
  6821.  stMsg2  BYTE    13, 10, "File problem. Try again? $"
  6822.  stMsg3  BYTE    13, 10, "File too large: $"
  6823.  stMsg4  BYTE    13, 10, "Memory problem.", 13, 10, "$"
  6824.  stMsg5  BYTE    13, 10, "Must have DOS 2.0 or higher", 13, 10, "$"
  6825.  
  6826.  ; Call table
  6827.  
  6828.  achKeys BYTE    71, 72, 73, 79, 80, 81, 'q', 'Q'; Key table
  6829.  afnKeys WORD    HomeKey                         ; Corresponding procedures
  6830.          WORD    UpKey
  6831.          WORD    PgUpKey
  6832.          WORD    EndKey
  6833.          WORD    DownKey
  6834.          WORD    PgDnKey
  6835.          WORD    Quit
  6836.          WORD    Quit
  6837.          WORD    UnknownKey
  6838.  
  6839.          .CODE
  6840.          .STARTUP
  6841.  
  6842.  ; Adjust memory allocation (works for tiny or small model)
  6843.  
  6844.          mov     bx, sp                  ; Convert stack pointer to paragraphs
  6845.          mov     cl, 4                   ;   to get stack size
  6846.          shr     bx, cl
  6847.          mov     ax, ss                  ; Add SS to get end of program
  6848.          add     ax, bx
  6849.          mov     bx, es                  ; Get start of program
  6850.          sub     ax, bx                  ; Subtract start from end
  6851.          inc     ax
  6852.          @ModBlock ax                    ; Release memory after program
  6853.  
  6854.  ; Check DOS
  6855.  
  6856.          @GetVer                         ; Get DOS version
  6857.          .IF     al < 2                  ; Requires DOS 2.0
  6858.          @ShowStr stMsg5                 ;   else error and quit
  6859.          int     20h
  6860.          .ENDIF
  6861.  
  6862.  ; Get command line and copy to file name buffer
  6863.  
  6864.          mov     di, 80h                 ; PSP offset of command line
  6865.          mov     bl, es:[di]             ; Get length from first byte
  6866.          sub     bh, bh
  6867.          or      bx, bx
  6868.          je      Prompter
  6869.  
  6870.          mov     WORD PTR es:[bx+81h], 0 ; Convert to ASCIIZ
  6871.          mov     al, ' '                 ; Character to check for
  6872.          inc     di                      ; Advance beyond count
  6873.          mov     cx, 0FFFFh              ; Don't let count interfere
  6874.          repe    scasb                   ; Find first non-space
  6875.          dec     di                      ; Adjust
  6876.  
  6877.          mov     si, di                  ; Filename source
  6878.          mov     di, OFFSET stFiles      ; Name buffer destination
  6879.          mov     bx, ds                  ; Save segment registers
  6880.          mov     dx, es
  6881.          mov     ds, dx                  ; DS = ES
  6882.          mov     es, bx                  ; ES = DS
  6883.          mov     cx, NAME_MAX            ; Count = max file name allowed
  6884.  
  6885.          .REPEAT
  6886.          lodsb                           ; Copy characters
  6887.          .BREAK .IF (al == ' ') || (al == 0) ; Stop at space or null
  6888.          stosb
  6889.          .UNTILCXZ                       ; Until name exceeds max
  6890.  
  6891.          mov     ds, bx                  ; Restore segments
  6892.          mov     es, dx
  6893.          mov     BYTE PTR [di], 0
  6894.          jmp     FindFile
  6895.  NoFile:
  6896.  
  6897.          @ShowStr stMsg2                 ; Prompt to try again
  6898.          @GetChar 0, 1, 0
  6899.          and     al, 11011111y           ; Convert key to uppercase
  6900.          .IF     al != 'Y'               ; If not yes,
  6901.          jmp     quit                    ;   quit
  6902.          .ENDIF
  6903.  
  6904.  ; Prompt for file
  6905.  
  6906.  Prompter:
  6907.          @ShowStr stMsg1                 ; Prompt for file
  6908.          @GetStr achBuf, 0               ; Get response as ASCIIZ
  6909.  
  6910.  ; Find first (or only) file in filespec
  6911.  
  6912.  FindFile:
  6913.  
  6914.          @SetDTA <OFFSET fiFiles>        ; Set DTA to file info structure
  6915.                                          ; Don't need DTA for anything else,
  6916.                                          ;   so no need to restore it
  6917.          @GetFirst stFiles,0             ; Find a matching file
  6918.  
  6919.          jc      NoFile                  ; If not found, prompt for new
  6920.          inc     cFiles                  ; Some files remaining
  6921.  
  6922.          INVOKE  GetVid
  6923.  
  6924.  ; Main program loop to process files
  6925.  
  6926.          .REPEAT
  6927.  
  6928.  ; Copy file name to file spec
  6929.  
  6930.          mov     bCsrSta, 2              ; Cursor hidden, position unchanged
  6931.          INVOKE  GetNamePos,             ; Get file name position in file spec
  6932.                  ADDR stFiles
  6933.  
  6934.          mov     si, OFFSET fiFiles.FName; Point to source name
  6935.          push    ds                      ; ES = DS
  6936.          pop     es
  6937.          mov     di, ax                  ; Load address from return value
  6938.  
  6939.          .REPEAT                         ; Copy to (and including) null
  6940.          movsb
  6941.          .UNTIL BYTE PTR [si-1] == 0
  6942.  
  6943.  ; Copy file name to status line
  6944.  
  6945.          mov     si, OFFSET fiFiles.FName    ; Point to source name
  6946.          mov     di, OFFSET stFile[FILE_POS] ; Point to status line
  6947.  
  6948.          sub     cx, cx                  ; Count characters
  6949.          .REPEAT
  6950.          lodsb                           ; Copy to (but excluding) null
  6951.          .BREAK .IF al == 0
  6952.          stosb
  6953.          inc     cx
  6954.          .UNTIL  0
  6955.  
  6956.          mov     bx, 12                  ; Calculate blank spaces to fill
  6957.          sub     bx, cx
  6958.          mov     al, ' '                 ; Fill rest of name space with blanks
  6959.          mov     cx, bx
  6960.          rep     stosb
  6961.  
  6962.  ; Skip any file that is larger than 64K
  6963.  
  6964.          .IF     WORD PTR fiFiles.len[2] != 0 ; Error if high word isn't zero
  6965.          mov     bCsrSta, 1              ; Cursor hidden, position unchanged
  6966.  
  6967.          @ShowStr stMsg3                 ; Display error string and file name
  6968.          @Write   fiFiles.FName, cx, 1
  6969.  
  6970.          .IF     cFiles                  ; If files remaining,
  6971.          @GetChar 0                      ;  get a key
  6972.          .ENDIF
  6973.          .ENDIF
  6974.  
  6975.  ; Allocate dynamic memory for file buffer
  6976.  
  6977.          mov     ax, WORD PTR fiFiles.Len[0] ; Get length
  6978.          mov     cbBuf, ax               ; Save
  6979.          mov     offBuf, 0
  6980.          mov     cl, 4                   ; Convert to paragraphs
  6981.          shr     ax, cl
  6982.          inc     ax                      ; Zero adjust
  6983.  
  6984.          @GetBlock ax                    ; Try to allocate 64K
  6985.          .IF     carry?                  ; Display error and quit if
  6986.          @ShowStr stMsg4                 ;  request failed
  6987.          jmp     Quit
  6988.          .ENDIF
  6989.          mov     segBuf, ax              ; Save buffer segment
  6990.  
  6991.  ; Open file and read contents into buffer
  6992.  
  6993.          @OpenFile stFiles, 0            ; Try to open response
  6994.          jc      NoFile                  ; If fail, get a new file
  6995.          mov     hFileIn, ax             ; Save handle
  6996.  
  6997.          push    ds
  6998.          @Read   fpBuf, cbBuf, hFileIn  ; Read file
  6999.          pop     ds
  7000.          .IF     carry?
  7001.          jmp     NoFile                  ; If read error try again
  7002.          .ENDIF
  7003.  
  7004.  ; Search back for EOF marker and adjust        if necessary
  7005.  
  7006.          mov     di, cbBuf               ; Load file length
  7007.          dec     di                      ;   and adjust
  7008.          mov     es, segBuf
  7009.          std                             ; Look backward for 255 characters
  7010.          mov     cx, 0FFh
  7011.          .IF     cx >= di
  7012.          mov     cx, di
  7013.          .ENDIF
  7014.  
  7015.          mov     al, 1Ah                 ; Search for EOF marker
  7016.          repne   scasb
  7017.          cld
  7018.          .IF     cx != 0                 ; If found:
  7019.          inc     di                      ; Adjust and save file size
  7020.          mov     cbBuf, di
  7021.          .ENDIF
  7022.  
  7023.  ; Show a screen of text and allow commands
  7024.  
  7025.          INVOKE  Show
  7026.  
  7027.          @CloseFile hFileIn              ; Yes? Close file
  7028.          @FreeBlock segBuf               ; Release buffer
  7029.  
  7030.          @GetNext
  7031.  
  7032.          .IF     carry?
  7033.          dec     cFiles
  7034.          .ENDIF
  7035.          .UNTIL  !cFiles
  7036.  
  7037.  ; Fall through to Quit
  7038.  
  7039.  Quit    PROC
  7040.  
  7041.          cmp     bCsrSta, 1              ; Check cursor status
  7042.          jg      csrvislast              ; 2 - Make cursor visible on last lin
  7043.          je      csrvis                  ; 1 - Make cursor visible
  7044.          jmp     csrasis                 ; 0 - Leave cursor as is
  7045.  
  7046.  csrvislast:
  7047.          mov     dx, yMax                ; Load last row and first column
  7048.          xchg    dl, dh
  7049.          mov     cx, dx                  ; Make row the same
  7050.          mov     dl, 79
  7051.          @Scroll 0, atInit               ; Clear last line to original color
  7052.          sub     dl, dl                  ; Column 0
  7053.          @SetCsrPos                      ; Set cursor
  7054.  csrvis:                                 ; Fall through
  7055.                                          ; Restore cursor attribute
  7056.          @SetCsrSize <BYTE PTR shCsr[1]>, <BYTE PTR shCsr[0]>
  7057.  
  7058.  csrasis:
  7059.          .IF     fNewVid == 1
  7060.          @SetMode iMode                  ; Restore video mode, page, and curso
  7061.          @SetPage iPage
  7062.          .ENDIF
  7063.  
  7064.          .EXIT   0                       ; Quit
  7065.  
  7066.  Quit    ENDP
  7067.  
  7068.  
  7069.  Show    PROC
  7070.  
  7071.  ; Display first page
  7072.  
  7073.          mov     yCur, 1                 ; Reinitialize
  7074.          INVOKE  Pager,                  ; Start at 0
  7075.                  0
  7076.  
  7077.  ; Handle keys
  7078.  
  7079.          .REPEAT
  7080.  
  7081.          @GetChar 0, 0, 0                ; Get a key
  7082.  
  7083.          .BREAK .IF al == 27             ; If ESCAPE get out for next file
  7084.  
  7085.          ; If null or E0 (for extended keyboard), it's an extended key
  7086.          .IF     (al == 0) || (al == 0E0h)
  7087.          @GetChar 0, 0, 0                ; Get extended code
  7088.          .ENDIF
  7089.  
  7090.          push    ds                      ; ES = DS
  7091.          pop     es
  7092.          mov     di, OFFSET achKeys      ; Load address and length of key list
  7093.          mov     cx, LENGTHOF achKeys + 1
  7094.          repne   scasb                   ; Find position and point to key
  7095.          sub     di, OFFSET achKeys + 1
  7096.          shl     di, 1                   ; Adjust pointer for word addresses
  7097.          call    afnKeys[di]             ; Call procedure
  7098.          .UNTIL  0
  7099.  
  7100.          ret
  7101.  Show    ENDP
  7102.  
  7103.  HomeKey:
  7104.          mov     offBuf, 0               ; HOME - set position to 0
  7105.          mov     yCur, 1
  7106.          INVOKE  Pager, offBuf
  7107.          retn
  7108.  
  7109.  UpKey:
  7110.          INVOKE  Pager, -1               ; UP - scroll backward 1 line
  7111.          retn
  7112.  
  7113.  PgUpKey:
  7114.          mov     ax, yMax                ; PGUP - Page back
  7115.          neg     ax
  7116.          INVOKE  Pager, ax
  7117.          retn
  7118.  
  7119.  EndKey:
  7120.          mov     ax, cbBuf               ; END - Get last byte of file
  7121.          dec     ax                      ; Zero adjust
  7122.          mov     offBuf, ax              ; Make it the file position
  7123.          mov     yCur, -1                ; Set illegal line number as flag
  7124.          mov     ax, yMax                ; Page back
  7125.          neg     ax
  7126.          INVOKE  Pager, ax
  7127.          retn
  7128.  
  7129.  DownKey:
  7130.          INVOKE  Pager, 1                ; DOWN - scroll forward 1 line
  7131.          retn
  7132.  
  7133.  PgDnKey:
  7134.          INVOKE  Pager, yMax             ; PGDN - page forward
  7135.          retn
  7136.  
  7137.  UnknownKey:
  7138.          retn                            ; Ignore unknown key
  7139.  
  7140.  
  7141.  ;* GetVid - Gets the video mode and sets related global variables.
  7142.  ;*
  7143.  ;* Params: None
  7144.  ;*
  7145.  ;* Return: Number of lines in current mode (25, 43, or 50)
  7146.  
  7147.  GetVid  PROC
  7148.  
  7149.  ; Adjust for current mode and and video adapter
  7150.  
  7151.          INVOKE  IsEGA                   ; EGA (or VGA)?
  7152.          .IF     ax != 0                 ; If 0 must be CGA or MA
  7153.          mov     yMax, ax                ; Load rows
  7154.          dec     fCGA                    ; Not CGA
  7155.          .ENDIF
  7156.  
  7157.          @GetMode                        ; Get video mode
  7158.          mov     iMode, al               ; Save initial mode and page
  7159.          mov     iPage, bh
  7160.          mov     dl, al                  ; Work on copy
  7161.          cmp     dl, 7                   ; Is it mono 7?
  7162.          je      loadmono                ; Yes? Set mono
  7163.          cmp     dl, 15                  ; Is it mono 15?
  7164.          jne     graphchk                ; No? Check graphics
  7165.  loadmono:
  7166.          mov     segVid, SEG_MONO        ; Load mono address
  7167.          mov     atSta, STAT_BW          ; Set B&W defaults for status line
  7168.          mov     atScr, SCRN_BW          ;   and screen background
  7169.          dec     fCGA                    ; Not CGA
  7170.          cmp     al, 15                  ; Is it mono 15?
  7171.          jne     exit                    ; No? Done
  7172.          mov     dl, 7                   ; Yes? Set standard mono
  7173.          jmp     chmode
  7174.  graphchk:
  7175.          cmp     dl, 7                   ; 7 or higher?
  7176.          jg      color                   ; 8 to 14 are color (7 and 15 done)
  7177.          cmp     dl, 4                   ; 4 or higher?
  7178.          jg      bnw                     ; 5 and 6 are probably black and whit
  7179.          je      color                   ; 4 is color
  7180.          test    dl, 1                   ; Even?
  7181.          jz      bnw                     ; 0 and 2 are black and white
  7182.  color:                                  ; 1 and 3 are color
  7183.          cmp     dl, 3                   ; 3?
  7184.          je      exit                    ; Yes? Done
  7185.          mov     dl, 3                   ; Change mode to 3
  7186.          jmp     chmode
  7187.  bnw:
  7188.          mov     atSta, STAT_BW          ; Set B&W defaults for status line
  7189.          mov     atScr, SCRN_BW          ;   and screen background
  7190.          cmp     dl, 2                   ; 2?
  7191.          je      exit                    ; Yes? Done
  7192.          mov     dl, 2                   ; Make it 2
  7193.  chmode:
  7194.          @SetMode dl                     ; Set video mode
  7195.          @SetPage 0                      ; Set video page
  7196.          mov     fNewVid, 1              ; Set flag
  7197.  exit:
  7198.          @GetCsr                         ; Get cursor shape (ignore position)
  7199.          mov     shCsr, cx               ; Save shape
  7200.          @GetCharAtr                     ; Read the cell at the cursor
  7201.          mov     atInit, ah              ; Save attribute
  7202.          @SetCsrSize 20h, 20h            ; Turn off cursor (invisible shape)
  7203.  
  7204.          ret
  7205.  
  7206.  GetVid  ENDP
  7207.  
  7208.  
  7209.          END
  7210.  
  7211.  
  7212.  SHOWUTIL.ASM
  7213.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\SHOW\SHOWUTIL.ASM
  7214.  
  7215.  ;* SHOWUTIL.ASM - Module containing routines used by both the real and
  7216.  ;* protected mode versions of SHOW. Works with main module SHOWR.ASM or
  7217.  ;* SHOWP.ASM and with PAGERR.ASM or PAGERP.ASM.
  7218.  
  7219.          TITLE   ShowUtil
  7220.          .MODEL  small, pascal
  7221.  
  7222.          INCLUDE show.inc
  7223.  
  7224.          .CODE
  7225.  
  7226.  ;* GetNamePos - Given a file specification potentially including file name,
  7227.  ;* directory, and/or drive, return the position of the first character
  7228.  ;* of the file name.
  7229.  ;*
  7230.  ;* Params: pchSpec - address of file spec
  7231.  ;*
  7232.  ;* Return: Near pointer to position of name portion of file spec
  7233.  
  7234.  GetNamePos PROC USES di si,
  7235.          pchSpec:PTR BYTE
  7236.  
  7237.          push    ds
  7238.          pop     es
  7239.          mov     di, pchSpec             ; Load address of file name
  7240.          mov     si, di                  ; Save copy
  7241.  
  7242.          sub     cx, cx                  ; Use CX as count
  7243.          sub     dx, dx                  ; Use DX as found flag
  7244.          sub     ax, ax                  ; Search for null
  7245.  
  7246.          .REPEAT
  7247.          .IF BYTE PTR es:[di] == '\'     ; For each backslash:
  7248.          mov     si, di                  ; Save position
  7249.          inc     dx                      ; Set flag to true
  7250.          .ENDIF
  7251.          inc     cx                      ; Count it
  7252.          scasb                           ; Get next character
  7253.          .UNTIL  zero?
  7254.  
  7255.          .IF     dx != 0                 ; If found backslash:
  7256.          mov     ax, si                  ; Return position in AX
  7257.          dec     ax
  7258.  
  7259.          .ELSE                           ; Else search for colon
  7260.          mov     di, si                  ; Restore start of name
  7261.          mov     ax, ":"                 ; Search for colon
  7262.          repne   scasb
  7263.  
  7264.          .IF     zero?                   ; If colon:
  7265.          mov     ax, di                  ; Return position in DX:AX
  7266.          .ELSE                           ; Else:
  7267.          mov     ax, si                  ; Return original address
  7268.          .ENDIF
  7269.          .ENDIF
  7270.  
  7271.          ret
  7272.  
  7273.  GetNamePos ENDP
  7274.  
  7275.  
  7276.  ;* GoBack - Purpose   Searches backward through buffer
  7277.  ;*
  7278.  ;* Params:   CX has number of lines
  7279.  ;*           ES:DI has buffer position
  7280.  ;*           AL has 10 (line feed character)
  7281.  ;*
  7282.  ;* Return:   None
  7283.  ;*
  7284.  ;* Modifies: Updates yCur and offBuf
  7285.  
  7286.  GoBack  PROC
  7287.  
  7288.          neg     cx                      ; Make count positive
  7289.          mov     dx, cx                  ; Save a copy
  7290.          inc     cx                      ; One extra to go up one
  7291.          .IF     di == 0                 ; If start of file, done
  7292.          ret
  7293.          .ENDIF
  7294.  
  7295.          .REPEAT
  7296.          push    cx                      ; Save count
  7297.          mov     cx, 0FFh                ; Load maximum character count
  7298.          .IF     cx >= SWORD PTR di      ; If near start of buffer,
  7299.          mov     cx, di                  ;   search only to start
  7300.          .ENDIF
  7301.          std                             ; Go backward
  7302.          repne   scasb                   ; Find last previous LF
  7303.          cld                             ; Go foreward
  7304.          jcxz    atstart                 ; If not found, must be at start
  7305.          pop     cx
  7306.          .UNTILCXZ
  7307.  
  7308.          .IF     yCur == 0FFFFh          ; IF end of file flag:
  7309.          add     di, 2                   ; Adjust for cr/lf
  7310.          mov     offBuf, di              ; Save position
  7311.          call    EndCount                ; Count back to get line number
  7312.          mov     yCur, ax                ; Store line count
  7313.          ret
  7314.          .ENDIF
  7315.  
  7316.          sub     yCur, dx                ; Calculate line number
  7317.          jg      positive
  7318.          mov     yCur, 1                 ; Set to 1 if negative
  7319.  positive:
  7320.          add     di, 2                   ; Adjust for cr/lf
  7321.          mov     offBuf, di              ; Save position
  7322.          ret
  7323.  atstart:
  7324.          pop     cx
  7325.          sub     di, di                  ; Load start of file
  7326.          mov     yCur, 1                 ; Line 1
  7327.          mov     offBuf, di              ; Save position
  7328.          ret
  7329.  
  7330.  GoBack  ENDP
  7331.  
  7332.  
  7333.  ;* GoForeward - Skips forward through a buffer of text a specified
  7334.  ;* number of lines.
  7335.  ;*
  7336.  ;* Params:  CX - number of text lines to skip
  7337.  ;*          ES:DI - starting buffer position
  7338.  ;*          AL has 10 (line feed character)
  7339.  ;*
  7340.  ;* Return:  None
  7341.  ;*
  7342.  ;* Modifes: yCur, offBuf, bx, cx, di
  7343.  
  7344.  GoForeward PROC
  7345.  
  7346.          cld                             ; Go forward
  7347.          mov     dx, cx                  ; Copy count
  7348.  
  7349.          .REPEAT
  7350.          push    cx                      ; Save count
  7351.          mov     cx, 0FFh                ; Load maximum character count
  7352.          mov     bx, cbBuf               ; Get end of file
  7353.  
  7354.          sub     bx, di                  ; Characters to end of file
  7355.          .IF     cx >= bx                ; If less than maximum per line:
  7356.          mov     cx, bx                  ; Adjust
  7357.          .ENDIF
  7358.  
  7359.          repne   scasb                   ; Find next LF
  7360.          pop     cx
  7361.  
  7362.  
  7363.          .IF     !zero? || (di >= cbBuf) ; If LF not found or beyond end:
  7364.          mov     di, offBuf              ; Restore original position
  7365.          ret                             ;  and quit
  7366.          .ENDIF
  7367.          .UNTILCXZ
  7368.  
  7369.          add     yCur, dx                ; Calulate line number
  7370.          mov     offBuf, di              ; Save position
  7371.          ret
  7372.  
  7373.  GoForeward ENDP
  7374.  
  7375.  
  7376.  ;* EndCount - Skips backward through a buffer of text, counting each
  7377.  ;* text line.
  7378.  ;*
  7379.  ;* Params: ES:DI - buffer position (end of file)
  7380.  ;*
  7381.  ;* Return: Number of lines counted
  7382.  
  7383.  EndCount PROC USES di dx cx
  7384.  
  7385.          std                             ; Backward
  7386.          mov     al, 13                  ; Search for CR
  7387.          mov     dx, -1                  ; Initialize (first will inc to 0)
  7388.  
  7389.          .REPEAT
  7390.          inc     dx                      ; Adjust count
  7391.          mov     cx, 0FFh                ; Load maximum character count
  7392.  
  7393.          .IF     SWORD PTR cx >= di      ; If near start of buffer:
  7394.          mov     cx, di                  ; Search only to start
  7395.          .ENDIF
  7396.  
  7397.          repne   scasb                   ; Find last previous cr
  7398.          .UNTIL  !zero?                  ; If not found, must be at start
  7399.  
  7400.          mov     ax, dx                  ; Return count
  7401.          cld                             ; Forward
  7402.          ret
  7403.  
  7404.  EndCount ENDP
  7405.  
  7406.  
  7407.  ;* BinToStr - Converts an unsigned integer to a string. User is
  7408.  ;* responsible for providing a large enough buffer. The string is
  7409.  ;* not null-terminated.
  7410.  ;*
  7411.  ;* Params: i - Integer to be converted
  7412.  ;*         pch - Pointer to character buffer to receive string
  7413.  ;*
  7414.  ;* Return: Number of character in string.
  7415.  
  7416.  BinToStr PROC,
  7417.          i:WORD,
  7418.          pch:PTR BYTE
  7419.  
  7420.          mov     ax, i
  7421.          mov     di, pch
  7422.  
  7423.          sub     cx, cx                  ; Clear counter
  7424.          mov     bx, 10                  ; Divide by 10
  7425.  
  7426.  ; Convert and save on stack backwards
  7427.  
  7428.          .REPEAT
  7429.          sub     dx, dx                  ; Clear top
  7430.          div     bx                      ; Divide to get last digit as remaind
  7431.          add     dl, "0"                 ; Convert to ASCII
  7432.          push    dx                      ; Save on stack
  7433.          .UNTILCXZ ax == 0               ; Until quotient is 0
  7434.  
  7435.  ; Take off the stack and store forward
  7436.  
  7437.          neg     cx                      ; Negate and save count
  7438.          mov     dx, cx
  7439.  
  7440.          .REPEAT
  7441.          pop     ax                      ; Get character
  7442.          stosb                           ; Store it
  7443.          .UNTILCXZ
  7444.          mov     ax, dx                  ; Return digit count
  7445.  
  7446.          ret
  7447.  
  7448.  BinToStr ENDP
  7449.  
  7450.  
  7451.          END
  7452.  
  7453.  
  7454.  SNAP.ASM
  7455.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM6\TSR\SNAP.ASM
  7456.  
  7457.          .MODEL  small, pascal, os_dos
  7458.          .DOSSEG
  7459.          INCLUDE demo.inc
  7460.          INCLUDE tsr.inc
  7461.  
  7462.  OpenBox         PROTO
  7463.  CloseBox        PROTO
  7464.  
  7465.          .STACK
  7466.          .DATA
  7467.  
  7468.  DEFAULT_COLR    EQU     1Eh             ; Default = white on blue (color)
  7469.  DEFAULT_MONO    EQU     70h             ; Default = reverse video (mono)
  7470.  
  7471.  ; Set ALT + LEFT SHIFT + S as hot key combination. To set multiple shift
  7472.  ; keys, OR the appropriate values together for the shift value (HOT_SHIFT).
  7473.  
  7474.  HOT_SCAN        EQU     1Fh             ; Hot key scan code (S)
  7475.  HOT_SHIFT       EQU     shAlt OR shLeft ; Shift value (ALT + LEFT SHIFT)
  7476.  HOT_MASK        EQU     (shIns OR shCaps OR shNum OR shScroll) XOR 0FFh
  7477.  
  7478.  ROW1            EQU     9               ; Query box begins on row 9
  7479.  ROW2            EQU     14              ;  and ends on row 14
  7480.  HEIGHT          EQU     ROW2 - ROW1 + 1 ; Number of rows in query box
  7481.  
  7482.  Box     BYTE    '┌──────────────────────────────────────┐', 0
  7483.          BYTE    '│  Enter file name                     │', 0
  7484.          BYTE    '│  (press Esc to cancel):              │', 0
  7485.          BYTE    '│                                      │', 0
  7486.          BYTE    '│                                      │', 0
  7487.  boxend  BYTE    '└──────────────────────────────────────┘', 0
  7488.  LEN     EQU     (LENGTHOF boxend) - 1
  7489.  
  7490.  OldPos  WORD    ?                       ; Original cursor position
  7491.  Handle  WORD    ?                       ; File handle number
  7492.  FilSpec BYTE    (LEN - 3) DUP(0)        ; ASCIIZ string for file spec
  7493.  
  7494.  ; Fill attribute for prompt box. This is changed by running SNAP with
  7495.  ; the /Cx switch, where x = new display attribute in hexadecimal. For
  7496.  ; example, to change the color to yellow on brown for a color monitor,
  7497.  ; enter
  7498.  ;         SNAP /C6E
  7499.  ; where the first digit specifies the background color and the second
  7500.  ; digit the foreground color. Typical values for x on a monochrome
  7501.  ; system are
  7502.  ;       07 normal                 70 reverse video
  7503.  ;       0F high intensity         78 reverse video, high intensity
  7504.  
  7505.  BoxFill BYTE    DEFAULT_MONO            ; Assume monochrome
  7506.  
  7507.  ; Hold contains the screen text and attributes replaced by the query box.
  7508.  ; Buffer holds text captured from the screen, with room for 50 rows of 82
  7509.  ; characters, including carriage return/linefeed. To change Buffer's
  7510.  ; capacity, replace the dimensions with r * (c + 2) DUP(?), where r and
  7511.  ; c are row and column count respectively.
  7512.  
  7513.  Hold    BYTE    (HEIGHT * LEN) + 3 DUP(?)
  7514.  Buffer  BYTE    50 * 82 DUP(?)
  7515.  
  7516.  
  7517.          .CODE
  7518.  
  7519.  ;* Snap - Main procedure for resident program. Called from the Activate
  7520.  ;* procedure when TSR is invoked by the proper key combination.
  7521.  ;*
  7522.  ;* Params:  DS, ES = @data
  7523.  ;*
  7524.  ;* Return:  None
  7525.  
  7526.  Snap    PROC    FAR
  7527.  
  7528.          INVOKE  GetVidConfig            ; Get video information
  7529.  
  7530.          mov     al, vconfig.mode        ; AL = video mode
  7531.          .IF     (al <= 3) || (al == 7)  ; If text mode:
  7532.  
  7533.          INVOKE  GetCurPos               ; Get original cursor coordinates
  7534.          mov     OldPos, ax              ;   and store them
  7535.  
  7536.          INVOKE  OpenBox                 ; Display query box
  7537.  
  7538.          mov     bl, vconfig.cols        ; Calculate column
  7539.          sub     bl, LEN
  7540.          shr     bl, 1
  7541.          add     bl, 3
  7542.  
  7543.          INVOKE  StrInput,               ; Request input
  7544.                  ROW1 + 4,               ; Row
  7545.                  bl,                     ; Column
  7546.                  LEN - 4,                ; Maximum string
  7547.                  ADDR FilSpec            ; Address of string buffer
  7548.  
  7549.          push    ax                      ; Save terminating keypress
  7550.          call    CloseBox                ; Restore screen to original state
  7551.          pop     ax                      ; Recover key
  7552.          .IF     al != ESCAPE            ; If Esc key not pressed:
  7553.          call    OpenFile                ; Open (or create) file
  7554.  
  7555.          .IF     !carry?                 ; If okay:
  7556.          call    Capture                 ; Write screen to file
  7557.          .ELSE
  7558.          mov     ax, 0E07h               ; Write bell character
  7559.          int     10h                     ;   (ASCII 7) to console
  7560.          .ENDIF                          ; End file-okay test
  7561.          .ENDIF                          ; End ESCAPE test
  7562.  
  7563.          mov     ax, OldPos              ; Recover original cursor position
  7564.          mov     bl, ah
  7565.  
  7566.          INVOKE  SetCurPos,              ; Restore cursor
  7567.                  bx, ax                  ; Pass cursor row and column
  7568.  
  7569.          .ENDIF                          ; End text mode test
  7570.  
  7571.          retf                            ; Far return to Activate procedure
  7572.  
  7573.  Snap    ENDP
  7574.  
  7575.  
  7576.  ;* OpenBox - Saves portion of screen to Hold buffer, then opens a box.
  7577.  ;*
  7578.  ;* Uses:    vconfig - Video configuration structure
  7579.  ;*
  7580.  ;* Params:  None
  7581.  ;*
  7582.  ;* Return:  None
  7583.  
  7584.  OpenBox PROC
  7585.  
  7586.          mov     dh, ROW1                ; DH = top screen row for box
  7587.          mov     dl, vconfig.cols
  7588.          sub     dl, LEN
  7589.          shr     dl, 1                   ; DL = left col for centered box
  7590.          push    dx                      ; Save coords
  7591.          sub     ch, ch
  7592.          mov     cl, dh                  ; CX = row
  7593.          sub     dh, dh                  ; DX = column
  7594.          GetVidOffset cx, dx
  7595.          mov     si, ax                  ; Get video offset in SI
  7596.          mov     bx, HEIGHT              ; BX = number of window rows
  7597.          mov     cx, LEN                 ; CX = number of columns
  7598.  
  7599.          push    ds
  7600.          pop     es
  7601.          mov     di, OFFSET Hold         ; Point ES:DI to hold buffer
  7602.          mov     ax, si
  7603.          stosw                           ; Copy video offset to buffer
  7604.          mov     ax, bx
  7605.          stosw                           ; Number of rows to buffer
  7606.          mov     ax, cx
  7607.          stosw                           ; Number of cols to buffer
  7608.          mov     al, vconfig.cols
  7609.          shl     ax, 1                   ; AX = number of video cells/row
  7610.          mov     ds, vconfig.sgmnt       ; DS = video segment
  7611.  
  7612.          .REPEAT
  7613.          push    si                      ; Save ptr to start of line
  7614.          push    cx                      ;   and number of columns
  7615.          .IF     vconfig.adapter == CGA  ; If CGA adapter:
  7616.          INVOKE  DisableCga              ; Disable video
  7617.          .ENDIF
  7618.          rep     movsw                   ; Copy one row to buffer
  7619.          .IF     vconfig.adapter == CGA  ; If CGA adapter:
  7620.          INVOKE  EnableCga               ; Reenable CGA video
  7621.          .ENDIF
  7622.          pop     cx                      ; Recover number of columns
  7623.          pop     si                      ;   and start of line
  7624.          add     si, ax                  ; Point to start of next line
  7625.          dec     bx                      ; Decrement row counter
  7626.          .UNTIL  zero?                   ; Loop while rows remain
  7627.  
  7628.  ; Screen contents (including display attributes) are now copied to buffer.
  7629.  ; Next open window, overwriting the screen portion just saved.
  7630.  
  7631.          push    es
  7632.          pop     ds                      ; Restore DS
  7633.  
  7634.          mov     ax, 0600h               ; Scroll service
  7635.          mov     bh, BoxFill             ; BH = fill attribute
  7636.          pop     cx                      ; CX = row/col for upper left
  7637.          mov     dh, ROW2
  7638.          mov     dl, cl
  7639.          add     dl, LEN
  7640.          dec     dl                      ; DX = row/col for lower right
  7641.          int     10h                     ; Blank window area on screen
  7642.  
  7643.  ; Write box frame and text to screen
  7644.  
  7645.          mov     dx, cx                  ; DX = row/col for upper left
  7646.          mov     si, OFFSET Box          ; Point to text
  7647.          mov     cx, HEIGHT              ; Number of rows in box
  7648.  
  7649.          .REPEAT
  7650.          push    dx                      ; Save coordinates
  7651.          sub     bh, bh
  7652.          mov     bl, dh                  ; BX = row
  7653.          sub     dh, dh                  ; DX = column
  7654.          INVOKE  StrWrite, bx, dx, si    ; Display one line of box
  7655.          pop     dx                      ; Recover coordinates
  7656.          inc     dh                      ; Next screen row
  7657.          add     si, LEN                 ; Point to next line in box
  7658.          inc     si
  7659.          .UNTILCXZ
  7660.  
  7661.          ret
  7662.  
  7663.  OpenBox ENDP
  7664.  
  7665.  
  7666.  ;* CloseBox - Restores the original screen text to close the window
  7667.  ;* previously opened by the OpenBox procedure
  7668.  ;*
  7669.  ;* Uses:    vconfig - Video configuration structure
  7670.  ;*
  7671.  ;* Params:  None
  7672.  ;*
  7673.  ;* Return:  None
  7674.  
  7675.  CloseBox PROC
  7676.  
  7677.          mov     si, OFFSET Hold
  7678.          lodsw
  7679.          mov     di, ax                  ; DI = video offset of window
  7680.          lodsw
  7681.          mov     bx, ax                  ; BX = number of window rows
  7682.          lodsw
  7683.          mov     cx, ax                  ; CX = number of columns
  7684.  
  7685.          mov     al, vconfig.cols
  7686.          shl     ax, 1                   ; AX = number of video cells/row
  7687.  
  7688.          .REPEAT
  7689.          push    di                      ; Save ptr to start of line
  7690.          push    cx                      ;   and number of columns
  7691.          .IF     vconfig.adapter == CGA  ; If CGA adapter:
  7692.          INVOKE  DisableCga              ; Disable video
  7693.          .ENDIF
  7694.          rep     movsw                   ; Copy one row to buffer
  7695.          .IF     vconfig.adapter == CGA  ; If CGA adapter:
  7696.          INVOKE  EnableCga               ; Reenable CGA video
  7697.          .ENDIF
  7698.          pop     cx                      ; Recover number of columns
  7699.          pop     di                      ;   and start of line
  7700.          add     di, ax                  ; Point to start of next line
  7701.          dec     bx                      ; Decrement row counter
  7702.          .UNTIL  zero?                   ; Loop while rows remain
  7703.  
  7704.          ret
  7705.  
  7706.  CloseBox ENDP
  7707.  
  7708.  
  7709.  ;* OpenFile - Opens or creates specified file. Resets file pointer to
  7710.  ;* end of file so that subsequent text is appended to bottom of file.
  7711.  ;*
  7712.  ;* Params:  DS:SI = Pointer to file spec
  7713.  ;*
  7714.  ;* Return:  None
  7715.  
  7716.  OpenFile PROC
  7717.  
  7718.          mov     ax, 3D01h               ; Request DOS to open file
  7719.          mov     dx, OFFSET FilSpec      ; DS:DX points to file specification
  7720.          int     21h                     ; Open File
  7721.          .IF     carry?                  ; If it doesn't exist:
  7722.          mov     ah, 3Ch                 ; Request create file
  7723.          sub     cx, cx                  ;   with normal attributes
  7724.          int     21h                     ; Create File
  7725.          .ENDIF
  7726.  
  7727.          .IF     !carry?                 ; If no error:
  7728.          mov     Handle, ax              ; Store file handle
  7729.          mov     bx, ax
  7730.          mov     ax, 4202h               ; Request DOS to reset file pointer
  7731.          sub     cx, cx                  ;   to end of file
  7732.          sub     dx, dx
  7733.          int     21h                     ; Set File Pointer
  7734.          .ENDIF
  7735.          ret
  7736.  
  7737.  OpenFile ENDP
  7738.  
  7739.  
  7740.  ;* Capture - Copies screen text to Buffer, then writes Buffer to file.
  7741.  ;*
  7742.  ;* Uses:    vconfig - Video configuration structure
  7743.  ;*
  7744.  ;* Params:  None
  7745.  ;*
  7746.  ;* Return:  None
  7747.  
  7748.  Capture PROC
  7749.  
  7750.          mov     es, vconfig.sgmnt       ; ES points to video segment address
  7751.          sub     si, si                  ; ES:SI points to 1st video byte
  7752.          sub     bx, bx                  ; BX = index to capture buffer
  7753.          mov     dx, 3DAh                ; DX = address of CGA status register
  7754.  
  7755.          .REPEAT
  7756.          sub     ch, ch
  7757.          mov     cl, vconfig.cols        ; CX = number of columns in line
  7758.          mov     di, cx
  7759.          dec     di
  7760.          shl     di, 1                   ; ES:DI points to video byte for
  7761.          add     di, si                  ;   last column in line
  7762.  
  7763.          .REPEAT
  7764.          .IF     vconfig.adapter == CGA  ; If CGA:
  7765.          cli                             ; Disallow interruptions
  7766.          .REPEAT
  7767.          in      al, dx                  ; Read current video status
  7768.          .UNTIL  !(al & 1)               ;   until horizontal retrace done
  7769.          .REPEAT
  7770.          in      al, dx                  ; Read video status
  7771.          .UNTIL  al & 1                  ;   until horizontal retrace starts
  7772.          .ENDIF                          ; End CGA retrace check
  7773.  
  7774.          mov     al, es:[di]             ; Get screen char, working backward
  7775.          sti                             ; Reenable interrupts in case CGA
  7776.          sub     di, 2                   ; DI points to next character
  7777.          .UNTILCXZ (al != ' ')           ; Scan for last non-blank character
  7778.  
  7779.          .IF     !zero?                  ; If non-blank char found:
  7780.          inc     cx                      ; Adjust column counter
  7781.          mov     di, si                  ; ES:DI points to start of line
  7782.  
  7783.          .REPEAT
  7784.          .IF     vconfig.adapter == CGA  ; If CGA:
  7785.          cli                             ; Disallow interruptions
  7786.          .REPEAT
  7787.          in      al, dx                  ; Read current video status
  7788.          .UNTIL  !(al & 1)               ;   until horizontal retrace done
  7789.          .REPEAT
  7790.          in      al, dx                  ; Read video status
  7791.          .UNTIL  al & 1                  ;   until horizontal retrace starts
  7792.          .ENDIF                          ; End CGA retrace check
  7793.  
  7794.          mov     al, es:[di]             ; Get character, working forward
  7795.          sti
  7796.          add     di, 2                   ; DI points to next character
  7797.          mov     Buffer[bx], al          ; Copy to buffer
  7798.          inc     bx
  7799.          .UNTILCXZ
  7800.          .ENDIF                          ; End check for non-blank char
  7801.  
  7802.          mov     WORD PTR Buffer[bx], CRLF; Finish line with return/line feed
  7803.          add     bx, 2
  7804.          mov     al, vconfig.cols
  7805.          sub     ah, ah
  7806.          shl     ax, 1
  7807.          add     si, ax                  ; SI points to start of next line
  7808.          dec     vconfig.rows            ; Decrement row count
  7809.          .UNTIL  sign?                   ; Repeat for next screen row
  7810.  
  7811.          mov     ah, 40h                 ; Request DOS Function 40h
  7812.          mov     cx, bx                  ; CX = number of bytes to write
  7813.          mov     bx, Handle              ; BX = file handle
  7814.          mov     dx, OFFSET Buffer       ; DS:DX points to buffer
  7815.          int     21h                     ; Write to File
  7816.          .IF     (ax != cx)              ; If number of bytes written !=
  7817.          stc                             ;   number requested, set carry
  7818.          .ENDIF                          ;   flag to indicate failure
  7819.  
  7820.          pushf                           ; Save carry flag
  7821.          mov     ah, 3Eh                 ; Request DOS Function 3Eh
  7822.          int     21h                     ; Close File
  7823.          popf                            ; Recover carry
  7824.          ret
  7825.  
  7826.  Capture ENDP
  7827.  
  7828.  
  7829.  @CurSeg ENDS
  7830.  
  7831.  ;* INSTALLATION SECTION - The following code and data are used only
  7832.  ;* during SNAP's installation phase. When the program terminates
  7833.  ;* through Function 31h, the above code and data remain resident;
  7834.  ;* memory occupied by the following code and data segments is returned
  7835.  ;* to the operating system.
  7836.  
  7837.  DGROUP  GROUP INSTALLCODE, INSTALLDATA
  7838.  
  7839.  INSTALLDATA SEGMENT WORD PUBLIC 'DATA2'
  7840.  
  7841.  IDstr   BYTE    'SNAP DEMO TSR', 0      ; Multiplex identifier string
  7842.  
  7843.  INSTALLDATA ENDS
  7844.  
  7845.  INSTALLCODE SEGMENT PARA PUBLIC 'CODE2'
  7846.          ASSUME  ds:@data
  7847.  
  7848.  Begin   PROC    NEAR
  7849.  
  7850.          mov     ax, DGROUP
  7851.          mov     ds, ax                  ; Initialize DS
  7852.          mov     ah, 15
  7853.          int     10h                     ; Get Video Mode
  7854.          .IF     al != 7                 ; If not default monochrome:
  7855.          mov     BoxFill, DEFAULT_COLR   ; Reset to default color value
  7856.          .ENDIF
  7857.  
  7858.  ; Before calling any of the TSR procedures, initialize global data
  7859.  
  7860.          INVOKE  InitTsr,                ; Initialize data
  7861.                  es,                     ; Segment of PSP
  7862.                  ADDR IDstr,             ; Far address of multiplex ID string
  7863.                  ADDR BoxFill            ; Far address of memory shared
  7864.                                          ;  with multiplex handler
  7865.          .IF     ax == WRONG_DOS         ; If DOS version less than 2.0:
  7866.          jmp     exit                    ; Exit with message
  7867.          .ENDIF
  7868.  
  7869.  ; This section gets the command line argument to determine task:
  7870.  ;    No argument   = install
  7871.  ;    /D or -D      = deinstall
  7872.  ;    /Cx or -Cx    = change box fill attribute to value x
  7873.  
  7874.          mov     al, 'd'                 ; Search command line for
  7875.          call    GetOptions              ;   /D or -D argument
  7876.          cmp     ax, NO_ARGUMENT         ; No argument?
  7877.          je      installtsr              ; If so, try to install
  7878.          cmp     ax, OK_ARGUMENT         ; /D argument found?
  7879.          je      deinstalltsr            ; If so, try to deinstall
  7880.          mov     al, 'c'                 ; Else search command line for
  7881.          call    GetOptions              ;   /C or -C argument
  7882.          cmp     ax, BAD_ARGUMENT        ; If neither /D or /C arguments,
  7883.          je      exit                    ;   quit with error message
  7884.  
  7885.  ; This section changes the fill attribute of SNAP's prompt box. It converts
  7886.  ; to binary the two-digit hex number following the /C argument, calls the
  7887.  ; multiplex handler to find the address of the attribute variable stored in
  7888.  ; shared memory, then resets the attribute to the new value. It does not
  7889.  ; verify that the value specified in the command line is a valid two-digit
  7890.  ; hex number.
  7891.  
  7892.          mov     ax, es:[di+1]           ; AH = low digit, AL = high digit
  7893.          mov     cx, 2                   ; Process two digits
  7894.  
  7895.          .REPEAT
  7896.          sub     al, '0'                 ; Convert digit to binary
  7897.          .IF     (al > 9)                ; If not digit 0-9:
  7898.          and     al, 00011111y           ; Mask out lower-case bit
  7899.          sub     al, 7                   ; Convert A to 10, B to 11, etc
  7900.          .ENDIF
  7901.          xchg    ah, al                  ; Get next digit in AL
  7902.          .UNTILCXZ
  7903.  
  7904.          mov     cl, 4
  7905.          shl     al, cl                  ; Multiply high digit by 16
  7906.          or      al, ah                  ; AL = binary value of attribute
  7907.          push    ax                      ; Save new attribute
  7908.  
  7909.          mov     al, 2                   ; Request function 2
  7910.          call    CallMultiplex           ; Get shared memory addr in ES:DI
  7911.          .IF     ax != IS_INSTALLED      ; If TSR is not installed:
  7912.          pop     ax                      ; Clean stack and
  7913.          mov     ax, CANT_ACCESS         ;   quit with error message
  7914.          jmp     exit
  7915.          .ELSE                           ; If TSR is installed:
  7916.          pop     ax                      ; Recover new fill attribute in AL
  7917.          mov     es:[di], al             ; Write it to resident shared memory
  7918.          mov     ax, OK_ACCESS           ; Signal successful completion
  7919.          jmp     exit
  7920.          .ENDIF
  7921.  
  7922.  ; This section sets up the TSR's interrupt handlers and
  7923.  ; makes the program memory-resident
  7924.  
  7925.  installtsr:
  7926.          push    es                      ; Preserve PSP address
  7927.  
  7928.          mov     ax, @code
  7929.          mov     es, ax
  7930.          mov     bx, OFFSET Snap         ; ES:BX points to Snap
  7931.          INVOKE  Install,                ; Install handlers
  7932.                  HOT_SCAN,               ; Scan code of hot key
  7933.                  HOT_SHIFT,              ; Bit value of hot key
  7934.                  HOT_MASK,               ; Bit mask for shift hot key
  7935.                  es::bx                   ; Far address of Snap procedure
  7936.  
  7937.          pop     bx                      ; Recover PSP address
  7938.          or      ax, ax                  ; If non-zero return code,
  7939.          jnz     exit                    ;   exit with appropriate message
  7940.          mov     ax, INSTALLCODE         ; Bottom of resident section
  7941.          sub     ax, bx                  ; AX = number of paragraphs in
  7942.                                          ;   block to be made resident
  7943.          INVOKE  KeepTsr,                ; Make TSR memory-resident
  7944.                  ax                      ; Resident paragraphs
  7945.  
  7946.  ; This section deinstalls the resident TSR from memory
  7947.  
  7948.  deinstalltsr:
  7949.  
  7950.          INVOKE  Deinstall               ; Unchain interrupt handlers
  7951.  
  7952.          .IF     ax > OK_ARGUMENT        ; If successful:
  7953.          INVOKE  FreeTsr,                ; Deinstall TSR by freeing memory
  7954.                  ax                      ; Address of resident seg
  7955.          .ENDIF                          ; Else exit with message
  7956.  exit:
  7957.          INVOKE  FatalError,             ; Exit to DOS with message
  7958.                  ax                      ; Error number
  7959.  
  7960.  Begin   ENDP
  7961.  
  7962.  INSTALLCODE ENDS
  7963.  
  7964.          END     Begin
  7965.  Microsoft MASM: Sample Code from Version 5.x
  7966.  
  7967.  
  7968.  BA.ASM
  7969.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\MIXED\BA.ASM
  7970.  
  7971.  
  7972.  .MODEL medium
  7973.  .CODE
  7974.  
  7975.  ; BASIC        function for QuickBASIC, Version 4 and future versions
  7976.  ;   of Microsoft and IBM BASIC Compilers
  7977.  
  7978.          PUBLIC        Power2
  7979.  Power2        PROC
  7980.          push        bp                ; Entry        sequence - save        o
  7981.          mov        bp,sp                ; Set stack framepointer
  7982.  
  7983.          mov        bx,[bp+8]        ; Load Arg1 into
  7984.          mov        ax,[bx]                ;   AX
  7985.          mov        bx,[bp+6]        ; Load Arg2 into
  7986.          mov        cx,[bx]                ;   CX
  7987.          shl        ax,cl                ; AX = AX * (2 to power        of CX)
  7988.                                  ; Leave        return value in        AX
  7989.  
  7990.          pop        bp                ; Restore old framepointer
  7991.          ret        4                ; Exit,        and restore 4 bytes of arg
  7992.  Power2        ENDP
  7993.  
  7994.  ; BASIC        subprogram for QuickBASIC, Versions 1, 2, and 3;
  7995.  ;     for the Microsoft        BASIC Compiler through Version 5.36
  7996.  ;     for the IBM BASIC        Compiler through Version 2.02
  7997.  
  7998.          PUBLIC        Power2S
  7999.  Power2S        PROC
  8000.          push        bp                ; Entry        sequence - save        o
  8001.          mov        bp,sp                ; Set stack framepointer
  8002.  
  8003.          mov        bx,[bp+10]        ; Load Arg1 into
  8004.          mov        ax,[bx]                ;   AX
  8005.          mov        bx,[bp+8]        ; Load Arg2 into
  8006.          mov        cx,[bx]                ;   CX
  8007.          shl        ax,cl                ; AX = AX * (2 to power        of CX)
  8008.          mov        bx,[bp+6]        ; Store        result in
  8009.          mov        [bx],ax                ;   Arg3
  8010.  
  8011.          pop        bp                ; Restore old framepointer
  8012.          ret        4                ; Exit,        and restore 4 bytes of arg
  8013.  Power2S        ENDP
  8014.          END
  8015.  
  8016.  
  8017.  
  8018.  CA.ASM
  8019.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\MIXED\CA.ASM
  8020.  
  8021.  
  8022.  .MODEL SMALL
  8023.  .CODE
  8024.          PUBLIC        _Power2
  8025.  _Power2 PROC
  8026.          push        bp        ;Entry sequence
  8027.          mov        bp,sp
  8028.  
  8029.          mov        ax,[bp+4]        ; Load Arg1 into AX
  8030.          mov        cx,[bp+6]        ; Load Arg2 into CX
  8031.          shl        ax,cl                ; AX = AX * (2 to power of CX)
  8032.                                  ; Leave return value in AX
  8033.  
  8034.          pop        bp                ; Exit sequence
  8035.          ret
  8036.  _Power2 ENDP
  8037.          END
  8038.  
  8039.  
  8040.  
  8041.  FA.ASM
  8042.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\MIXED\FA.ASM
  8043.  
  8044.  
  8045.  .MODEL large
  8046.  .CODE
  8047.          PUBLIC        Power2
  8048.  Power2        PROC
  8049.          push        bp                ; Entry sequence - save old BP
  8050.          mov         bp,sp                ; Set stack framepointer
  8051.  
  8052.          les        bx,[bp+10]        ; Load Arg1 into
  8053.          mov        ax,[bx]                ;   AX
  8054.          les        bx,[bp+6]        ; Load Arg2 into
  8055.          mov        cx,[bx]                ;   CX
  8056.          shl        ax,cl                ; AX = AX * (2 to power of CX)
  8057.                                  ; Leave return value in AX
  8058.  
  8059.          pop        bp                ; Restore old framepointer
  8060.          ret        4                ; Exit, and restore 4 bytes of args
  8061.  Power2  ENDP
  8062.          END
  8063.  
  8064.  
  8065.  PA.ASM
  8066.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\MIXED\PA.ASM
  8067.  
  8068.  
  8069.  .MODEL medium
  8070.  .CODE
  8071.          PUBLIC        Power2
  8072.  Power2        PROC
  8073.          push        bp                ; Entry sequence - save old BP
  8074.          mov         bp,sp                ; Set stack framepointer
  8075.  
  8076.          mov        ax,[bp+8]        ; Load Arg1 into AX
  8077.          mov        cx,[bp+6]        ; Load Arg2 into CX
  8078.          shl        ax,cl                ; AX = AX * (2 to power of CX)
  8079.                                  ; Leave return value in AX
  8080.  
  8081.          pop        bp                ; Restore old framepointer
  8082.          ret        4                ; Exit, and restore 4 bytes of args
  8083.  Power2  ENDP
  8084.          END
  8085.  
  8086.  
  8087.  PAGERP.ASM
  8088.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\PAGERP.ASM
  8089.  
  8090.            TITLE   Pager
  8091.            .MODEL  small, pascal
  8092.  
  8093.  INCL_VIO  EQU 1
  8094.  
  8095.            INCLUDE os2.inc
  8096.            .DATA
  8097.            EXTRN   stAtrib:BYTE, scAtrib:BYTE, Cell:WORD, stLine:BYTE
  8098.            EXTRN   sBuffer:WORD, oBuffer:WORD, Buffer:DWORD, lBuffer:WORD
  8099.            EXTRN   nLines:WORD, curLine:WORD
  8100.  
  8101.            .CODE
  8102.            PUBLIC  Pager
  8103.  
  8104.  ; Procedure Pager
  8105.  ; Purpose   Displays status and text lines
  8106.  ; Input     Stack variable: lines to scroll (negative up, positive down)
  8107.  ;           Global variables: "sbuffer", "oBuffer", "curLine"
  8108.  ; Output    To screen
  8109.  
  8110.  Pager     PROC    count
  8111.  
  8112.            mov     es, sBuffer           ; Initialize buffer position
  8113.            mov     di, oBuffer
  8114.  
  8115.            mov     cx, count             ; Get count argument
  8116.            mov     ax, 10                ; Search for linefeed
  8117.  
  8118.            or      cx, cx                ; Argument 0?
  8119.            jl      skip1                 ; If below, backward
  8120.            jg      skip2                 ; If above, forward
  8121.            jmp     SHORT skip3           ; If equal, done
  8122.  
  8123.  skip1:    call    GoBack                ; Adjust backward
  8124.            jmp     SHORT skip3           ; Show screen
  8125.  skip2:    call    GoForwd               ; Adjust forward
  8126.  
  8127.  ; Write line number to status line
  8128.  
  8129.  skip3:    cld                           ; Go forward
  8130.            push    di                    ; Save
  8131.            push    ds                    ; ES = DS
  8132.            pop     es
  8133.  
  8134.  ; BinToStr (curLine, OFFSET stLine[6])
  8135.  
  8136.            push    curLine               ; Arg 1
  8137.            @Pushc  <OFFSET stLine[6]>    ; Arg 2
  8138.            call    BinToStr              ; Convert to string
  8139.  
  8140.  ; Fill in status line
  8141.  
  8142.            mov     cx, 6                 ; Six spaces to fill
  8143.            sub     cx, ax                ; Subtract those already done
  8144.            mov     al, " "               ; Fill with space
  8145.            rep     stosb
  8146.  
  8147.            @VioWrtCharStrAtt stLine, 80, 0, 0, stAtrib, 0 ; Write to screen
  8148.  
  8149.            pop     di                    ; Update position
  8150.            mov     si, di
  8151.            mov     cx, nLines            ; Lines per screen
  8152.  
  8153.  loop1:    mov     bx, nLines            ; Lines of text
  8154.            inc     bx                    ; Adjust for 0
  8155.            sub     bx, cx                ; Calculate current row
  8156.            push    cx                    ; Save line number
  8157.  
  8158.  ; ShowLine (position, line, maxlength, &scAtrib)
  8159.  
  8160.            push    sBuffer               ; Arg 1
  8161.            push    si
  8162.            push    bx                    ; Arg 2
  8163.            push    lBuffer               ; Arg 3
  8164.            push    ds                    ; Arg r4
  8165.            @Pushc  <OFFSET scAtrib>
  8166.            call    ShowLine              ; Write line
  8167.  
  8168.            pop     cx                    ; Restore line number
  8169.            mov     si, ax                ; Get returned position
  8170.  
  8171.            cmp     ax, lBuffer           ; If beyond end of file,
  8172.            jae     skip4                 ;   fill screen with spaces
  8173.            loop    loop1                 ; else next line
  8174.            jmp     SHORT exit            ; Exit if done
  8175.  
  8176.  ; Fill the rest with spaces
  8177.  
  8178.  skip4:    dec     cx
  8179.            jcxz    exit
  8180.            mov     ax, 80                ; Columns times remaining lines
  8181.            mul     cl
  8182.            mov     dx, ax                ; Macros use AX, so use DX
  8183.            sub     cx, nLines            ; Calculate starting line
  8184.            neg     cx
  8185.            inc     cx
  8186.  
  8187.            @VioWrtNCell Cell, dx, cx, 0, 0 ; Write space cells
  8188.  
  8189.  exit:     ret
  8190.  Pager     ENDP
  8191.  
  8192.  ; Procedure ShowLine (inLine, sRow, &pAtrib)
  8193.  ; Purpose   Writes a line to screen
  8194.  ; Input     Stack variables: 1 - FAR PTR to input line
  8195.  ;                            2 - line number
  8196.  ;                            3 - maximum number of characters (file length)
  8197.  ;                            4 - FAR PTR to attribute
  8198.  ; Output    Line to screen
  8199.  
  8200.  ShowLine  PROC    USES si di, inLine:FAR PTR BYTE, srow, max, pAtrib:FAR PTR
  8201.            LOCAL   outLine[80]:BYTE
  8202.  
  8203.            push    ds                    ; Save
  8204.            push    ss                    ; ES = SS
  8205.            pop     es
  8206.            lea     di, outLine           ; Destination line
  8207.            lds     si, inLine            ; Source line
  8208.            mov     cx, 80                ; Cells per row
  8209.            mov     bx, di                ; Save copy of start for tab calc
  8210.  loop1:    lodsb                         ; Get character
  8211.            cmp     al, 9                 ; Tab?
  8212.            je      skip1                 ; Space out tab
  8213.            cmp     al, 13                ; CR?
  8214.            je      skip3                 ; Fill rest of line with spaces
  8215.            stosb                         ; Copy out
  8216.            cmp     si, max               ; Check for end of file
  8217.            ja      skip3
  8218.            loop    loop1
  8219.  
  8220.  loop2:    lodsb                         ; Throw away rest of line to truncate
  8221.            cmp     si, max               ; Check for end of file
  8222.            ja      exit
  8223.            cmp     al, 13                ; Check for end of line
  8224.            jne     loop2
  8225.            inc     si                    ; Throw away line feed
  8226.  
  8227.            jmp     SHORT exit            ; Done
  8228.  
  8229.  skip1:    push    bx                    ; Fill tab with spaces
  8230.            push    cx
  8231.  
  8232.            sub     bx, di                ; Get current position in line
  8233.            neg     bx
  8234.  
  8235.            mov     cx, 8                 ; Default count 8
  8236.            and     bx, 7                 ; Get modulus
  8237.            sub     cx, bx                ; Subtract
  8238.            mov     bx, cx                ; Save modulus
  8239.  
  8240.            mov     al, " "               ; Write spaces
  8241.            rep     stosb
  8242.  
  8243.            pop     cx
  8244.            sub     cx, bx                ; Adjust count
  8245.            jns     skip2                 ; Make negative count 0
  8246.            sub     cx, cx
  8247.  skip2:    pop     bx
  8248.            jcxz    loop2                 ; If beyond limit done
  8249.            jmp     SHORT loop1
  8250.  
  8251.  skip3:    inc     si                    ; After CR, throw away LF
  8252.            mov     al, ' '               ; Fill rest of line
  8253.            rep     stosb
  8254.  
  8255.  exit:     pop     ds
  8256.            @VioWrtCharStrAtt outLine, 80, [srow], 0, [pAtrib], 0
  8257.  
  8258.            mov     ax, si                ; Return position
  8259.            ret
  8260.  ShowLine  ENDP
  8261.  
  8262.  ; Procedure GoBack
  8263.  ; Purpose   Searches backward through buffer
  8264.  ; Input     CX has number of lines; ES:DI has buffer position
  8265.  ; Output    Updates "curLine" and "oBuffer"
  8266.  
  8267.  GoBack    PROC
  8268.            std                           ; Go backward
  8269.            neg     cx                    ; Make count positive
  8270.            mov     dx, cx                ; Save a copy
  8271.            inc     cx                    ; One extra to go up one
  8272.            or      di, di                ; Start of file?
  8273.            je      exit                  ; If so, ignore
  8274.  loop1:    push    cx                    ;   else save count
  8275.            mov     cx, 0FFh              ; Load maximum character count
  8276.            cmp     cx, di                ; Near start of buffer?
  8277.            jl      skip1                 ; No? Continue
  8278.            mov     cx, di                ;   else search only to start
  8279.  skip1:    repne   scasb                 ; Find last previous LF
  8280.            jcxz    skip4                 ; If not found, must be at start
  8281.            pop     cx
  8282.            loop    loop1
  8283.            cmp     curLine, -1           ; End of file flag?
  8284.            jne     skip2                 ; No? Continue
  8285.            add     di, 2                 ; Adjust for cr/lf
  8286.            mov     oBuffer, di           ; Save position
  8287.            call    EndCount              ; Count back to get line number
  8288.            ret
  8289.  
  8290.  skip2:    sub     curLine, dx           ; Calculate line number
  8291.            jg      skip3
  8292.            mov     curLine, 1            ; Set to 1 if negative
  8293.  skip3:    add     di, 2                 ; Adjust for cr/lf
  8294.            mov     oBuffer, di           ; Save position
  8295.            ret
  8296.  
  8297.  skip4:    pop     cx
  8298.            sub     di, di                ; Load start of file
  8299.            mov     curLine, 1            ; Line 1
  8300.            mov     oBuffer, di           ; Save position
  8301.  exit:     ret
  8302.  GoBack    ENDP
  8303.  
  8304.  ; Procedure GoForwd
  8305.  ; Purpose   Searches forward through a buffer
  8306.  ; Input     CX has number of lines; ES:DI has buffer position
  8307.  ; Output    Updates "curLine" and "oBuffer"
  8308.  
  8309.  GoForwd   PROC
  8310.            cld                           ; Go forward
  8311.            mov     dx, cx                ; Copy count
  8312.  loop1:    push    cx                    ; Save count
  8313.            mov     cx, 0FFh              ; Load maximum character count
  8314.            mov     bx, lBuffer           ; Get end of file
  8315.  
  8316.            sub     bx, di                ; Characters to end of file
  8317.            cmp     cx, bx                ; Less than maximum per line?
  8318.            jb      skip1
  8319.            mov     cx, bx
  8320.  skip1:    repne   scasb                 ; Find next LF
  8321.            jcxz    exit                  ; If not found, must be at end
  8322.            cmp     di, lBuffer           ; Beyond end?
  8323.            jae     exit
  8324.            pop     cx
  8325.            loop    loop1
  8326.            add     curLine, dx           ; Calulate line number
  8327.            mov     oBuffer, di           ; Save position
  8328.            ret
  8329.  
  8330.  exit:     pop     cx
  8331.            mov     di, oBuffer           ; Restore position
  8332.            ret
  8333.  GoForwd   ENDP
  8334.  
  8335.  ; Procedure EndCount
  8336.  ; Purpose   Counts backward to count lines in file
  8337.  ; Input     ES:DI has buffer position
  8338.  ; Output    Modifies "curLine"
  8339.  
  8340.  EndCount  PROC
  8341.            push    di
  8342.  
  8343.            mov     al, 13                ; Search for CR
  8344.            mov     curLine, 0            ; Initialize
  8345.  
  8346.  loop1:    inc     curLine               ; Adjust count
  8347.            mov     cx, 0FFh              ; Load maximum character count
  8348.            cmp     cx, di                ; Near start of buffer?
  8349.            jl      skip1                 ; No? Continue
  8350.            mov     cx, di                ;   else search only to start
  8351.  skip1:    repne   scasb                 ; Find last previous cr
  8352.            jcxz    exit                  ; If not found, must be at start
  8353.            jmp     SHORT loop1
  8354.  
  8355.  exit:     pop     di
  8356.            ret
  8357.  EndCount  ENDP
  8358.  
  8359.  ; Procedure BinToStr (number, string)
  8360.  ; Purpose   Converts integer to string
  8361.  ; Input     Stack arguments: 1 - Number to convert; 2 - Near address for writ
  8362.  ; Output    AX has characters written
  8363.  
  8364.  BinToStr  PROC    number, string:PTR BYTE
  8365.  
  8366.            mov     ax,number
  8367.            mov     di,string
  8368.  
  8369.            sub     cx, cx                ; Clear counter
  8370.            mov     bx, 10                ; Divide by 10
  8371.  
  8372.  ; Convert and save on stack backwards
  8373.  
  8374.  loop1:    sub     dx, dx                ; Clear top
  8375.            div     bx                    ; Divide to get last digit as remaind
  8376.            add     dl, "0"               ; Convert to ASCII
  8377.            push    dx                    ; Save on stack
  8378.            or      ax, ax                ; Quotient 0?
  8379.            loopnz  loop1                 ; No? Get another
  8380.  
  8381.  ; Take off the stack and store forward
  8382.  
  8383.            neg     cx                    ; Negate and save count
  8384.            mov     dx, cx
  8385.  loop2:    pop     ax                    ; Get character
  8386.            stosb                         ; Store it
  8387.            loop    loop2
  8388.            mov     ax, dx                ; Return digit count
  8389.  
  8390.            ret
  8391.  BinToStr  ENDP
  8392.  
  8393.            END
  8394.  
  8395.  
  8396.  PAGERR.ASM
  8397.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\PAGERR.ASM
  8398.  
  8399.            PAGE          60,132
  8400.            .MODEL  small
  8401.            .DATA
  8402.            EXTRN          statatr:BYTE,scrnatr:BYTE,sbuffer:WORD,pbuffer:WORD
  8403.            EXTRN          fsize:WORD,cell:WORD,statline:BYTE,linenum:WORD
  8404.            EXTRN          rows:WORD,vidadr:WORD,cga:BYTE
  8405.  
  8406.            .CODE
  8407.            PUBLIC  Pager,isEGA
  8408.  
  8409.  ; Procedure Pager
  8410.  ; Purpose   Displays status and        text lines
  8411.  ; Input            Stack variable: lines to scroll (negative up, positive dow
  8412.  ;            Global variables: "sbuffer", "pbuffer", "linenum"
  8413.  ; Output    To screen
  8414.  
  8415.  Pager          PROC
  8416.            push          bp
  8417.            mov          bp,sp
  8418.  
  8419.            mov          es,sbuffer                ; Initialize buffer position
  8420.            mov          di,pbuffer
  8421.  
  8422.            mov          cx,[bp+4]                ; Get count argument
  8423.            mov          ax,10                        ; Search for linefeed
  8424.  
  8425.            or          cx,cx                        ; Argument 0?
  8426.            jg          forward                ; If above, forward
  8427.            jl          backward                ; If below, backward
  8428.            jmp          SHORT        show                ; If equal, done
  8429.  
  8430.  backward: call          GoBack                ; Adjust backward
  8431.            jmp          SHORT        show                ; Show screen
  8432.  forward:  call          GoForwd                ; Adjust forward
  8433.  
  8434.  ; Write        line number to status line
  8435.  
  8436.  show:          cld                                ; Go forward
  8437.            push          di
  8438.            push          es
  8439.            push          ds                        ; Load DS to ES
  8440.            pop          es
  8441.  
  8442.  ; BinToStr (linenum,OFFSET statline[7])
  8443.  
  8444.            push          linenum                ; Arg 1
  8445.            mov          ax,OFFSET statline[7]
  8446.            push          ax                        ; Arg 2
  8447.            call          BinToStr                ; Convert to string
  8448.  
  8449.  ; Fill in status line
  8450.  
  8451.            mov          cx,7                        ; Seven        spaces to f
  8452.            sub          cx,ax                        ; Subtract those already
  8453.            mov          al," "                ; Fill with space
  8454.            rep          stosb
  8455.            pop          es
  8456.  
  8457.            mov          bl,statatr                ; Load status attribute
  8458.            mov          BYTE PTR cell[1],bl
  8459.  
  8460.  ; CellWrt (DS,OFFSET statline,0,cell)
  8461.  
  8462.            push          ds                        ; Arg 1
  8463.            mov          ax,OFFSET statline        ; Arg 2
  8464.            push          ax
  8465.            sub          ax,ax                        ; Arg 3
  8466.            push          ax
  8467.            push          cell                        ; Arg 4
  8468.            call          CellWrt                ; Write        status line
  8469.  
  8470.            pop          di
  8471.            mov          bl,scrnatr                ; Load screen attribute
  8472.            mov          BYTE PTR cell[1],bl
  8473.            mov          si,di                        ; Update position
  8474.            mov          cx,rows                ; Lines        per screen
  8475.  
  8476.  show1:          mov          bx,rows                ; Lines        of text
  8477.            inc          bx                        ; Adjust for 0
  8478.            sub          bx,cx                        ; Calculate current row
  8479.            push          cx                        ; Save line number
  8480.  
  8481.  ; CellWrt (sbuffer,position,line,cell)
  8482.  
  8483.            push          sbuffer                ; Arg 1
  8484.            push          si                        ; Arg 2
  8485.            push          bx                        ; Arg 3
  8486.            push          cell                        ; Arg 4
  8487.            call          cellwrt                ; Write        line
  8488.  
  8489.            push          ss                        ; Restore DS from SS
  8490.            pop          ds
  8491.  
  8492.            pop          cx                        ; Restore line number
  8493.            mov          si,ax                        ; Get returned position
  8494.  
  8495.            cmp          ax,fsize                ; Beyond end of        file?
  8496.            jae          fillout                ; Yes? Fill screen with
  8497.            loop          show1                        ;    else next line
  8498.            jmp          SHORT        pagedone        ; Get out if done
  8499.  
  8500.  ; Fill the rest        with spaces
  8501.  
  8502.  fillout:  dec          cx                        ; Adjust
  8503.            jcxz          pagedone
  8504.            mov          al,80                        ; Columns times        re
  8505.            mul          cl
  8506.  
  8507.  ; CellFil (sbuffer,count,cell)
  8508.  
  8509.            push          sbuffer                ; Arg 1
  8510.            push          ax                        ; Arg 2
  8511.            push          cell                        ; Arg 3
  8512.            call          CellFil                ; Fill screen with spaces
  8513.  
  8514.            push          ss                        ; Restore DS from SS
  8515.            pop          ds
  8516.  
  8517.  pagedone: pop          bp
  8518.            ret          2
  8519.  Pager          ENDP
  8520.  
  8521.  ; Procedure CellWrt (segment,offset,line,cell)
  8522.  ; Purpose   Writes a line to screen buffer
  8523.  ; Input            Stack variables: 1 - segment of line
  8524.  ;                             2 - offset
  8525.  ;                             3 - line number
  8526.  ;                             4 - attribute
  8527.  ; Output    Line to screen buffer
  8528.  
  8529.  CellWrt          PROC
  8530.            push          bp
  8531.            mov          bp,sp
  8532.            sub          dx,dx                        ; Clear        as flag
  8533.            cmp          cga,1                        ; CGA?
  8534.            jne          noscan
  8535.            mov          dx,03DAh                ; Load port #
  8536.  
  8537.  noscan:          mov          es,vidadr                ; Load screen buffer s
  8538.            mov          ds,[bp+10]                ; Buffer segment
  8539.            mov          si,[bp+8]                ; Buffer position
  8540.            mov          cx,80                        ; Cells        per row
  8541.            mov          ax,[bp+6]                ; Starting row
  8542.            mov          bx,80*2                ; Bytes        per row
  8543.            mul          bl                        ; Figure columns per row
  8544.            mov          di,ax                        ; Load as destination
  8545.            mov          bx,di                        ; Save start for tab calc
  8546.            mov          ax,[bp+4]                ; Attribute
  8547.  movechar: lodsb                                ; Get character
  8548.            cmp          al,13                        ; CR?
  8549.            je          fillspc
  8550.            cmp          al,9                        ; Tab?
  8551.            jne          notab
  8552.            call          filltab                ; Yes? fill with spaces
  8553.            jcxz          nextline                ; If beyond limit done
  8554.            jmp          SHORT        movechar
  8555.  
  8556.  notab:          or          dx,dx                        ; CGA?
  8557.            je          notab2
  8558.            call          Retrace                ; Yes? Write during retrace
  8559.            loop          movechar
  8560.            jmp          SHORT        nextline
  8561.  
  8562.  notab2:          stosw                                ; Write
  8563.            loop          movechar
  8564.            jmp          SHORT        nextline        ; Done
  8565.  
  8566.  fillspc:  mov          al," "                ; Fill with space
  8567.  
  8568.            or          dx,dx                        ; CGA?
  8569.            je          space2
  8570.  space1:          call          Retrace                ; Yes? Write during ret
  8571.            loop          space1
  8572.            inc          si                        ; Adjust
  8573.            jmp          SHORT        exit                ; Done
  8574.  
  8575.  space2:          rep          stosw                        ; Write
  8576.            inc          si                        ; Adjust for LF
  8577.            jmp          SHORT        exit                ; Done
  8578.  
  8579.  nextline: mov          ah,10                        ; Search for next line fe
  8580.  chklf:          lodsb                                ; Load and compare
  8581.            cmp          al,ah
  8582.            loopne  chklf
  8583.  
  8584.  exit:          mov          ax,si                        ; Return position
  8585.            pop          bp
  8586.            ret          8
  8587.  CellWrt          ENDP
  8588.  
  8589.  ; Procedure CellFil (segment,count,cell)
  8590.  ; Purpose   Fills screen with character
  8591.  ; Input            Stack variables: 1 - segment of text (offset 0)
  8592.  ;                             2 - number        of characters
  8593.  ;                             3 - attribute and character
  8594.  ; Output    Characters to screen buffer
  8595.  
  8596.  CellFil          PROC
  8597.            push          bp
  8598.            mov          bp,sp
  8599.            sub          dx,dx                        ; Clear        as flag
  8600.            cmp          cga,1                        ; CGA?
  8601.            jne          noscan2
  8602.            mov          dx,03DAh                ; Load port #
  8603.  
  8604.  noscan2:  mov          es,vidadr                ; Load screen buffer segment
  8605.            mov          ds,[bp+8]                ; Buffer segment (position 0)
  8606.            mov          cx,[bp+6]                ; Characters to        fill
  8607.            mov          ax,[bp+4]                ; Attribute
  8608.            or          dx,dx                        ; CGA?
  8609.            je          fillem2
  8610.  fillem1:  call          Retrace                ; Yes? Write during retrace
  8611.            loop          fillem1
  8612.            jmp          SHORT        filled                ; Done
  8613.  fillem2:  rep          stosw                        ; Write
  8614.  
  8615.  filled:          pop          bp
  8616.            ret          6
  8617.  CellFil          ENDP
  8618.  
  8619.  ; Procedure FillTab
  8620.  ; Purpose   Writes spaces for tab to screen
  8621.  ; Input            BX points to start of line,        DI points to current po
  8622.  ; Output    Spaces to screen buffer
  8623.  
  8624.  FillTab          PROC
  8625.            push          bx
  8626.            push          cx
  8627.  
  8628.            sub          bx,di                        ; Get current position in
  8629.            neg          bx
  8630.            shr          bx,1                        ; Divide by 2 bytes per
  8631.  
  8632.            mov          cx,8                        ; Default count        8
  8633.            and          bx,7                        ; Get modulus
  8634.            sub          cx,bx                        ; Subtract
  8635.            mov          bx,cx                        ; Save modulus
  8636.  
  8637.            mov          al," "                ; Spaces
  8638.            or          dx,dx                        ; CGA?
  8639.            je          tabem2
  8640.  
  8641.  tabem1:          call          Retrace                ; Yes? Write during ret
  8642.            loop          tabem1
  8643.            jmp          SHORT        tabbed
  8644.  tabem2:          rep          stosw                        ; Write
  8645.  
  8646.  tabbed:          pop          cx
  8647.            sub          cx,bx                        ; Adjust count
  8648.            jns          nomore                ; Make negative        count 0
  8649.            sub          cx,cx
  8650.  nomore:          pop          bx
  8651.            ret
  8652.  FillTab          ENDP
  8653.  
  8654.  ; Procedure GoBack
  8655.  ; Purpose   Searches backward through buffer
  8656.  ; Input            CX has number of lines; ES:DI has buffer position
  8657.  ; Output    Updates "linenum" and "pbuffer"
  8658.  
  8659.  GoBack          PROC
  8660.            std                                ; Go backward
  8661.            neg          cx                        ; Make count positive
  8662.            mov          dx,cx                        ; Save a copy
  8663.            inc          cx                        ; One extra to go up one
  8664.            or          di,di                        ; Start        of file?
  8665.            je          exback                ; If so, ignore
  8666.  findb:          push          cx                        ;   else save count
  8667.            mov          cx,0FFh                ; Load maximum character count
  8668.            cmp          cx,di                        ; Near start of        bu
  8669.            jl          notnear                ; No? Continue
  8670.            mov          cx,di                        ;   else search        on
  8671.  notnear:  repne          scasb                        ; Find last previous LF
  8672.            jcxz          atstart                ; If not found,        must be
  8673.            pop          cx
  8674.            loop          findb
  8675.            cmp          linenum,0FFFFh        ; End of file flag?
  8676.            jne          notend                ; No? Continue
  8677.            add          di,2                        ; Adjust for cr/lf
  8678.            mov          pbuffer,di                ; Save position
  8679.            call          EndCount                ; Count        back to
  8680.            ret
  8681.  
  8682.  notend:          sub          linenum,dx                ; Calculate line numb
  8683.            jg          positive
  8684.            mov          linenum,1                ; Set to 1 if negative
  8685.  positive: add          di,2                        ; Adjust for cr/lf
  8686.            mov          pbuffer,di                ; Save position
  8687.            ret
  8688.  
  8689.  atstart:  pop          cx
  8690.            sub          di,di                        ; Load start of        fi
  8691.            mov          linenum,1                ; Line 1
  8692.            mov          pbuffer,di                ; Save position
  8693.  exback:          ret
  8694.  GoBack          ENDP
  8695.  
  8696.  ; Procedure GoForwd
  8697.  ; Purpose   Searches forward through a buffer
  8698.  ; Input            CX has number of lines; ES:DI has buffer position
  8699.  ; Output    Updates "linenum" and "pbuffer"
  8700.  
  8701.  GoForwd          PROC
  8702.            cld                                ; Go forward
  8703.            mov          dx,cx                        ; Copy count
  8704.  findf:          push          cx                        ; Save count
  8705.            mov          cx,0FFh                ; Load maximum character count
  8706.            repne          scasb                        ; Find next LF
  8707.            jcxz          atend                        ; If not found,        m
  8708.            cmp          di,fsize                ; Beyond end?
  8709.            jae          atend
  8710.            pop          cx
  8711.            loop          findf
  8712.            add          linenum,dx                ; Calulate line        numbe
  8713.            mov          pbuffer,di                ; Save position
  8714.            ret
  8715.  
  8716.  atend:          pop          cx
  8717.            mov          di,pbuffer                ; Restore position
  8718.            ret
  8719.  GoForwd          ENDP
  8720.  
  8721.  ; Procedure EndCount
  8722.  ; Purpose   Counts backward to count lines in file
  8723.  ; Input            ES:DI has buffer position
  8724.  ; Output    Modifies "linenum"
  8725.  
  8726.  EndCount  PROC
  8727.            push          di
  8728.  
  8729.            mov          al,13                        ; Search for CR
  8730.            mov          linenum,0                ; Initialize
  8731.  
  8732.  findstrt: inc          linenum                ; Adjust count
  8733.            mov          cx,0FFh                ; Load maximum character count
  8734.            cmp          cx,di                        ; Near start of        bu
  8735.            jl          notnear2                ; No? Continue
  8736.            mov          cx,di                        ;   else search        on
  8737.  notnear2: repne          scasb                        ; Find last previous cr
  8738.            jcxz          found                        ; If not found,        m
  8739.            jmp          SHORT        findstrt
  8740.  
  8741.  found:          pop          di
  8742.            ret
  8743.  EndCount  ENDP
  8744.  
  8745.  ; Procedure isEGA
  8746.  ; Purpose   Determines if an EGA is active
  8747.  ; Input            None
  8748.  ; Output    0 if no; lines per screen if yes
  8749.  
  8750.  isEGA          PROC
  8751.            push          bp
  8752.            push          es
  8753.            mov          ah,12h                ; Call EGA status function
  8754.            mov          bl,10h
  8755.            sub          cx,cx                        ; Clear        status bit
  8756.            int          10h
  8757.            sub          ax,ax                        ; Segment 0 and        as
  8758.            jcxz          noega                        ; If status still clear,
  8759.  
  8760.            mov          es,ax                        ; ES=0
  8761.            test          BYTE PTR es:[487h],1000b ; Test active bit
  8762.            jnz          noega                        ; If set, not active
  8763.            mov          ax,1130h                ; Get EGA information
  8764.            int          10h
  8765.            mov          al,dl                        ; Return lines per screen
  8766.            cbw
  8767.  
  8768.  noega:          pop          es
  8769.            pop          bp
  8770.            ret
  8771.  isEGA          ENDP
  8772.  
  8773.  ; Procedure BinToStr (number,address)
  8774.  ; Purpose   Converts integer to        string
  8775.  ; Input            Stack arguments: 1 - Number        to convert; 2 -
  8776.  ; Output    AX has characters written
  8777.  
  8778.  BinToStr  PROC
  8779.            push          bp
  8780.            mov          bp,sp
  8781.            mov          ax,[bp+6]                ; Arg 1
  8782.            mov          di,[bp+4]                ; Arg 2
  8783.  
  8784.            sub          cx,cx                        ; Clear        counter
  8785.            mov          bx,10                        ; Divide by 10
  8786.  
  8787.  ; Convert and save on stack backwards
  8788.  
  8789.  getdigit: sub          dx,dx                        ; Clear        top
  8790.            div          bx                        ; Divide to get        last
  8791.            add          dl,"0"                ; Convert to ASCII
  8792.            push          dx                        ; Save on stack
  8793.            or          ax,ax                        ; Quotient 0?
  8794.            loopnz  getdigit                ; No? Get another
  8795.  
  8796.  ; Take off the stack and store forward
  8797.  
  8798.            neg          cx                        ; Negate and save count
  8799.            mov          dx,cx
  8800.  putdigit: pop          ax                        ; Get character
  8801.            stosb                                ; Store        it
  8802.            loop          putdigit
  8803.            mov          ax,dx                        ; Return digit count
  8804.  
  8805.            pop          bp
  8806.            ret          4
  8807.  BinToStr  ENDP
  8808.  
  8809.  ; Procedure Retrace
  8810.  ; Purpose   Writes cell        during horizontal retrace (CGA)
  8811.  ; Input            ES:DI has screen buffer position, AX has cell
  8812.  ; Output    Character to screen        buffer
  8813.  
  8814.  Retrace          PROC
  8815.            push          bx
  8816.            mov          bx,ax                        ; Save character
  8817.  lscan2:          in          al,dx                        ; Look in the port
  8818.            shr          al,1                        ;   until it goes low
  8819.            jc          lscan2
  8820.            cli
  8821.  hscan2:          in          al,dx                        ; Look in the port
  8822.            shr          al,1                        ;   until it goes high
  8823.            jnc          hscan2
  8824.            mov          ax,bx                        ; Restore and write it
  8825.            stosw
  8826.            sti
  8827.            pop          bx
  8828.            ret
  8829.  Retrace          ENDP
  8830.  
  8831.            END
  8832.  
  8833.  
  8834.  POWER2.ASM
  8835.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\MIXED\POWER2.ASM
  8836.  
  8837.  ; Default command line for BASIC:    MASM /Dmodel=medium /Dlang=BASIC power2;
  8838.  ; Default command line for C:        MASM /MX /Dmodel=small /Dlang=C power2;
  8839.  ; Default command line for FORTRAN:  MASM /Dmodel=large /Dlang=FORTRAN power2
  8840.  ; Default command line for Pascal:   MASM /Dmodel=large /Dlang=Pascal power2;
  8841.  
  8842.  %         .MODEL  model,lang
  8843.            INCLUDE mixed.inc
  8844.  
  8845.  %         IFIDNI  <lang>,<BASIC>
  8846.  reference EQU     1
  8847.  %         ELSEIFIDNI <lang>,<FORTRAN>
  8848.  reference EQU     1
  8849.            ENDIF
  8850.  
  8851.            .CODE
  8852.  
  8853.  ; Function for C, FORTRAN, Pascal, Version 4 of QuickBASIC, and
  8854.  ;   future versions of Microsoft and IBM BASIC Compilers
  8855.  
  8856.            IFDEF   reference          ; Pass by reference for BASIC or FORTRAN
  8857.  Power2    PROC    Value:PTR WORD, Count:PTR WORD
  8858.  
  8859.            pLes    bx,Value           ; Load arguments passed by reference
  8860.            mov     ax,FP[bx]
  8861.            pLes    bx,Count
  8862.            mov     cx,FP[bx]
  8863.  
  8864.            ELSE                       ; Pass by value for C or Pascal
  8865.  Power2    PROC    Value, Count
  8866.  
  8867.            mov     ax,Value           ; Load arguments passed by value
  8868.            mov     cx,Count
  8869.            ENDIF
  8870.  
  8871.            shl     ax,cl              ; AX = AX * (2 to power of CL)
  8872.                                       ; Return result in AX
  8873.            ret
  8874.  Power2    ENDP
  8875.  
  8876.            IFIDNI  <lang>,<BASIC>
  8877.  
  8878.  ; Subprogram for QuickBASIC, Versions 1, 2, and 3;
  8879.  ;     for the Microsoft BASIC Compiler through Version 5.36
  8880.  ;     for the IBM BASIC Compiler through Version 2.02
  8881.  
  8882.  Power2S   PROC    Value:PTR WORD, Count:PTR WORD, RetVal:PTR WORD
  8883.  
  8884.            pLes    bx,Value           ; Load BASIC arguments
  8885.            mov     ax,FP[bx]          ;   passed by reference
  8886.            pLes    bx,Count
  8887.            mov     cx,FP[bx]
  8888.  
  8889.            shl     ax,cl              ; AX = AX * (2 to power of CL)
  8890.  
  8891.            pLes    bx,RetVal          ; Load return address
  8892.            mov     FP[bx],ax          ;   and store result in it
  8893.  
  8894.            ret
  8895.  Power2S   ENDP
  8896.            ENDIF   ; BASIC
  8897.            END
  8898.  
  8899.  
  8900.  
  8901.  SHOWP.ASM
  8902.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\SHOWP.ASM
  8903.  
  8904.            TITLE   Show
  8905.  
  8906.  ; Program SHOW.ASM
  8907.  ; Purpose Text file displayer
  8908.  ; Input   File name from command line or prompt
  8909.  ; Output  Display file to screen
  8910.  
  8911.            DOSSEG
  8912.            .MODEL  small, pascal
  8913.  
  8914.  INCL_DOSFILEMGR   EQU 1         ; Enable call groups
  8915.  INCL_DOSMEMMGR    EQU 1
  8916.  INCL_KBD          EQU 1
  8917.  INCL_VIO          EQU 1
  8918.  
  8919.            INCLUDE os2.inc
  8920.            INCLUDELIB doscalls.lib
  8921.  
  8922.            .STACK  800h
  8923.  
  8924.            .DATA
  8925.  
  8926.  ; Status line
  8927.  
  8928.            PUBLIC  stLine, nLines, curLine
  8929.  curLine   DW      1             ; Current line number
  8930.  nLines    DW      ?             ; Lines per screen
  8931.  stLine    DB      "Line: 12345 "
  8932.  stFile    DB      "File: 12345678.123  "
  8933.            DB      "Quit: Q  Next: ESC  Move:   PGUP PGDN HOME END"
  8934.  
  8935.  ; Variables for screen and cursor handling
  8936.  
  8937.            PUBLIC  vMode, Cell, stAtrib, scAtrib
  8938.  vMode     VIOMODEINFO <>        ; Structures for video and cursor data
  8939.  lvMode    EQU     $ - vMode     ; Length of structure
  8940.  vType     DW      0             ; Video type - 0 flag for no change
  8941.  
  8942.  cMode     VIOCURSORINFO <>
  8943.  cAtrib    DW      -1            ; Cursor attribute (initized to hidden)
  8944.  cStatus   DB      0             ; 0 = cursor visible, position unchanged
  8945.                                  ; 1 = cursor invisible, position unchanged
  8946.                                  ; 2 = cursor invisible, position changed
  8947.  
  8948.  stAtrib   DB      030h          ; Status line color default - black on cyan
  8949.  stBW      EQU     070h          ; B&W default - black on white
  8950.  Cell      LABEL   WORD          ; Cell (character and attribute)
  8951.  scChar    DB      " "           ; Initialize to space
  8952.  scAtrib   DB      017h          ; Screen color default - white on blue
  8953.  scBW      EQU     007h          ; B&W default - white on black
  8954.  
  8955.  ; Variables for buffer and file handling
  8956.  
  8957.            PUBLIC  Buffer, oBuffer, sBuffer, lBuffer
  8958.  Buffer    LABEL   DWORD
  8959.  oBuffer   DW      0             ; Position in buffer (offset)
  8960.  sBuffer   DW      ?             ; Base of buffer (segment)
  8961.  lBuffer   DW      ?             ; Length of buffer
  8962.  
  8963.  ; File information
  8964.  
  8965.  lfName    EQU     66
  8966.  fName     DB      lfName DUP (" ")
  8967.  fHandle   DW      ?             ; Holds file handle on open
  8968.  fAction   DW      ?             ; Result of open
  8969.  fAtrib    EQU     0             ; Normal file
  8970.  fFlag     EQU     1             ; Open file if exist, fail if not exist
  8971.  ; Read only, deny none, private, error codes, use cache, normal file
  8972.  fModeRec  RECORD  DA:1=0,WT:1=0,FE:1=0,R1:5=0,INF:1=1,SM:3=2,R2:1=0,AM:3=0
  8973.  fMode     fModeRec <>
  8974.  fRead     DW      ?             ; Bytes read from file
  8975.  
  8976.  ; Directory information for file name search
  8977.  
  8978.  dHandle   DW      -1            ; Directory handle
  8979.  dResult   FILEFINDBUF <>        ; Structure for results
  8980.  dlResult  EQU     $ - dResult   ;   length of result
  8981.  dCount    DW      1             ; Find one file at a time
  8982.  
  8983.  Prompt    DB      13,10,"Enter filename: "
  8984.  lPrompt   EQU     $ - Prompt
  8985.  Prompt2   DB      13,10,"No such file. Try again? "
  8986.  lPrompt2  EQU     $ - Prompt2
  8987.  Prompt3   DB      13,10,"File too large: "
  8988.  lPrompt3  EQU     $ - Prompt3
  8989.  Prompt4   DB      13,10,"Memory problem.",13,10
  8990.  lPrompt4  EQU     $ - Prompt4
  8991.  
  8992.  ; Keyboard data
  8993.  
  8994.  kChar     KBDKEYINFO <>         ; Structures for character and string input
  8995.  kStr      STRINGINBUF <>
  8996.  kWait     EQU     0             ; Wait flag
  8997.  
  8998.  ; Call table
  8999.  
  9000.  kTable    DB      71,72,73,79,80,81,'q','Q'; Key codes
  9001.  lkTable   EQU     $-kTable
  9002.  procTable DW      homek                    ; Table of keys and procedures
  9003.            DW      upk
  9004.            DW      pgupk
  9005.            DW      endk
  9006.            DW      downk
  9007.            DW      pgdnk
  9008.            DW      Quit
  9009.            DW      Quit
  9010.            DW      nonek
  9011.  
  9012.            .CODE
  9013.            EXTRN   Pager:PROC         ; Routine in other module
  9014.  
  9015.  start     PROC
  9016.            mov     es, ax             ; Load environment segment
  9017.            mov     di, bx
  9018.  
  9019.  ; Throw away .EXE name
  9020.  
  9021.            sub     ax, ax             ; Find null at end of program name
  9022.            repne   scasb
  9023.            cmp     BYTE PTR es:[di], 0; If double zero, there's no name
  9024.            je      Prompter           ;   so get from prompt
  9025.  
  9026.            cmp          BYTE PTR es:[di], ' '
  9027.            jne     skip1
  9028.            inc     di                 ; Skip leading space
  9029.  skip1:
  9030.  ; Copy command line to file name buffer
  9031.  
  9032.            mov     si, di             ; Filename source
  9033.            mov     di, OFFSET fName   ; Name buffer destination
  9034.            mov     bx, ds             ; Save segment registers
  9035.            mov     dx, es
  9036.            mov     ds, dx             ; DS = ES
  9037.            mov     es, bx             ; ES = DS
  9038.            mov          cx, lfName             ; Count = max file name allowed
  9039.  loop1:          lodsb                      ; Copy first character
  9040.            stosb
  9041.            cmp     al,' '             ; Terminate on space too
  9042.            je      skip2
  9043.            or          al,al
  9044.            loopnz  loop1              ; If not null, copy another
  9045.  skip2:
  9046.            mov     ds, bx             ; Restore DS
  9047.            mov          BYTE PTR [di-1], 0
  9048.            jmp     FindFile
  9049.  
  9050.  NoFile:   @VioWrtTTy Prompt2, lPrompt2, 0
  9051.            @KbdCharIn kChar, kWait, 0
  9052.            and     kChar.kbci_chChar, 11011111b ; Convert to uppercase
  9053.            cmp     kChar.kbci_chChar, "Y"
  9054.            mov     dHandle, -1
  9055.            mov     dCount, 1
  9056.            je      Prompter           ; If yes, try again
  9057.            jmp     Quit               ;   else quit
  9058.  
  9059.  Prompter: @VioWrtTTy Prompt, lPrompt, 0 ; Else prompt for file name
  9060.  
  9061.            mov     kStr.kbsi_cb, lfName
  9062.  
  9063.            @KbdStringIn fName, kStr, kWait, 0
  9064.            mov     di, kStr.kbsi_cchIn ; Null terminate
  9065.            mov     fName[di], 0
  9066.  
  9067.  ; Find first (or only) file in filespec
  9068.  
  9069.  FindFile: @DosFindFirst fName, dHandle, 0, dResult, dlResult, dCount, 0
  9070.            or      ax, ax
  9071.            jz      skip3
  9072.            jmp     NoFile
  9073.  
  9074.  ; Adjust for current mode and video adapter and hide cursor
  9075.  skip3:    call    GetVid
  9076.  
  9077.  FileLoop:
  9078.            mov     cStatus, 2         ; Cursor invisible, position unchanged
  9079.  
  9080.  ; Copy file name to file spec
  9081.  
  9082.            push    ds                 ; Get file name position in file spec
  9083.            @Pushc  <OFFSET fName>
  9084.            call    GetNamPos
  9085.            mov     si, OFFSET dResult.findbuf_achName ; Load source name
  9086.            mov     es, dx             ; Load adjusted destination address
  9087.            mov     di, ax             ;   from return value
  9088.            sub     cx, cx             ; Load file length
  9089.            mov     cl, dResult.findbuf_cchName
  9090.            rep     movsb              ; Copy to spec
  9091.            mov     BYTE PTR es:[di], 0; Null terminate
  9092.  
  9093.  ; Copy file name to status line
  9094.  
  9095.            sub     cx, cx             ; Load file length
  9096.            mov     cl, dResult.findbuf_cchName
  9097.            mov     bx, 12             ; Calculate blank spaces to fill
  9098.            sub     bx, cx
  9099.            push    ds                 ; ES=DS
  9100.            pop     es
  9101.            mov     si, OFFSET dResult.findbuf_achName ; File name as source
  9102.            mov     di, OFFSET stFile[6] ; Status line as destination
  9103.            rep     movsb
  9104.            mov     al, " "            ; Fill rest of name space with blanks
  9105.            mov     cx, bx
  9106.            rep     stosb
  9107.  
  9108.  ; Open file
  9109.  
  9110.            @DosOpen fName, fHandle, fAction, 0, fAtrib, fFlag, [fMode], 0
  9111.            or      ax, ax
  9112.            jz      skip4
  9113.            jmp     NoFile
  9114.  
  9115.  skip4:    cmp     WORD PTR dResult.findbuf_cbFile[2], 0
  9116.            jz      skip6              ; Make sure file is less than a segment
  9117.            mov     cStatus, 1         ; Cursor invisible, position unchanged
  9118.            @VioWrtTTy Prompt3, lPrompt3, 0
  9119.            @VioWrtTTy <stFile + 6>, 12, 0
  9120.            cmp     [dCount], 0        ; Get key if there's another file
  9121.            je      skip5
  9122.            @KbdCharIn kChar, kWait, 0
  9123.  skip5:    jmp     skip11
  9124.  
  9125.  ; Allocate file buffer
  9126.  
  9127.  skip6:    mov     ax, WORD PTR dResult.findbuf_cbFile[0] ; Save size
  9128.            mov     lBuffer, ax
  9129.            mov     oBuffer, 0
  9130.            @DosAllocSeg ax, sBuffer, 0
  9131.            or      ax, ax
  9132.            jz      skip7
  9133.            mov     cStatus, 1         ; Cursor invisible, position unchanged
  9134.            @VioWrtTTy Prompt4, lPrompt4, 0
  9135.            jmp     Quit
  9136.  
  9137.  ; Read the file into the buffer
  9138.  
  9139.  skip7:    @DosRead [fHandle], [Buffer], [lBuffer], fRead
  9140.            or      ax, ax
  9141.            jz      skip8
  9142.            jmp     NoFile
  9143.  
  9144.  ; Search back for EOF marker and adjust if necessary
  9145.  
  9146.  skip8:    mov     di, [fRead]        ; Load file length
  9147.            dec     di                 ;   and adjust
  9148.            mov     es, [sBuffer]      ; Save ES and load buffer segment
  9149.            std                        ; Look backward for 255 characters
  9150.            mov     cx, 0FFh
  9151.            cmp     cx, di
  9152.            jb      skip9
  9153.            mov     cx, di
  9154.  skip9:    mov     al, 1Ah            ; Search for EOF marker
  9155.            repe    scasb
  9156.            cld
  9157.            jcxz    skip10             ; If none, we're OK
  9158.            inc     di                 ;   else adjust and save file size
  9159.            mov     [lBuffer], di
  9160.  
  9161.  ; Show a screen of text and allow commands
  9162.  
  9163.  skip10:   call    Show
  9164.  
  9165.  
  9166.  skip11:   @DosClose [fHandle]        ; Close file
  9167.  
  9168.            @DosFreeSeg [sBuffer]      ; Free memofy
  9169.  
  9170.            @DosFindNext [dHandle], dResult, dlResult, dCount ; Get next file
  9171.  
  9172.            cmp     [dCount], 0        ; Quit if no next file
  9173.            jz      exit
  9174.            jmp     FileLoop
  9175.  
  9176.  exit:     jmp     Quit
  9177.  start     ENDP
  9178.  
  9179.  Show      PROC
  9180.  
  9181.  ; Display first page
  9182.  
  9183.            @Pushc  0                  ; Start at 0
  9184.            call    Pager
  9185.  
  9186.  ; Handle keys
  9187.  
  9188.  nextkey:  @KbdCharIn kChar, kWait, 0 ; Get a key and load to register
  9189.            mov     al, kChar.kbci_chChar
  9190.            or      al, al             ; Is ascii code null?
  9191.            jz      skip1              ; Yes? Load scan
  9192.            cmp     al, 0E0h           ; Extended key on extended keyboard?
  9193.            jne     skip2              ; No? Got code
  9194.  skip1:    mov     al, kChar.kbci_chScan
  9195.  skip2:
  9196.            cmp     al, 27             ; Is it ESCAPE?
  9197.            je      Exit               ; Yes? Get out for next file
  9198.  
  9199.            push    ds                 ; ES = DS
  9200.            pop     es
  9201.            mov     di, OFFSET kTable  ; Load address and length of key list
  9202.            mov     cx, lkTable + 1
  9203.            repne   scasb              ; Find position and point to key
  9204.            sub     di, (OFFSET kTable) + 1
  9205.            shl     di, 1              ; Adjust pointer for word addresses
  9206.            call    procTable[di]      ; Call procedure
  9207.            jmp     nextkey
  9208.  
  9209.  exit:     ret
  9210.  Show      ENDP
  9211.  
  9212.  homek:    mov     oBuffer, 0         ; HOME - set position to 0
  9213.            push    oBuffer
  9214.            mov     curLine, 1
  9215.            call    Pager
  9216.            retn
  9217.  
  9218.  upk:      @Pushc  -1                 ; UP - scroll back 1 line
  9219.            call    Pager
  9220.            retn
  9221.  
  9222.  pgupk:    mov     ax, nLines         ; PGUP - Page back
  9223.            neg     ax
  9224.            push    ax
  9225.            call    Pager
  9226.            retn
  9227.  
  9228.  endk:     mov     ax, lBuffer        ; END - Get last byte of file
  9229.            mov     oBuffer, ax        ; Make it the file position
  9230.            mov     curLine, -1        ; Set illegal line number as flag
  9231.            mov     ax, nLines         ; Page back
  9232.            neg     ax
  9233.            push    ax
  9234.            call    Pager
  9235.            retn
  9236.  
  9237.  downk:    @Pushc  1                  ; DOWN - scroll forward 1 line
  9238.            call    Pager
  9239.            retn
  9240.  
  9241.  pgdnk:    push    nLines             ; PGDN - page forward
  9242.            call    Pager
  9243.            retn
  9244.  
  9245.  nonek:    retn                       ; Ignore unknown key
  9246.  
  9247.  GetVid    PROC
  9248.  
  9249.            mov     vMode.viomi_cb, lvMode
  9250.            @VioGetMode vMode, 0       ; Get video mode
  9251.  
  9252.            sub     ax, ax             ; Clear AH
  9253.            mov     al, vMode.viomi_fbType ; Put type in register
  9254.            mov     vType, ax          ;   and save
  9255.            test    al, 1              ; Test for color
  9256.            jz      skip1              ; No? Mono
  9257.            test    al, 100b           ; Test for color burst on
  9258.            jz      skip2              ; Yes? Color
  9259.  skip1:    mov     stAtrib, stBW      ; Set B&W defaults for status line
  9260.            mov     scAtrib, scBW      ;   and screen background
  9261.  
  9262.  skip2:    @VioGetCurType cMode, 0    ; Get cursor mode
  9263.            mov     ax, cMode.vioci_attr ; Save attribute
  9264.            xchg    cAtrib, ax
  9265.            mov     cMode.vioci_attr, ax ; Set hidden cursor attribute
  9266.            mov     ax, vMode.viomi_row; Get number of rows and adjust
  9267.            dec     ax
  9268.            mov     nLines, ax
  9269.  
  9270.            @VioSetCurType cMode, 0    ; Hide cursor
  9271.  
  9272.            ret
  9273.  GetVid    ENDP
  9274.  
  9275.  GetNamPos PROC    USES di si, argline:FAR PTR BYTE
  9276.  
  9277.            les     di, argline        ; Load address of file name
  9278.            mov     si, di             ; Save copy
  9279.  
  9280.            sub     cx, cx             ; Ignore count
  9281.            sub     dx, dx             ; Use DX as found flag
  9282.            dec     di                 ; Adjust
  9283.            mov     ax, "\"            ; Search for backslash
  9284.  loop1:    scasb                      ; Get next character
  9285.            jz      skip1              ; If backslash, set flag and save
  9286.            cmp     BYTE PTR es:[di], 0; If end of name, done
  9287.            je      skip2
  9288.            loop    loop1              ; If neither, continue
  9289.  skip1:    mov     si, di             ; Save position
  9290.            inc     dx                 ; Set flag to true
  9291.            loop    loop1
  9292.  
  9293.  skip2:    or      dx, dx             ; Found backslash?
  9294.            je      skip3              ; If none, search for colon
  9295.            mov     ax, si             ;   else return position in DX:AX
  9296.            jmp     SHORT exit
  9297.  
  9298.  skip3:    neg     cx                 ; Adjust count
  9299.            mov     di, si             ; Restore start of name
  9300.            mov     ax, ":"            ; Search for colon
  9301.            repne   scasb
  9302.            jnz     skip4              ; If no colon, restore original
  9303.            mov     ax, di             ;   else return position in DX:AX
  9304.            jmp     SHORT exit
  9305.  
  9306.  skip4:    mov     ax, si             ; Return original address
  9307.  
  9308.  exit:     mov     dx, es
  9309.            ret
  9310.  GetNamPos ENDP
  9311.  
  9312.  Quit      PROC
  9313.  
  9314.            mov     scAtrib, 7         ; Restore cell attribute for clear scree
  9315.  
  9316.            cmp     cStatus, 1         ; Check cursor status
  9317.            jg      skip1              ; 2 - Make cursor visible on last line
  9318.            je      skip1              ; 1 - Make cursor visible
  9319.            jmp     SHORT skip3        ; 0 - Leave cursor as is
  9320.  
  9321.  skip1:    @VioSetCurPos [nLines], 0, 0 ; Restore cursor on last line
  9322.            @VioScrollDn [nLines], 0, [nLines], 79, 1, Cell, 0
  9323.  
  9324.  
  9325.  skip2:    mov     ax, cAtrib         ; Restore cursor attribute
  9326.            mov     cMode.vioci_attr, ax
  9327.            @VioSetCurType cMode, 0
  9328.  
  9329.  skip3:    @DosExit 1, 0              ; Quit
  9330.  
  9331.  Quit      ENDP
  9332.  
  9333.            END    start
  9334.  
  9335.  
  9336.  SHOWR.ASM
  9337.  CD-ROM Disc Path:   \SAMPCODE\MASM\MASM5\SHOWR.ASM
  9338.  
  9339.            PAGE          60,132
  9340.            TITLE          SHOW
  9341.  
  9342.  ; Program SHOW.ASM
  9343.  ; Purpose Text file displayer
  9344.  ; Input          File name from command line or prompt
  9345.  ; Output  Display file to screen
  9346.  
  9347.            DOSSEG
  9348.            .MODEL  small
  9349.  
  9350.            INCLUDE dos.inc
  9351.            INCLUDE bios.inc
  9352.  
  9353.            .STACK  100h
  9354.  
  9355.            .DATA
  9356.  
  9357.  ; Status line
  9358.  
  9359.            PUBLIC  statline,linenum
  9360.  statline  DB          " Line:         "
  9361.  statfile  DB          " File:                "
  9362.  stathelp  DB          " Quit: ESC    Move:   PGUP PGDN HOME END "
  9363.  linenum          DW          1
  9364.  
  9365.  ; Variables for        screen handling
  9366.  
  9367.            PUBLIC  cell,rows,columns,vidadr,statatr,scrnatr,cga
  9368.  cell          LABEL          WORD                ; Cell (character and attrib
  9369.  char          DB          " "                ; Initialize to        space
  9370.  attr          DB          ?                ; Attribute
  9371.  
  9372.  columns          EQU          80                ; Number of columns
  9373.  rows          DW          24                ; Number of rows - status line ta
  9374.  mode          DB          ?                ; Initial mode
  9375.  pag          DB          ?                ; Initial display page
  9376.  newvid          DB          0                ; Video        change flag
  9377.  cga          DB          1                ; CGA flag - default yes
  9378.  
  9379.  vidadr          DW          0B800h        ; Video        buffer address - def
  9380.  mono          EQU          0B000h        ; Monochrome address
  9381.  statatr          DB          030h                ; Color        default
  9382.  bwstat          EQU          070h                ; B&W default -        black
  9383.  scrnatr          DB          017h                ; Color        default
  9384.  bwscrn          EQU          007h                ; B&W default -        white
  9385.  
  9386.  ; Variables for        buffer and file        handling
  9387.  
  9388.            PUBLIC  buffer,pbuffer,sbuffer,fsize,namebuf
  9389.  buffer          LABEL          DWORD
  9390.  pbuffer          DW          0                ; Position in buffer (offset)
  9391.  sbuffer          DW          ?                ; Base of buffer (segment)
  9392.  lbuffer          DW          ?                ; Length of buffer
  9393.  fhandle          DW          ?                ; Holds        file handle on o
  9394.  fsize          DW          ?                ; File size after dosopen
  9395.  
  9396.  prompt          DB          13,10,13,10,"Enter filename: $"
  9397.  prompt2          DB          13,10,"File problem. Try again? $"
  9398.  namebuf          DB          66,?
  9399.  filename  DB          66 DUP (0)                ; Buffer for file name
  9400.  
  9401.  err1          DB          13,10,"Must have DOS 2.0 or higher",13,10,"$"
  9402.  err2          DB          13,10,"File too big",13,10,"$"
  9403.  
  9404.  ; Call table
  9405.  
  9406.  exkeys          DB          71,72,73,79,80,81        ; Extended key codes
  9407.  lexkeys          EQU          $-exkeys                ; Table        of keys
  9408.  extable          DW          homek
  9409.            DW          upk
  9410.            DW          pgupk
  9411.            DW          endk
  9412.            DW          downk
  9413.            DW          pgdnk
  9414.            DW          nonek
  9415.  
  9416.            .CODE
  9417.            EXTRN          pager:PROC,isEGA:PROC        ; Routines in other mod
  9418.  start:          mov          ax,@DATA                ; Initialize data segmen
  9419.            mov          ds,ax
  9420.  
  9421.            cli                                ; Turn off interrupts
  9422.            mov          ss,ax                        ; Make SS and
  9423.            mov          sp,OFFSET STACK        ;   SP relative        to DGROU
  9424.            sti
  9425.  
  9426.  ; Adjust memory        allocation
  9427.  
  9428.            mov          bx,sp                        ; Convert stack        po
  9429.            mov          cl,4                        ;   to get stack size
  9430.            shr          bx,cl
  9431.            add          ax,bx                        ; Add SS to get        en
  9432.            mov          bx,es                        ; Get start of program
  9433.            sub          ax,bx                        ; Subtract start from end
  9434.            @ModBlok ax                        ; Release memory after program
  9435.  
  9436.  ; Allocate dynamic memory for file buffer
  9437.  
  9438.            @GetBlok 0FFFh                ; Try to allocate 64K
  9439.            mov          sbuffer,ax                ; Save buffer segment
  9440.            mov          lbuffer,bx                ; Save actual length allocat
  9441.  
  9442.  ; Check        DOS
  9443.  
  9444.            @GetVer                        ; Get DOS version
  9445.            cmp          al,2                        ; Requires DOS 2.0
  9446.            jge          video
  9447.            @DispStr err1                        ;   else error and quit
  9448.            int          20h
  9449.  
  9450.  ; Adjust for current mode and and video        adapter
  9451.  
  9452.  video:          call          isEGA                        ; EGA (or VGA)?
  9453.            or          ax,ax                        ; If 0 must be CGA or MA
  9454.            je          modechk                ; Leave        default
  9455.            mov          rows,ax                ; Load rows
  9456.            dec          cga                        ; Not CGA
  9457.  
  9458.  modechk:  @GetMode                        ; Get video mode
  9459.            mov          mode,al                ; Save initial mode and
  9460.            mov          pag,bh
  9461.            mov          dl,al                        ; Work on copy
  9462.            cmp          dl,7                        ; Is it        mono 7?
  9463.            je          loadmono                ; Yes? Set mono
  9464.            cmp          dl,15                        ; Is it        mono 15?
  9465.            jne          graphchk                ; No? Check graphics
  9466.  loadmono: mov          vidadr,mono                ; Load mono address
  9467.            mov          statatr,bwstat        ; Set B&W defaults for status li
  9468.            mov          scrnatr,bwscrn        ;   and        screen background
  9469.            dec          cga                        ; Not CGA
  9470.            cmp          al,15                        ; Is it        mono 15?
  9471.            jne          cmdchk                ; No? Done
  9472.            mov          dl,7                        ; Yes? Set standard mono
  9473.            jmp          SHORT        chmode
  9474.  
  9475.  graphchk: cmp          dl,7                        ; 7 or higher?
  9476.            jg          color                        ; 8 to 14 are color (7 and
  9477.            cmp          dl,4                        ; 4 or higher?
  9478.            jg          bnw                        ; 5 and        6 are probabl
  9479.            je          color                        ; 4 is color
  9480.            test          dl,1                        ; Even?
  9481.            jz          bnw                        ; 0 and        2 are black a
  9482.  color:                                        ; 1 and        3 are color
  9483.            cmp          dl,3                        ; 3?
  9484.            je          cmdchk                ; Yes? Done
  9485.            mov          dl,3                        ; Change mode to 3
  9486.            jmp          SHORT        chmode
  9487.  
  9488.  bnw:          mov          statatr,bwstat        ; Set B&W defaults for statu
  9489.            mov          scrnatr,bwscrn        ;   and        screen background
  9490.            cmp          dl,2                        ; 2?
  9491.            je          cmdchk                ; Yes? Done
  9492.            mov          dl,2                        ; Make it 2
  9493.  
  9494.  chmode:          @SetMode dl                        ; Set video mode
  9495.            @SetPage 0                        ; Set video page
  9496.            mov          newvid,1                ; Set flag
  9497.  
  9498.  ; Try to open command line file
  9499.  
  9500.  cmdchk:          mov          bl,es:[80h]                ; Get length
  9501.            sub          bh,bh
  9502.            mov          WORD PTR es:[bx+81h],0; Convert to ASCIIZ
  9503.            push          ds
  9504.            @OpenFil 82h,0,es                ; Open argument
  9505.            pop          ds
  9506.            jc          getname                ; If error, get        from prom
  9507.            mov          fhandle,ax                ;   else save handle
  9508.            push          ds
  9509.            @GetFirst 82h,,es                ; Let DOS convert to file name
  9510.            pop          ds
  9511.            jnc          opened                ; If OK        file is        op
  9512.  
  9513.  ; Prompt for file
  9514.  
  9515.  getname:  @DispStr prompt                ; Prompt for file
  9516.            @GetStr namebuf,0                ; Get response as ASCIIZ
  9517.            @OpenFil filename,0                ; Try to open response
  9518.            jc          badfile                ; If successful, continue
  9519.            mov          fhandle,ax                ; Save handle
  9520.            @GetFirst filename                ; Let DOS convert to file name
  9521.            jnc          opened                ; If OK, file is opened
  9522.  
  9523.  badfile:  @DispStr prompt2                ;    else prompt to try        agai
  9524.            @GetKey 0,1,0
  9525.            and          al,11011111b                ; Convert key to uppercase
  9526.            cmp          al,"Y"                ; If yes,
  9527.            je          getname                ;   try        again
  9528.            jmp          quit                        ;   else quit
  9529.  
  9530.  ; Copy file name to status line
  9531.  
  9532.  opened:          mov          si,9Eh                ; Load FCB as as source
  9533.            mov          di,OFFSET statfile[7]        ; Load status line as des
  9534.            mov          al,es:[si]                ; Load first byte
  9535.            inc          si
  9536.  copy:          mov          [di],al                ; Save and load        byt
  9537.            inc          di
  9538.            mov          al,es:[si]
  9539.            inc          si
  9540.            or          al,al                        ; Check        for 0
  9541.            loopne  copy
  9542.  
  9543.  ; Check        file size
  9544.  
  9545.            @GetFilSz fhandle                ; Get file size
  9546.  
  9547.            or          dx,dx                        ; Larger than 64K?
  9548.            jne          big                        ; Yes? Too big
  9549.            mov          fsize,ax                ; Save file size
  9550.            mov          cx,4                        ; Convert to paragraphs
  9551.            shr          ax,cl
  9552.            cmp          ax,lbuffer                ; Is it        larger than b
  9553.            jle          fileread                ; No? Continue
  9554.  
  9555.  big:          @DispStr err2                        ;   else error
  9556.            @Exit          2
  9557.  
  9558.  fileread: push          ds
  9559.            @Read          buffer,fsize,fhandle        ; Read file
  9560.            pop          ds
  9561.            jnc          readok                ; If no        read error contin
  9562.            jmp          getname                ;   else try again
  9563.  
  9564.  ; Search back for EOF marker and adjust        if necessary
  9565.  
  9566.  readok:          mov          di,ax                        ; Load file length
  9567.            push          es                        ; Save ES and load buffer s
  9568.            mov          es,sbuffer
  9569.            std                                ; Look backward        for 255
  9570.            mov          cx,0FFh
  9571.            mov          al,1Ah                ; Search for EOF marker
  9572.            repne          scasb
  9573.            cld
  9574.            jcxz          noeof                        ; If none, we're OK
  9575.            inc          di                        ;   else adjust        and s
  9576.            mov          fsize,di
  9577.  
  9578.  noeof:          pop          es
  9579.            @SetCurPos 0,43                ; Turn off cursor by moving off
  9580.  
  9581.  ; Display first        page
  9582.  
  9583.            xor          ax,ax                        ; Start        at 0
  9584.            push          ax
  9585.  firstpg:  call          pager
  9586.  
  9587.  ; Handle keys
  9588.  
  9589.  nextkey:  @GetKey 0,0,0                        ; Get a        key
  9590.  nextkey2: cmp          al,0                        ; Is it        a null?
  9591.            je          extended                ; Yes? Must be extended
  9592.  
  9593.            cmp          al,27                        ; Is it        ESCAPE?
  9594.            jne          nextkey                ; No? Ignore unknown command
  9595.  
  9596.  quit:          @ClosFil fhandle                ; Yes? Close file
  9597.            @FreeBlok sbuffer                ; Release buffer
  9598.            cmp          newvid,1                ; Restore video?
  9599.            jne          thatsall                ; No?
  9600.            @SetMode mode                        ; Restore video        mode, p
  9601.            @SetPage pag
  9602.  thatsall: mov          dx,rows                ; Load last row        and firs
  9603.            xchg          dl,dh
  9604.            mov          cx,dx                        ; Make row the same
  9605.            mov          dl,79
  9606.            @Scroll 0                        ; Clear        last line
  9607.            sub          dl,dl
  9608.            @SetCurPos                        ; Set cursor
  9609.  
  9610.            @Exit          0                        ; Quit
  9611.  
  9612.  extended: @GetKey 0,0,0                        ; Get extended code
  9613.            push          es
  9614.            push          ds                        ; Load DS into ES
  9615.            pop          es
  9616.            mov          di,OFFSET exkeys        ; Load address and length of k
  9617.            mov          cx,lexkeys+1
  9618.            repne          scasb                        ; Find position
  9619.            pop          es
  9620.            sub          di,(OFFSET exkeys)+1        ; Point        to key
  9621.            shl          di,1                        ; Adjust pointer for word
  9622.            call          extable[di]                ; Call procedure
  9623.            jmp          nextkey
  9624.  
  9625.  homek:          mov          pbuffer,0                ; HOME - set position t
  9626.            push          pbuffer
  9627.            mov          linenum,1
  9628.            call          pager
  9629.            retn
  9630.  
  9631.  upk:          mov          ax,-1                        ; UP - scroll back 1
  9632.            push          ax
  9633.            call          pager
  9634.            retn
  9635.  
  9636.  pgupk:          mov          ax,rows                ; PGUP - Page back
  9637.            neg          ax
  9638.            push          ax
  9639.            call          pager
  9640.            retn
  9641.  
  9642.  endk:          mov          ax,fsize                ; END -        Get last b
  9643.            mov          pbuffer,ax                ; Make it the file position
  9644.            mov          linenum,-1                ; Set illegal line number as
  9645.            mov          ax,rows                ; Page back
  9646.            neg          ax
  9647.            push          ax
  9648.            call          pager
  9649.            retn
  9650.  
  9651.  downk:          mov          ax,1                        ; DOWN - scroll
  9652.            push          ax
  9653.            call          pager
  9654.            retn
  9655.  
  9656.  pgdnk:          push          rows                        ; PGDN - page forwa
  9657.            call          pager
  9658.            retn
  9659.  
  9660.  nonek:          retn                                ; Ignore unknown key
  9661.  
  9662.            END         start
  9663.  
  9664.  
  9665.