home *** CD-ROM | disk | FTP | other *** search
/ Best of Walnut Creek / 1-57176-081-4_Best_of_Walnut_Creek_1995-04.iso / mac / UTILS / ASK.ASM < prev    next >
Assembly Source File  |  1995-03-03  |  33KB  |  852 lines

  1. ;--------------------------------------------------------------------------;
  2. ;  Program:    Ask    .Asm                                                 ;
  3. ;  Purpose:    Ask question in batch file and time out if desired.         ;
  4. ;  Notes:      Compiles under TURBO Assembler, v2.0. Should work on any    ;
  5. ;                 machine running MS-DOS, v2.xx or higher.                 ;
  6. ;  Status:     Released into the public domain. Enjoy! If you use it,      ;
  7. ;                 let me know what you think. You don't have to send       ;
  8. ;                 any money, just comments and suggestions.                ;
  9. ;  Updates:    23-Apr-90, v1.0, GAT                                        ;
  10. ;                 - initial version.                                       ;
  11. ;              13-May-90, GAT                                              ;
  12. ;                 - fixed problem with return code if time-out reached.    ;
  13. ;              08-Jul-90, GAT                                              ;
  14. ;                 - added macros to push/pop registers.                    ;
  15. ;              28-Aug-90, v1.1a, GAT                                       ;
  16. ;                 - put equates and macros in separate files.              ;
  17. ;                 - put common routines in libs.                           ;
  18. ;--------------------------------------------------------------------------;
  19.  
  20. ;--------------------------------------------------------------------------;
  21. ;  Author:     George A. Theall                                            ;
  22. ;  Phone:      +1 215 662 0558                                             ;
  23. ;  SnailMail:  TifaWARE                                                    ;
  24. ;              506 South 41st St., #3M                                     ;
  25. ;              Philadelphia, PA.  19104   USA                              ;
  26. ;  E-Mail:     GTHEALL@PENNDRLS.UPENN.EDU (Internet)                       ;
  27. ;--------------------------------------------------------------------------;
  28.  
  29. ;--------------------------------------------------------------------------;
  30. ;                          D I R E C T I V E S                             ;
  31. ;--------------------------------------------------------------------------;
  32. DOSSEG
  33. MODEL                tiny
  34.  
  35. IDEAL
  36. LOCALS
  37. JUMPS
  38.  
  39. ;
  40. ; This section comes from D:\ASM\INCLUDE\Equates.Inc.
  41. ;
  42. EOS                 EQU       0              ; terminates strings
  43. BELL                EQU       7
  44. BS                  EQU       8
  45. TAB                 EQU       9
  46. CR                  EQU       13
  47. LF                  EQU       10
  48. ESCAPE              EQU       27             ; nb: ESC is a TASM keyword
  49. SPACE               EQU       ' '
  50. KEY_F1              EQU       3bh
  51. KEY_F2              EQU       3ch
  52. KEY_F3              EQU       3dh
  53. KEY_F4              EQU       3eh
  54. KEY_F5              EQU       3fh
  55. KEY_F6              EQU       40h
  56. KEY_F7              EQU       41h
  57. KEY_F8              EQU       42h
  58. KEY_F9              EQU       43h
  59. KEY_F10             EQU       44h
  60. KEY_HOME            EQU       47h
  61. KEY_UP              EQU       48h
  62. KEY_PGUP            EQU       49h
  63. KEY_LEFT            EQU       4bh
  64. KEY_RIGHT           EQU       4dh
  65. KEY_END             EQU       4fh
  66. KEY_DOWN            EQU       50h
  67. KEY_PGDN            EQU       51h
  68. KEY_INS             EQU       52h
  69. KEY_DEL             EQU       53h
  70. KEY_C_F1            EQU       5eh
  71. KEY_C_F2            EQU       5fh
  72. KEY_C_F3            EQU       60h
  73. KEY_C_F4            EQU       61h
  74. KEY_C_F5            EQU       62h
  75. KEY_C_F6            EQU       63h
  76. KEY_C_F7            EQU       64h
  77. KEY_C_F8            EQU       65h
  78. KEY_C_F9            EQU       66h
  79. KEY_C_F10           EQU       67h
  80. KEY_C_LEFT          EQU       73h
  81. KEY_C_RIGHT         EQU       74h
  82. KEY_C_END           EQU       75h
  83. KEY_C_PGDN          EQU       76h
  84. KEY_C_HOME          EQU       77h
  85. KEY_C_PGUP          EQU       84h
  86. KEY_F11             EQU       85h
  87. KEY_F12             EQU       86h
  88. KEY_C_F11           EQU       89h
  89. KEY_C_F12           EQU       8ah
  90. DOS                 EQU       21h            ; main MSDOS interrupt
  91. STDIN               EQU       0              ; standard input
  92. STDOUT              EQU       1              ; standard output
  93. STDERR              EQU       2              ; error output
  94. STDAUX              EQU       3              ; COM port
  95. STDPRN              EQU       4              ; printer
  96.  
  97. ;
  98. ; This section comes from D:\ASM\INCLUDE\Macros.Inc.
  99. ;
  100. MACRO    Pop_M    RegList                    ;; Pops registers off stack.
  101.    IRP      Reg, <RegList>
  102.       IFIDNI   <Reg>, <flags>
  103.          popf
  104.       ELSE
  105.          pop      Reg
  106.       ENDIF
  107.    ENDM
  108. ENDM
  109. MACRO    Push_M   RegList                    ;; Pushes registers onto stack.
  110.    IRP      Reg, <RegList>
  111.       IFIDNI   <Reg>, <flags>
  112.          pushf
  113.       ELSE
  114.          push     Reg
  115.       ENDIF
  116.    ENDM
  117. ENDM
  118. MACRO    Zero     Reg                        ;; Zeros any register.
  119.          xor      Reg, Reg
  120. ENDM
  121.  
  122.  
  123. ERRH                 equ      255            ; errorlevel if help given
  124.  
  125.  
  126. ;--------------------------------------------------------------------------;
  127. ;                        C O D E    S E G M E N T                          ;
  128. ;--------------------------------------------------------------------------;
  129. CODESEG
  130.  
  131. ORG       80h                                ; commandline
  132. LABEL     CmdLen    BYTE
  133.           db        ?
  134. LABEL     CmdLine   BYTE
  135.           db        127 dup (?)
  136.  
  137. ORG       100h                               ; start of .COM file
  138. STARTUPCODE
  139.           jmp       main                     ; skip over data and stack
  140.  
  141. ;--------------------------------------------------------------------------;
  142. ;                               D A T A                                    ;
  143. ;--------------------------------------------------------------------------;
  144. LABEL     ProgName  BYTE
  145.           db        'ask: ', EOS
  146. LABEL     EOL       BYTE
  147.           db        '.', CR, LF, EOS
  148. LABEL     HelpMsg   BYTE
  149.           db        CR, LF
  150.           db        'TifaWARE ASK, v1.1a, ', ??Date
  151.           db        ' - ask questions in batch files.', CR, LF
  152.           db        'Usage: ask [-options] [msgtxt]', CR, LF, LF
  153.           db        'Options:', CR, LF
  154.           db        '  -l  = convert response to lower case', CR, LF
  155.           db        '  -tn = wait n seconds before timing out', CR, LF
  156.           db        '  -u  = convert response to upper case', CR, LF
  157.           db        '  -?  = display this help message', CR, LF, LF
  158.           db        'msgtxt is an optional message to display.', CR, LF, EOS
  159. LABEL     Err1Msg   BYTE
  160.           db        'illegal option -- '
  161. LABEL     OptCh     BYTE
  162.           db        ?
  163.           db        EOS
  164. LABEL     Err2Msg   BYTE
  165.           db        'time-out value not specified', EOS
  166. LABEL     Err3Msg   BYTE
  167.           db        'time-out value too large -- ', EOS
  168.  
  169. SwitCh    db        '-'                      ; char introducing options
  170. HFlag     db        0                        ; flag for on-line help
  171. LFlag     db        0                        ; flag for lowercase response
  172. TFlag     db        0                        ; flag for time-out
  173. UFlag     db        0                        ; flag for uppercase response
  174. Delay     dw        ?                        ; time to pause for key
  175. MsgLen    db        0                        ; length of message text
  176. MsgTxt    dw        ?                        ; near pointer to message text
  177. RCode     db        0                        ; program return code
  178.  
  179.  
  180. ;--------------------------------------------------------------------------;
  181. ;                          L O C A L   S T A C K                           ;
  182. ;--------------------------------------------------------------------------;
  183.           db        16 dup ("STACK   ")      ; 128 bytes for local stack
  184. StackTop  =         $
  185.  
  186. ;--------------------------------------------------------------------------;
  187. ;                           P R O C E D U R E S                            ;
  188. ;--------------------------------------------------------------------------;
  189. ;----  skip_Spaces  -------------------------------------------------------;
  190. ;  Purpose:    Skips past spaces in a string.                              ;
  191. ;  Notes:      Scanning stops with either a non-space *OR* CX = 0.         ;
  192. ;  Entry:      DS:SI = start of string to scan.                            ;
  193. ;  Exit:       AL = next non-space character,                              ;
  194. ;              CX is adjusted as necessary,                                ;
  195. ;              DS:SI = pointer to next non-space.                          ;
  196. ;  Calls:      none                                                        ;
  197. ;  Changes:    AL, CX, SI                                                  ;
  198. ;--------------------------------------------------------------------------;
  199. PROC skip_Spaces
  200.  
  201.           jcxz      SHORT @@Fin
  202. @@NextCh:
  203.           lodsb
  204.           cmp       al, ' '
  205.           loopz     @@NextCh
  206.           jz        SHORT @@Fin              ; CX = 0; don't adjust
  207.  
  208.           inc       cx                       ; adjust counters if cx > 0
  209.           dec       si
  210.  
  211. @@Fin:
  212.           ret
  213. ENDP skip_Spaces
  214.  
  215.  
  216. ;----  get_Opt  -----------------------------------------------------------;
  217. ;  Purpose:    Get a commandline option.                                   ;
  218. ;  Notes:      none                                                        ;
  219. ;  Entry:      AL = option character,                                      ;
  220. ;              CX = count of characters left in commandline,               ;
  221. ;              DS:SI = pointer to first option to process.                 ;
  222. ;  Exit:       CX = count of characters left _after_ processing,           ;
  223. ;              DS:SI = pointer to whitespace _after_ options,              ;
  224. ;  Calls:      tolower, errmsg, isdigit, atou, fputs                       ;
  225. ;  Changes:    AX, BL, CX, DX, SI,                                         ;
  226. ;              [OptCh], [HFlag], [LFlag], [TFlag], [UFlag], [Delay]        ;
  227. ;--------------------------------------------------------------------------;
  228. PROC get_Opt
  229.  
  230.           mov       [OptCh], al              ; save for later
  231.           call      tolower                  ; use only lowercase in cmp.
  232.           cmp       al, 'l'
  233.           jz        SHORT @@OptL
  234.           cmp       al, 't'
  235.           jz        SHORT @@OptT
  236.           cmp       al, 'u'
  237.           jz        SHORT @@OptU
  238.           cmp       al, '?'
  239.           jz        SHORT @@OptH
  240.           mov       dx, OFFSET Err1Msg       ; unrecognized option
  241.           call      errmsg                   ; then *** DROP THRU *** to OptH
  242.  
  243. ;
  244. ; Various possible options.
  245. ;
  246. @@OptH:
  247.           mov       [HFlag], 1               ; set help flag
  248.           jmp       SHORT @@Fin
  249.  
  250. @@OptL:
  251.           mov       [LFlag], 1               ; set lowercase flag
  252.           jmp       SHORT @@Fin
  253.  
  254. @@OptT:
  255.           mov       [TFlag], 1               ; set time-out flag
  256.           mov       al, [BYTE PTR si]        ; get next character
  257.           call      isdigit                  ; if not a digit, trouble!
  258.           jz        SHORT @@GetDelay
  259.  
  260.           mov       dx, OFFSET Err2Msg       ; no delay specified
  261.           call      errmsg
  262.           jmp       @@OptH
  263.  
  264. @@GetDelay:
  265.           mov       dx, si                   ; save to adjust CX and if error
  266.           call      atou
  267.           pushf                              ; preserve flags
  268.           add       cx, dx                   ; adjust counter
  269.           sub       cx, si
  270.           popf                               ; restore flags
  271.           jc        SHORT @@BadDelay         ; error in conversion?
  272.           cmp       ax, 60*60*12             ; 12 or more hours?
  273.           jae       SHORT @@BadDelay         ;   yes, bad delay
  274.           mov       [Delay], ax
  275.           jmp       SHORT @@Fin
  276.  
  277. @@BadDelay:
  278.           push      dx
  279.           mov       bx, STDERR
  280.           mov       dx, OFFSET ProgName
  281.           call      fputs
  282.           mov       dx, OFFSET Err3Msg
  283.           call      fputs
  284.           pop       dx
  285.           mov       al, [si]                 ; save next non-digit
  286.           mov       [BYTE PTR si], EOS       ; replace with EOS
  287.           call      fputs
  288.           mov       [si], al                 ; restore it
  289.           mov       dx, OFFSET EOL
  290.           call      fputs
  291.           jmp       SHORT @@OptH
  292.  
  293. @@OptU:
  294.           mov       [UFlag], 1               ; set uppercase flag
  295.  
  296. @@Fin:
  297.           ret
  298. ENDP get_Opt
  299.  
  300.  
  301. ;----  get_Arg  -----------------------------------------------------------;
  302. ;  Purpose:    Gets a non-option from the set of commandline arguments.    ;
  303. ;  Notes:      Anything left on the commandline is user's message text.    ;
  304. ;  Entry:      CX = count of characters left in commandline,               ;
  305. ;              DS:SI = pointer to argument to process.                     ;
  306. ;  Exit:       CX = zero                                                   ;
  307. ;              DS:SI = points to CR after commandline.                     ;
  308. ;  Calls:      none                                                        ;
  309. ;  Changes:    CX, SI                                                      ;
  310. ;              [MsgLen], [MsgTxt]                                          ;
  311. ;--------------------------------------------------------------------------;
  312. PROC get_Arg
  313.  
  314.           mov       [MsgLen], cl             ; for safekeeping
  315.           mov       [MsgTxt], si
  316.           add       si, cx                   ; adjust so nothing's left
  317.           Zero      cl
  318.           mov       [BYTE PTR si], EOS       ; finish off string
  319.  
  320.           ret
  321. ENDP get_Arg
  322.  
  323.  
  324. ;----  process_CmdLine  ---------------------------------------------------;
  325. ;  Purpose:    Processes commandline arguments.                            ;
  326. ;  Notes:      A switch character by itself is ignored.                    ;
  327. ;              No arguments whatsoever causes help flag to be set.         ;
  328. ;  Entry:      n/a                                                         ;
  329. ;  Exit:       n/a                                                         ;
  330. ;  Calls:      skip_Spaces, get_Opt, get_Arg                               ;
  331. ;  Changes:    AX, CX, SI,                                                 ;
  332. ;              BL, DX (get_Opt),                                           ;
  333. ;              [HFlag],                                                    ;
  334. ;              [OptCh], [LFlag], [TFlag], [UFlag], [Delay] (get_Opt),      ;
  335. ;              [MsgLen], [MsgTxt] (get_Arg),                               ;
  336. ;              Direction flag is cleared.                                  ;
  337. ;--------------------------------------------------------------------------;
  338. PROC process_CmdLine
  339.  
  340.           cld                                ; forward, march!
  341.           Zero      ch, ch
  342.           mov       cl, [CmdLen]             ; length of commandline
  343.           mov       si, OFFSET CmdLine       ; offset to start of commandline
  344.  
  345.           call      skip_Spaces              ; check if any args supplied
  346.           or        cl, cl
  347.           jnz       SHORT @@ArgLoop
  348.           mov       [HFlag], 1               ; if none, set help flag
  349.           jmp       SHORT @@Fin
  350.  
  351. ;
  352. ; For each blank-delineated argument on the commandline...
  353. ;
  354. @@ArgLoop:
  355.           lodsb                              ; next character
  356.           dec       cl
  357.           cmp       al, [SwitCh]             ; is it the switch character?
  358.           jnz       SHORT @@NonOpt           ;   no
  359.  
  360. ;
  361. ; Isolate each option and process it. Stop when a space is reached.
  362. ;
  363. @@OptLoop:
  364.           jcxz      SHORT @@Fin              ; abort if nothing left
  365.           lodsb
  366.           dec       cl
  367.           cmp       al, ' '
  368.           jz        SHORT @@NextArg          ; abort when space reached
  369.           call      get_Opt
  370.           jmp       @@OptLoop
  371.  
  372. ;
  373. ; Process the current argument, which is *not* an option.
  374. ; Then, *drop thru* to advance to next argument.
  375. ;
  376. @@NonOpt:
  377.           dec       si                       ; back up one character
  378.           inc       cl
  379.           call      get_Arg
  380.  
  381. ;
  382. ; Skip over spaces until next argument is reached.
  383. ;
  384. @@NextArg:
  385.           call      skip_Spaces
  386.           or        cl, cl
  387.           jnz       @@ArgLoop
  388.  
  389. @@Fin:
  390.           ret
  391. ENDP process_CmdLine
  392.  
  393.  
  394. ;--------------------------------------------------------------------------;
  395. ;                         E N T R Y   P O I N T                            ;
  396. ;--------------------------------------------------------------------------;
  397. ;----  main  --------------------------------------------------------------;
  398. ;  Purpose:    Main section of program.                                    ;
  399. ;  Notes:      none                                                        ;
  400. ;  Entry:      Arguments as desired                                        ;
  401. ;  Exit:       Return code as follows:                                     ;
  402. ;                   0   => program timed-out                               ;
  403. ;                   255 => on-line help requested                          ;
  404. ;                   else => ASCII value of character pressed.              ;
  405. ;  Calls:      process_CmdLine, fputs, pause, getchar, tolower, toupper    ;
  406. ;  Changes:    n/a                                                         ;
  407. ;--------------------------------------------------------------------------;
  408. main:
  409.           mov       sp, OFFSET StackTop      ; set up local stack
  410.  
  411. ;
  412. ; Process commandline arguments. If the variable HFlag is set, then
  413. ; on-line help is displayed and the program immediately terminates.
  414. ;
  415.           call      process_CmdLine          ; process commandline args
  416.  
  417.           cmp       [HFlag], 0               ; is help needed?
  418.           jz        SHORT @@NoHelp           ;   no
  419.           mov       al, ERRH                 ;   yes, so set return code
  420.           mov       bx, STDERR
  421.           mov       dx, OFFSET HelpMsg       ;     point to help message
  422.           call      fputs
  423.           jmp       SHORT @@Fin              ;     and jump to end of program
  424.  
  425. ;
  426. ; Display any message from commandline then get keypress from user.
  427. ;
  428. @@NoHelp:
  429.           mov       bx, STDOUT               ; everything to stdout
  430.           cmp       [MsgLen], 0              ; anything to print out?
  431.           jz        SHORT @@NoPrompt         ;   nope
  432.           mov       dx, [MsgTxt]
  433.           call      fputs
  434.  
  435. @@NoPrompt:
  436.           cmp       [TFlag], 0               ; need to wait?
  437.           jz        SHORT @@KeyIn            ;   no
  438.           mov       ax, [Delay]              ;   yes, so...
  439.           call      pause                    ;     pause
  440.           jz        SHORT @@KeyIn            ;     zf means a key is ready
  441.           Zero      al                       ;     set return code to zero
  442.           jmp       SHORT @@NewLine
  443.  
  444. @@KeyIn:
  445.           call      getchar
  446.  
  447. ;
  448. ; Convert character in AL as necessary. NB: if both '-l' and '-u' options
  449. ; are specified, the return value will be based on *uppercase* value.
  450. ;
  451.           cmp       [LFlag], 0               ; convert to lowercase?
  452.           jz        SHORT @@MaybeUpper       ;   no
  453.           call      tolower
  454.  
  455. @@MaybeUpper:
  456.           cmp       [UFlag], 0               ; convert to uppercase?
  457.           jz        SHORT @@NewLine          ;   no
  458.           call      toupper
  459.  
  460. ;
  461. ; Add a final newline to keep things neat.
  462. ;
  463. @@NewLine:
  464.           mov       dx, OFFSET EOL + 1
  465.           call      fputs
  466.  
  467. ;
  468. ; Ok, let's terminate the program. Return code is already in AL.
  469. ;
  470. @@Fin:
  471.           mov       ah, 4ch
  472.           int       DOS
  473.  
  474. EVEN
  475. ;-------------------------------------------------------------------------;
  476. ;  Purpose:    Gets current system time, based on DOS's internal clock.
  477. ;  Notes:      none
  478. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  479. ;  Entry:      n/a
  480. ;  Exit:       CL = minutes (0 - 59)
  481. ;              CH = hour (0 - 23)
  482. ;              DL = hundredths of seconds (0 - 99)
  483. ;              DH = seconds (0 - 59)
  484. ;  Calls:      none
  485. ;  Changes:    CX, DX
  486. ;-------------------------------------------------------------------------;
  487. PROC gettime
  488.  
  489.    push     ax
  490.    mov      ah, 2ch                       ; MS-DOS get system time function
  491.    int      DOS
  492.    pop      ax
  493.    ret
  494.  
  495. ENDP gettime
  496.  
  497.  
  498. EVEN
  499. ;-------------------------------------------------------------------------;
  500. ;  Purpose:    Writes an ASCIIZ string to specified device.
  501. ;  Notes:      A zero-length string doesn't seem to cause problems when
  502. ;                 this output function is used.
  503. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  504. ;  Entry:      BX = device handle,
  505. ;              DS:DX = pointer to string.
  506. ;  Exit:       Carry flag set if EOS wasn't found or handle is invalid.
  507. ;  Calls:      strlen
  508. ;  Changes:    none
  509. ;-------------------------------------------------------------------------;
  510. PROC fputs
  511.  
  512. IF @DataSize EQ 0
  513.    Push_M   <ax, cx, di>
  514. ELSE
  515.    Push_M   <ax, cx, di, es>
  516.    mov      ax, ds
  517.    mov      es, ax
  518. ENDIF
  519.    mov      di, dx
  520.    call     strlen                        ; set CX = length of string
  521.    jc       SHORT @@Fin                   ; abort if problem finding end
  522.    mov      ah, 40h                       ; MS-DOS raw output function
  523.    int      DOS
  524. @@Fin:
  525. IF @DataSize EQ 0
  526.    Pop_M    <di, cx, ax>
  527. ELSE
  528.    Pop_M    <es, di, cx, ax>
  529. ENDIF
  530.    ret
  531.  
  532. ENDP fputs
  533.  
  534.  
  535. EVEN
  536. ;-------------------------------------------------------------------------;
  537. ;  Purpose:    Reads a character from STDIN.
  538. ;  Notes:      Character is echoed to display.
  539. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  540. ;  Entry:      n/a
  541. ;  Exit:       AL = character.
  542. ;  Calls:      none
  543. ;  Changes:    AX
  544. ;-------------------------------------------------------------------------;
  545. PROC getchar
  546.  
  547.    mov      ah, 1
  548.    int      DOS
  549.    ret
  550.  
  551. ENDP getchar
  552.  
  553.  
  554. EVEN
  555. ;-------------------------------------------------------------------------;
  556. ;  Purpose:    Writes an error message to stderr.
  557. ;  Notes:      none
  558. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  559. ;  Entry:      DS:DX = pointer to error message.
  560. ;  Exit:       n/a
  561. ;  Calls:      fputs
  562. ;  Changes:    none
  563. ;-------------------------------------------------------------------------;
  564. PROC errmsg
  565.  
  566.    Push_M   <bx, dx>
  567.    push     dx                            ; save again calling parameters
  568.    mov      bx, STDERR
  569.    mov      dx, OFFSET ProgName           ; display program name
  570.    call     fputs
  571.    pop      dx                            ; recover calling parameters
  572.    call     fputs                         ; display error message
  573.    mov      dx, OFFSET EOL
  574.    call     fputs
  575.    Pop_M    <dx, bx>
  576.    ret
  577.  
  578. ENDP errmsg
  579.  
  580.  
  581. EVEN
  582. ;-------------------------------------------------------------------------;
  583. ;  Purpose:    Checks if a character is ready for input from STDIN.
  584. ;  Notes:      none
  585. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  586. ;  Entry:      n/a
  587. ;  Exit:       zf = 1 if character available.
  588. ;  Calls:      none
  589. ;  Changes:    flags
  590. ;-------------------------------------------------------------------------;
  591. PROC kbhit
  592.  
  593.    push     ax
  594.    mov      ah, 0bh
  595.    int      DOS
  596.    cmp      al, 0ffh                      ; AL = FFh if character ready
  597.    pop      ax
  598.    ret
  599.  
  600. ENDP kbhit
  601.  
  602.  
  603. EVEN
  604. ;-------------------------------------------------------------------------;
  605. ;  Purpose:    Pauses execution for a specified number of seconds or 
  606. ;                 until a keypress is detected.
  607. ;  Notes:      Delay should be less than 12 hours (43200 seconds) to 
  608. ;                 avoid checks on date rollover yet ensure we haven't
  609. ;                 paused too long. Delay is not checked however!
  610. ;              This procedure works by adding the delay to the current
  611. ;                 time and waiting until then. If the system clock is
  612. ;                 adjusted in meantime, results are unpredictable. I
  613. ;                 tried looping and calling gettime(), but that was
  614. ;                 inaccurate due to roundoff of hundreths of secs.
  615. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  616. ;  Entry:      AX = delay time (in seconds).
  617. ;  Exit:       Zero flag set if input ready; cleared otherwise.
  618. ;  Calls:      gettime, kbhit
  619. ;  Changes:    flags
  620. ;-------------------------------------------------------------------------;
  621. PROC pause
  622.  
  623.    Push_M   <ax, bx, cx, dx>
  624.    Zero     bx
  625.    mov      cx, 60                        ; 60 secs/min and 60 mins/hour
  626.    Zero     dx
  627.    div      cx                            ; now ax = hours and minutes
  628.    mov      bh, dl                        ; and bh = seconds (dh == 0)
  629.    Zero     dx
  630.    div      cx                            ; now ax = hours, dx = minutes
  631.    mov      ah, al                        ; hours
  632.    mov      al, dl                        ; minutes
  633.  
  634. ;
  635. ; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
  636. ;
  637.    call     gettime                       ; get current time
  638.    add      ax, cx                        ; compute alarm time
  639.    add      bx, dx
  640.    cmp      bh, 60                        ; too many seconds?
  641.    jb       SHORT @@CheckMins             ;   nope
  642.    sub      bh, 60                        ;   yep, adjust
  643.    inc      al
  644.  
  645. @@CheckMins:
  646.    cmp      al, 60                        ; too many minutes?
  647.    jb       SHORT @@CheckHours            ;   nope
  648.    sub      al, 60                        ;   yep, adjust
  649.    inc      ah
  650.  
  651. @@CheckHours:
  652.    cmp      ah, 24                        ; too many hours?
  653.    jb       SHORT @@TimeLoop              ;   nope
  654.    sub      ah, 24                        ;   yep, adjust
  655.  
  656. ;
  657. ; Here's the main loop. Check for both keypress and alarm time.
  658. ; NB: Because of overhead in the code it's possible to overshoot
  659. ; the alarm time by a few hundreths of a second so check for that.
  660. ;
  661. @@TimeLoop:
  662.    call     kbhit                         ; check for user input
  663.    jz       SHORT @@Fin                   ; and abort if present
  664.    call     gettime                       ; get current time
  665.    sub      cx, ax                        ; cx = current time - alarm time
  666.    jz       SHORT @@CheckSecs             ; if zero, check seconds
  667.    cmp      ah, 12                        ; adjust if alarm is a.m. ...
  668.    jae      SHORT @@TooFar?
  669.    cmp      ch, 0                         ;   and current time is p.m. ...
  670.    jl       SHORT @@TooFar?               ;      (signed comparison!!!)
  671.    sub      ch, 24                        ;   by subtracting 24 hours
  672.  
  673. @@TooFar?:
  674.    neg      cx                            ; cx = alarm time - current time
  675.    cmp      cx, 12 * 256                  ; more than 12 hours difference?
  676.    ja       SHORT @@Fin                   ;   yep (unsigned comparison!!!)
  677.    jmp      @@TimeLoop                    ;   nope (already checked if ==)
  678.  
  679. @@CheckSecs:
  680.    cmp      dx, bx                        ; wait a few more seconds?
  681.    jb       @@TimeLoop                    ;   yep
  682.    or       dl, 1                         ;   no, clear zf
  683.  
  684. @@Fin:
  685.    Pop_M    <dx, cx, bx, ax>
  686.    ret
  687.  
  688. ENDP pause
  689.  
  690.  
  691. EVEN
  692. ;-------------------------------------------------------------------------;
  693. ;  Purpose:    Converts string of digits to an *unsigned* integer in
  694. ;              range [0, 65535].
  695. ;  Notes:      Conversion stops with first non-numeric character.
  696. ;  Requires:   8086-class CPU.
  697. ;  Entry:      DS:SI = pointer to string of digits.
  698. ;  Exit:       AX = unsigned integer (garbage if cf = 1),
  699. ;              DS:SI = pointer to first non-digit found,
  700. ;              cf = 1 if number is too big.
  701. ;  Calls:      none
  702. ;  Changes:    AX, SI
  703. ;              flags
  704. ;-------------------------------------------------------------------------;
  705. PROC atou
  706.  
  707.    Push_M   <bx, cx, dx>                  ; DX destroyed by MUL below
  708.    Zero     ax                            ; AX = digit to convert
  709.    Zero     bx                            ; BX = integer word
  710.    mov      cx, 10                        ; CX = conversion factor
  711.  
  712. @@NextCh:
  713.    mov      bl, [si]                      ; get character
  714.    cmp      bl, '0'                       ; test if a digit
  715.    jb       SHORT @@Fin
  716.    cmp      bl, '9'
  717.    ja       SHORT @@Fin
  718.    inc      si                            ; bump up pointer
  719.    mul      cx                            ; multiply old result by 10
  720.    jc       SHORT @@Overflow
  721.    sub      bl, '0'                       ; convert digit
  722.    add      ax, bx                        ; add current value
  723.    jnc      @@NextCh                      ; continue unless result too big
  724.  
  725. @@Overflow:
  726.    Zero     cx                            ; denotes overflow
  727.    jmp      @@NextCh
  728.  
  729. @@Fin:
  730.    cmp      cx, 10                        ; cf = (cx != 10)
  731.    Pop_M    <dx, cx, bx>
  732.    ret
  733.  
  734. ENDP atou
  735.  
  736.  
  737. EVEN
  738. ;-------------------------------------------------------------------------;
  739. ;  Purpose:    Tests if character is a valid ASCII digit.
  740. ;  Notes:      none
  741. ;  Requires:   8086-class CPU.
  742. ;  Entry:      AL = character to be tested.
  743. ;  Exit:       Zero flag set if true, cleared otherwise.
  744. ;  Calls:      none 
  745. ;  Changes:    flags
  746. ;-------------------------------------------------------------------------;
  747. PROC isdigit
  748.  
  749.    cmp      al, '0'                       ; if < '0' zf = 0
  750.    jb       SHORT @@Fin
  751.    cmp      al, '9'                       ; if > '9' zf = 0
  752.    ja       SHORT @@Fin
  753.    cmp      al, al                        ; set Z flag
  754. @@Fin:
  755.    ret
  756.  
  757. ENDP isdigit
  758.  
  759.  
  760. EVEN
  761. ;-------------------------------------------------------------------------;
  762. ;  Purpose:    Converts character to lowercase.
  763. ;  Notes:      none
  764. ;  Requires:   8086-class CPU.
  765. ;  Entry:      AL = character to be converted.
  766. ;  Exit:       AL = converted character.
  767. ;  Calls:      none
  768. ;  Changes:    AL
  769. ;              flags
  770. ;-------------------------------------------------------------------------;
  771. PROC tolower
  772.  
  773.    cmp      al, 'A'                       ; if < 'A' then done
  774.    jb       SHORT @@Fin
  775.    cmp      al, 'Z'                       ; if > 'Z' then done
  776.    ja       SHORT @@Fin
  777.    or       al, 20h                       ; make it lowercase
  778. @@Fin:
  779.    ret
  780.  
  781. ENDP tolower
  782.  
  783.  
  784. EVEN
  785. ;-------------------------------------------------------------------------;
  786. ;  Purpose:    Converts character to uppercase.
  787. ;  Notes:      none
  788. ;  Requires:   8086-class CPU.
  789. ;  Entry:      AL = character to be converted.
  790. ;  Exit:       AL = converted character.
  791. ;  Calls:      none
  792. ;  Changes:    AL
  793. ;              flags
  794. ;-------------------------------------------------------------------------;
  795. PROC toupper
  796.  
  797.    cmp      al, 'a'                       ; if < 'a' then done
  798.    jb       SHORT @@Fin
  799.    cmp      al, 'z'                       ; if > 'z' then done
  800.    ja       SHORT @@Fin
  801.    and      al, not 20h                   ; make it lowercase
  802. @@Fin:
  803.    ret
  804.  
  805. ENDP toupper
  806.  
  807.  
  808. EVEN
  809. ;-------------------------------------------------------------------------;
  810. ;  Purpose:    Calculates length of an ASCIIZ string.
  811. ;  Notes:      Terminal char is _not_ included in the count.
  812. ;  Requires:   8086-class CPU.
  813. ;  Entry:      ES:DI = pointer to string.
  814. ;  Exit:       CX = length of string,
  815. ;              cf = 0 and zf = 1 if EOS found,
  816. ;              cf = 1 and zf = 0 if EOS not found within segment.
  817. ;  Calls:      none
  818. ;  Changes:    CX,
  819. ;              flags
  820. ;-------------------------------------------------------------------------;
  821. PROC strlen
  822.  
  823.    Push_M   <ax, di, flags>
  824.    cld                                    ; scan forward only
  825.    mov      al, EOS                       ; character to search for
  826.    mov      cx, di                        ; where are we now
  827.    not      cx                            ; what's left in segment - 1
  828.    push     cx                            ; save char count
  829.    repne    scasb
  830.    je       SHORT @@Done
  831.    scasb                                  ; test final char
  832.    dec      cx                            ; avoids trouble with "not" below
  833.  
  834. @@Done:
  835.    pop      ax                            ; get original count
  836.    sub      cx, ax                        ; subtract current count
  837.    not      cx                            ; and invert it
  838.    popf                                   ; restore df
  839.    dec      di
  840.    cmp      [BYTE PTR es:di], EOS
  841.    je       SHORT @@Fin                   ; cf = 0 if equal
  842.    stc                                    ; set cf => error
  843.  
  844. @@Fin:
  845.    Pop_M    <di, ax>
  846.    ret
  847.  
  848. ENDP strlen
  849.  
  850.  
  851. END
  852.