home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / batch / library / ask / ask.asm next >
Assembly Source File  |  1991-11-10  |  44KB  |  1,185 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. ;              12-Oct-91, v1.1b, GAT                                       ;
  19. ;                 - revised include file names.                            ;
  20. ;                 - replaced references to Push_M and Pop_M macros with    ;
  21. ;                    calls to push and pop.                                ;
  22. ;                 - removed local stack: it's not necessary.               ;
  23. ;--------------------------------------------------------------------------;
  24.  
  25. ;--------------------------------------------------------------------------;
  26. ;  Author:     George A. Theall                                            ;
  27. ;  Phone:      +1 215 662 0558                                             ;
  28. ;  SnailMail:  TifaWARE                                                    ;
  29. ;              506 South 41st St., #3M                                     ;
  30. ;              Philadelphia, PA.  19104   USA                              ;
  31. ;  E-Mail:     theall@gdalsrv.sas.upenn.edu (Internet)                     ;
  32. ;--------------------------------------------------------------------------;
  33.  
  34. ;--------------------------------------------------------------------------;
  35. ;                          D I R E C T I V E S                             ;
  36. ;--------------------------------------------------------------------------;
  37. DOSSEG
  38. MODEL                tiny
  39.  
  40. IDEAL
  41. LOCALS
  42. JUMPS
  43.  
  44. ;
  45. ; This section comes from Misc.Inc.
  46. ;
  47. @16BIT              EQU       (@CPU AND 8) EQ 0
  48. @32BIT              EQU       (@CPU AND 8)
  49. MACRO    ZERO     RegList                    ;; Zeros registers
  50.    IRP      Reg, <RegList>
  51.          xor      Reg, Reg
  52.    ENDM
  53. ENDM
  54.  
  55. ;
  56. ; This section comes from DOS.Inc.
  57. ;
  58. BELL                EQU       7
  59. BS                  EQU       8
  60. TAB                 EQU       9
  61. CR                  EQU       13
  62. LF                  EQU       10
  63. ESCAPE              EQU       27             ; nb: ESC is a TASM keyword
  64. SPACE               EQU       ' '
  65. KEY_F1              EQU       3bh
  66. KEY_F2              EQU       3ch
  67. KEY_F3              EQU       3dh
  68. KEY_F4              EQU       3eh
  69. KEY_F5              EQU       3fh
  70. KEY_F6              EQU       40h
  71. KEY_F7              EQU       41h
  72. KEY_F8              EQU       42h
  73. KEY_F9              EQU       43h
  74. KEY_F10             EQU       44h
  75. KEY_HOME            EQU       47h
  76. KEY_UP              EQU       48h
  77. KEY_PGUP            EQU       49h
  78. KEY_LEFT            EQU       4bh
  79. KEY_RIGHT           EQU       4dh
  80. KEY_END             EQU       4fh
  81. KEY_DOWN            EQU       50h
  82. KEY_PGDN            EQU       51h
  83. KEY_INS             EQU       52h
  84. KEY_DEL             EQU       53h
  85. KEY_C_F1            EQU       5eh
  86. KEY_C_F2            EQU       5fh
  87. KEY_C_F3            EQU       60h
  88. KEY_C_F4            EQU       61h
  89. KEY_C_F5            EQU       62h
  90. KEY_C_F6            EQU       63h
  91. KEY_C_F7            EQU       64h
  92. KEY_C_F8            EQU       65h
  93. KEY_C_F9            EQU       66h
  94. KEY_C_F10           EQU       67h
  95. KEY_C_LEFT          EQU       73h
  96. KEY_C_RIGHT         EQU       74h
  97. KEY_C_END           EQU       75h
  98. KEY_C_PGDN          EQU       76h
  99. KEY_C_HOME          EQU       77h
  100. KEY_C_PGUP          EQU       84h
  101. KEY_F11             EQU       85h
  102. KEY_F12             EQU       86h
  103. KEY_C_F11           EQU       89h
  104. KEY_C_F12           EQU       8ah
  105. DOS                 EQU       21h            ; main MSDOS interrupt
  106. STDIN               EQU       0              ; standard input
  107. STDOUT              EQU       1              ; standard output
  108. STDERR              EQU       2              ; error output
  109. STDAUX              EQU       3              ; COM port
  110. STDPRN              EQU       4              ; printer
  111. TSRMAGIC            EQU       424bh          ; magic number
  112. STRUC     ISR
  113.           Entry     DW        10EBh          ; short jump ahead 16 bytes
  114.           OldISR    DD        ?              ; next ISR in chain
  115.           Sig       DW        TSRMAGIC       ; magic number
  116.           EOIFlag   DB        ?              ; 0 (80) if soft(hard)ware int
  117.           Reset     DW        ?              ; short jump to hardware reset
  118.           Reserved  DB        7 dup (0)
  119. ENDS
  120. STRUC     ISRHOOK
  121.           Vector    DB        ?              ; vector hooked
  122.           Entry     DW        ?              ; offset of TSR entry point
  123. ENDS
  124. STRUC     TSRSIG
  125.           Company   DB        8 dup (" ")    ; blank-padded company name
  126.           Product   DB        8 dup (" ")    ; blank-padded product name
  127.           Desc      DB        64 dup (0)     ; ASCIIZ product description
  128. ENDS
  129. GLOBAL at : PROC
  130. GLOBAL errmsg : PROC
  131.    GLOBAL ProgName : BYTE                    ; needed for errmsg()
  132.    GLOBAL EOL : BYTE                         ; ditto
  133. GLOBAL fgetc : PROC
  134. GLOBAL fputc : PROC
  135. GLOBAL fputs : PROC
  136. GLOBAL getchar : PROC
  137. GLOBAL getdate : PROC
  138. GLOBAL getswtch : PROC
  139. GLOBAL gettime : PROC
  140. GLOBAL getvdos : PROC
  141. GLOBAL getvect : PROC
  142. GLOBAL isatty : PROC
  143. GLOBAL kbhit : PROC
  144. GLOBAL pause : PROC
  145. GLOBAL putchar : PROC
  146. GLOBAL setvect : PROC
  147. GLOBAL sleep : PROC
  148. GLOBAL find_NextISR : PROC
  149. GLOBAL find_PrevISR : PROC
  150. GLOBAL hook_ISR : PROC
  151. GLOBAL unhook_ISR : PROC
  152. GLOBAL free_Env : PROC
  153. GLOBAL fake_Env : PROC
  154. GLOBAL check_ifInstalled : PROC
  155. GLOBAL install_TSR : PROC
  156. GLOBAL remove_TSR : PROC
  157.  
  158. ;
  159. ; This section comes from Math.Inc.
  160. ;
  161. GLOBAL atoi : PROC
  162. GLOBAL atou : PROC
  163. GLOBAL utoa : PROC
  164.  
  165. ;
  166. ; This section comes from String.Inc.
  167. ;
  168. EOS                 EQU       0              ; terminates strings
  169. GLOBAL isdigit : PROC
  170. GLOBAL islower : PROC
  171. GLOBAL isupper : PROC
  172. GLOBAL iswhite : PROC
  173. GLOBAL memcmp : PROC
  174. GLOBAL strchr : PROC
  175. GLOBAL strcmp : PROC
  176. GLOBAL strlen : PROC
  177. GLOBAL tolower : PROC
  178. GLOBAL toupper : PROC
  179.  
  180.  
  181. VERSION   equ       '1.1b'                   ; current version of program
  182. ERRH      equ       255                      ; errorlevel if help given
  183.  
  184.  
  185. ;--------------------------------------------------------------------------;
  186. ;                        C O D E    S E G M E N T                          ;
  187. ;--------------------------------------------------------------------------;
  188. CODESEG
  189.  
  190. ORG       80h                                ; commandline
  191. LABEL     CmdLen    BYTE
  192.           db        ?
  193. LABEL     CmdLine   BYTE
  194.           db        127 dup (?)
  195.  
  196. ORG       100h                               ; start of .COM file
  197. STARTUPCODE
  198.           jmp       main                     ; skip over data and stack
  199.  
  200. ;--------------------------------------------------------------------------;
  201. ;                               D A T A                                    ;
  202. ;--------------------------------------------------------------------------;
  203. LABEL     ProgName  BYTE
  204.           db        'ask: ', EOS
  205. LABEL     EOL       BYTE
  206.           db        '.', CR, LF, EOS
  207. LABEL     HelpMsg   BYTE
  208.           db        CR, LF
  209.           db        'TifaWARE ASK, v', VERSION, ', ', ??Date
  210.           db        ' - ask questions in batch files.', CR, LF
  211.           db        'Usage: ask [-options] [msgtxt]', CR, LF, LF
  212.           db        'Options:', CR, LF
  213.           db        '  -l  = convert response to lower case', CR, LF
  214.           db        '  -tn = wait n seconds before timing out', CR, LF
  215.           db        '  -u  = convert response to upper case', CR, LF
  216.           db        '  -?  = display this help message', CR, LF, LF
  217.           db        'msgtxt is an optional message to display.', CR, LF, EOS
  218. LABEL     Err1Msg   BYTE
  219.           db        'illegal option -- '
  220. LABEL     OptCh     BYTE
  221.           db        ?
  222.           db        EOS
  223. LABEL     Err2Msg   BYTE
  224.           db        'time-out value not specified', EOS
  225. LABEL     Err3Msg   BYTE
  226.           db        'time-out value too large -- ', EOS
  227.  
  228. SwitCh    db        '-'                      ; char introducing options
  229. HFlag     db        0                        ; flag for on-line help
  230. LFlag     db        0                        ; flag for lowercase response
  231. TFlag     db        0                        ; flag for time-out
  232. UFlag     db        0                        ; flag for uppercase response
  233. Delay     dw        ?                        ; time to pause for key
  234. MsgLen    db        0                        ; length of message text
  235. MsgTxt    dw        ?                        ; near pointer to message text
  236. RCode     db        0                        ; program return code
  237.  
  238.  
  239. ;--------------------------------------------------------------------------;
  240. ;                           P R O C E D U R E S                            ;
  241. ;--------------------------------------------------------------------------;
  242. ;----  skip_Spaces  -------------------------------------------------------;
  243. ;  Purpose:    Skips past spaces in a string.                              ;
  244. ;  Notes:      Scanning stops with either a non-space *OR* CX = 0.         ;
  245. ;  Entry:      DS:SI = start of string to scan.                            ;
  246. ;  Exit:       AL = next non-space character,                              ;
  247. ;              CX is adjusted as necessary,                                ;
  248. ;              DS:SI = pointer to next non-space.                          ;
  249. ;  Calls:      none                                                        ;
  250. ;  Changes:    AL, CX, SI                                                  ;
  251. ;--------------------------------------------------------------------------;
  252. PROC skip_Spaces
  253.  
  254.           jcxz      SHORT @@Fin
  255. @@NextCh:
  256.           lodsb
  257.           cmp       al, ' '
  258.           loopz     @@NextCh
  259.           jz        SHORT @@Fin              ; CX = 0; don't adjust
  260.  
  261.           inc       cx                       ; adjust counters if cx > 0
  262.           dec       si
  263.  
  264. @@Fin:
  265.           ret
  266. ENDP skip_Spaces
  267.  
  268.  
  269. ;----  get_Opt  -----------------------------------------------------------;
  270. ;  Purpose:    Get a commandline option.                                   ;
  271. ;  Notes:      none                                                        ;
  272. ;  Entry:      AL = option character,                                      ;
  273. ;              CX = count of characters left in commandline,               ;
  274. ;              DS:SI = pointer to first option to process.                 ;
  275. ;  Exit:       CX = count of characters left _after_ processing,           ;
  276. ;              DS:SI = pointer to whitespace _after_ options,              ;
  277. ;  Calls:      tolower, errmsg, isdigit, atou, fputs                       ;
  278. ;  Changes:    AX, BL, CX, DX, SI,                                         ;
  279. ;              [OptCh], [HFlag], [LFlag], [TFlag], [UFlag], [Delay]        ;
  280. ;--------------------------------------------------------------------------;
  281. PROC get_Opt
  282.  
  283.           mov       [OptCh], al              ; save for later
  284.           call      tolower                  ; use only lowercase in cmp.
  285.           cmp       al, 'l'
  286.           jz        SHORT @@OptL
  287.           cmp       al, 't'
  288.           jz        SHORT @@OptT
  289.           cmp       al, 'u'
  290.           jz        SHORT @@OptU
  291.           cmp       al, '?'
  292.           jz        SHORT @@OptH
  293.           mov       dx, OFFSET Err1Msg       ; unrecognized option
  294.           call      errmsg                   ; then *** DROP THRU *** to OptH
  295.  
  296. ;
  297. ; Various possible options.
  298. ;
  299. @@OptH:
  300.           mov       [HFlag], 1               ; set help flag
  301.           jmp       SHORT @@Fin
  302.  
  303. @@OptL:
  304.           mov       [LFlag], 1               ; set lowercase flag
  305.           jmp       SHORT @@Fin
  306.  
  307. @@OptT:
  308.           mov       [TFlag], 1               ; set time-out flag
  309.           mov       al, [BYTE PTR si]        ; get next character
  310.           call      isdigit                  ; if not a digit, trouble!
  311.           jz        SHORT @@GetDelay
  312.  
  313.           mov       dx, OFFSET Err2Msg       ; no delay specified
  314.           call      errmsg
  315.           jmp       @@OptH
  316.  
  317. @@GetDelay:
  318.           mov       dx, si                   ; save to adjust CX and if error
  319.           call      atou
  320.           pushf                              ; preserve flags
  321.           add       cx, dx                   ; adjust counter
  322.           sub       cx, si
  323.           popf                               ; restore flags
  324.           jc        SHORT @@BadDelay         ; error in conversion?
  325.           cmp       ax, 60*60*12             ; 12 or more hours?
  326.           jae       SHORT @@BadDelay         ;   yes, bad delay
  327.           mov       [Delay], ax
  328.           jmp       SHORT @@Fin
  329.  
  330. @@BadDelay:
  331.           push      dx
  332.           mov       bx, STDERR
  333.           mov       dx, OFFSET ProgName
  334.           call      fputs
  335.           mov       dx, OFFSET Err3Msg
  336.           call      fputs
  337.           pop       dx
  338.           mov       al, [si]                 ; save next non-digit
  339.           mov       [BYTE PTR si], EOS       ; replace with EOS
  340.           call      fputs
  341.           mov       [si], al                 ; restore it
  342.           mov       dx, OFFSET EOL
  343.           call      fputs
  344.           jmp       SHORT @@OptH
  345.  
  346. @@OptU:
  347.           mov       [UFlag], 1               ; set uppercase flag
  348.  
  349. @@Fin:
  350.           ret
  351. ENDP get_Opt
  352.  
  353.  
  354. ;----  get_Arg  -----------------------------------------------------------;
  355. ;  Purpose:    Gets a non-option from the set of commandline arguments.    ;
  356. ;  Notes:      Anything left on the commandline is user's message text.    ;
  357. ;  Entry:      CX = count of characters left in commandline,               ;
  358. ;              DS:SI = pointer to argument to process.                     ;
  359. ;  Exit:       CX = zero                                                   ;
  360. ;              DS:SI = points to CR after commandline.                     ;
  361. ;  Calls:      none                                                        ;
  362. ;  Changes:    CX, SI                                                      ;
  363. ;              [MsgLen], [MsgTxt]                                          ;
  364. ;--------------------------------------------------------------------------;
  365. PROC get_Arg
  366.  
  367.           mov       [MsgLen], cl             ; for safekeeping
  368.           mov       [MsgTxt], si
  369.           add       si, cx                   ; adjust so nothing's left
  370.           ZERO      cl
  371.           mov       [BYTE PTR si], EOS       ; finish off string
  372.  
  373.           ret
  374. ENDP get_Arg
  375.  
  376.  
  377. ;----  process_CmdLine  ---------------------------------------------------;
  378. ;  Purpose:    Processes commandline arguments.                            ;
  379. ;  Notes:      A switch character by itself is ignored.                    ;
  380. ;              No arguments whatsoever causes help flag to be set.         ;
  381. ;  Entry:      n/a                                                         ;
  382. ;  Exit:       n/a                                                         ;
  383. ;  Calls:      skip_Spaces, get_Opt, get_Arg                               ;
  384. ;  Changes:    AX, CX, SI,                                                 ;
  385. ;              BL, DX (get_Opt),                                           ;
  386. ;              [HFlag],                                                    ;
  387. ;              [OptCh], [LFlag], [TFlag], [UFlag], [Delay] (get_Opt),      ;
  388. ;              [MsgLen], [MsgTxt] (get_Arg),                               ;
  389. ;              Direction flag is cleared.                                  ;
  390. ;--------------------------------------------------------------------------;
  391. PROC process_CmdLine
  392.  
  393.           cld                                ; forward, march!
  394.           ZERO      ch, ch
  395.           mov       cl, [CmdLen]             ; length of commandline
  396.           mov       si, OFFSET CmdLine       ; offset to start of commandline
  397.  
  398.           call      skip_Spaces              ; check if any args supplied
  399.           or        cl, cl
  400.           jnz       SHORT @@ArgLoop
  401.           mov       [HFlag], 1               ; if none, set help flag
  402.           jmp       SHORT @@Fin
  403.  
  404. ;
  405. ; For each blank-delineated argument on the commandline...
  406. ;
  407. @@ArgLoop:
  408.           lodsb                              ; next character
  409.           dec       cl
  410.           cmp       al, [SwitCh]             ; is it the switch character?
  411.           jnz       SHORT @@NonOpt           ;   no
  412.  
  413. ;
  414. ; Isolate each option and process it. Stop when a space is reached.
  415. ;
  416. @@OptLoop:
  417.           jcxz      SHORT @@Fin              ; abort if nothing left
  418.           lodsb
  419.           dec       cl
  420.           cmp       al, ' '
  421.           jz        SHORT @@NextArg          ; abort when space reached
  422.           call      get_Opt
  423.           jmp       @@OptLoop
  424.  
  425. ;
  426. ; Process the current argument, which is *not* an option.
  427. ; Then, *drop thru* to advance to next argument.
  428. ;
  429. @@NonOpt:
  430.           dec       si                       ; back up one character
  431.           inc       cl
  432.           call      get_Arg
  433.  
  434. ;
  435. ; Skip over spaces until next argument is reached.
  436. ;
  437. @@NextArg:
  438.           call      skip_Spaces
  439.           or        cl, cl
  440.           jnz       @@ArgLoop
  441.  
  442. @@Fin:
  443.           ret
  444. ENDP process_CmdLine
  445.  
  446.  
  447. ;--------------------------------------------------------------------------;
  448. ;                         E N T R Y   P O I N T                            ;
  449. ;--------------------------------------------------------------------------;
  450. ;----  main  --------------------------------------------------------------;
  451. ;  Purpose:    Main section of program.                                    ;
  452. ;  Notes:      none                                                        ;
  453. ;  Entry:      Arguments as desired                                        ;
  454. ;  Exit:       Return code as follows:                                     ;
  455. ;                   0   => program timed-out                               ;
  456. ;                   255 => on-line help requested                          ;
  457. ;                   else => ASCII value of character pressed.              ;
  458. ;  Calls:      process_CmdLine, fputs, pause, getchar, tolower, toupper    ;
  459. ;  Changes:    n/a                                                         ;
  460. ;--------------------------------------------------------------------------;
  461. main:
  462.  
  463. ;
  464. ; Process commandline arguments. If the variable HFlag is set, then
  465. ; on-line help is displayed and the program immediately terminates.
  466. ;
  467.           call      process_CmdLine          ; process commandline args
  468.  
  469.           cmp       [HFlag], 0               ; is help needed?
  470.           jz        SHORT @@NoHelp           ;   no
  471.           mov       al, ERRH                 ;   yes, so set return code
  472.           mov       bx, STDERR
  473.           mov       dx, OFFSET HelpMsg       ;     point to help message
  474.           call      fputs
  475.           jmp       SHORT @@Fin              ;     and jump to end of program
  476.  
  477. ;
  478. ; Display any message from commandline then get keypress from user.
  479. ;
  480. @@NoHelp:
  481.           mov       bx, STDOUT               ; everything to stdout
  482.           cmp       [MsgLen], 0              ; anything to print out?
  483.           jz        SHORT @@NoPrompt         ;   nope
  484.           mov       dx, [MsgTxt]
  485.           call      fputs
  486.  
  487. @@NoPrompt:
  488.           cmp       [TFlag], 0               ; need to wait?
  489.           jz        SHORT @@KeyIn            ;   no
  490.           mov       ax, [Delay]              ;   yes, so...
  491.           call      pause                    ;     pause
  492.           jz        SHORT @@KeyIn            ;     zf means a key is ready
  493.           ZERO      al                       ;     set return code to zero
  494.           jmp       SHORT @@NewLine
  495.  
  496. @@KeyIn:
  497.           call      getchar
  498.  
  499. ;
  500. ; Convert character in AL as necessary. NB: if both '-l' and '-u' options
  501. ; are specified, the return value will be based on *uppercase* value.
  502. ;
  503.           cmp       [LFlag], 0               ; convert to lowercase?
  504.           jz        SHORT @@MaybeUpper       ;   no
  505.           call      tolower
  506.  
  507. @@MaybeUpper:
  508.           cmp       [UFlag], 0               ; convert to uppercase?
  509.           jz        SHORT @@NewLine          ;   no
  510.           call      toupper
  511.  
  512. ;
  513. ; Add a final newline to keep things neat.
  514. ;
  515. @@NewLine:
  516.           mov       dx, OFFSET EOL + 1
  517.           call      fputs
  518.  
  519. ;
  520. ; Ok, let's terminate the program. Return code is already in AL.
  521. ;
  522. @@Fin:
  523.           mov       ah, 4ch
  524.           int       DOS
  525.  
  526. EVEN
  527. Buffer   db    ?                          ; space for single character
  528.                                           ; nb: shared by fgetc() & fputc()
  529.  
  530.  
  531. ;-------------------------------------------------------------------------;
  532. ;  Purpose:    Reads a character from specified device.
  533. ;  Notes:      No checks are done on BX's validity.
  534. ;              Buffer is shared by fputc(). Do *NOT* use in a 
  535. ;                 multitasking environment like DESQview.
  536. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  537. ;  Entry:      BX = device handle.
  538. ;  Exit:       AL = character,
  539. ;              Carry flag set on error (AX holds error code).
  540. ;  Calls:      none
  541. ;  Changes:    AX
  542. ;              flags
  543. ;-------------------------------------------------------------------------;
  544. PROC fgetc
  545.  
  546.    push     cx dx
  547. IF @DataSize NE 0
  548.    push     ds
  549.    mov      ax, @data
  550.    mov      ds, ax
  551. ENDIF
  552.  
  553.    mov      dx, OFFSET Buffer             ; point to storage
  554.    mov      cx, 1                         ; only need 1 char
  555.    mov      ah, 3fh
  556.    int      DOS                           ; get it
  557.    jc       SHORT @@Fin                   ; abort on error
  558.    mov      al, [Buffer]
  559.  
  560. @@Fin:
  561. IF @DataSize NE 0
  562.    pop      ds
  563. ENDIF
  564.    pop      dx cx
  565.    ret
  566.  
  567. ENDP fgetc
  568.  
  569.  
  570. ;-------------------------------------------------------------------------;
  571. ;  Purpose:    Writes a character to specified device.
  572. ;  Notes:      No checks are done on BX's validity.
  573. ;              Buffer is shared by fputc(). Do *NOT* use in a 
  574. ;                 multitasking environment like DESQview.
  575. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  576. ;  Entry:      AL = character to display,
  577. ;              BX = device handle.
  578. ;  Exit:       AL = 1 if successful,
  579. ;              Carry flag set on error (AX holds error code).
  580. ;  Calls:      none
  581. ;  Changes:    AX
  582. ;-------------------------------------------------------------------------;
  583. PROC fputc
  584.  
  585.    push     cx dx
  586. IF @DataSize NE 0
  587.    push     ds
  588.    mov      dx, @data
  589.    mov      ds, ax
  590. ENDIF
  591.  
  592.    mov      dx, OFFSET Buffer             ; point to storage
  593.    mov      [Buffer], al                  ; save char
  594.    mov      cx, 1                         ; only write 1 char
  595.    mov      ah, 40h
  596.    int      DOS
  597.  
  598. IF @DataSize NE 0
  599.    pop      ds
  600. ENDIF
  601.    pop      dx cx
  602.    ret
  603.  
  604. ENDP fputc
  605.  
  606.  
  607. ;-------------------------------------------------------------------------;
  608. ;  Purpose:    Reads a character from STDIN.
  609. ;  Notes:      Character is echoed to display.
  610. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  611. ;  Entry:      n/a
  612. ;  Exit:       AL = character.
  613. ;  Calls:      none
  614. ;  Changes:    AX
  615. ;-------------------------------------------------------------------------;
  616. PROC getchar
  617.  
  618.    mov      ah, 1
  619.    int      DOS
  620.    ret
  621.  
  622. ENDP getchar
  623.  
  624.  
  625. ;-------------------------------------------------------------------------;
  626. ;  Purpose:    Writes a character to STDOUT device.
  627. ;  Notes:      none
  628. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  629. ;  Entry:      DL = character to display.
  630. ;  Exit:       n/a
  631. ;  Calls:      none
  632. ;  Changes:    none
  633. ;-------------------------------------------------------------------------;
  634. PROC putchar
  635.  
  636.    push     ax
  637.    mov      ah, 2
  638.    int      DOS
  639.    pop      ax
  640.    ret
  641.  
  642. ENDP putchar
  643.  
  644.  
  645. ;-------------------------------------------------------------------------;
  646. ;  Purpose:    Checks if a character is ready for input from STDIN.
  647. ;  Notes:      none
  648. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  649. ;  Entry:      n/a
  650. ;  Exit:       zf = 1 if character available.
  651. ;  Calls:      none
  652. ;  Changes:    flags
  653. ;-------------------------------------------------------------------------;
  654. PROC kbhit
  655.  
  656.    push     ax
  657.    mov      ah, 0bh
  658.    int      DOS
  659.    cmp      al, 0ffh                      ; AL = FFh if character ready
  660.    pop      ax
  661.    ret
  662.  
  663. ENDP kbhit
  664.  
  665.  
  666. EVEN
  667. ;-------------------------------------------------------------------------;
  668. ;  Purpose:    Writes an ASCIIZ string to specified device.
  669. ;  Notes:      A zero-length string doesn't seem to cause problems when
  670. ;                 this output function is used.
  671. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  672. ;  Entry:      BX = device handle,
  673. ;              DS:DX = pointer to string.
  674. ;  Exit:       Carry flag set if EOS wasn't found or handle is invalid.
  675. ;  Calls:      strlen
  676. ;  Changes:    none
  677. ;-------------------------------------------------------------------------;
  678. PROC fputs
  679.  
  680.    push     ax cx di es
  681.    mov      ax, ds
  682.    mov      es, ax
  683.    mov      di, dx
  684.    call     strlen                        ; set CX = length of string
  685.    jc       SHORT @@Fin                   ; abort if problem finding end
  686.    mov      ah, 40h                       ; MS-DOS raw output function
  687.    int      DOS
  688. @@Fin:
  689.    pop      es di cx ax
  690.    ret
  691.  
  692. ENDP fputs
  693.  
  694.  
  695. EVEN
  696. ;-------------------------------------------------------------------------;
  697. ;  Purpose:    Writes an error message to stderr.
  698. ;  Notes:      none
  699. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  700. ;  Entry:      DS:DX = pointer to error message.
  701. ;  Exit:       n/a
  702. ;  Calls:      fputs
  703. ;  Changes:    none
  704. ;-------------------------------------------------------------------------;
  705. PROC errmsg
  706.  
  707.    push     bx dx
  708.    mov      bx, STDERR
  709.    mov      dx, OFFSET ProgName           ; display program name
  710.    call     fputs
  711.    pop      dx                            ; recover calling parameters
  712.    push     dx                            ; and save again to avoid change
  713.    call     fputs                         ; display error message
  714.    mov      dx, OFFSET EOL
  715.    call     fputs
  716.    pop      dx bx
  717.    ret
  718.  
  719. ENDP errmsg
  720.  
  721.  
  722. EVEN
  723. ;-------------------------------------------------------------------------;
  724. ;  Purpose:    Gets current system date, based on DOS's internal clock.
  725. ;  Notes:      none
  726. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  727. ;  Entry:      n/a
  728. ;  Exit:       AL = day of week (0 = Sunday)
  729. ;              DL = day (1 to 31)
  730. ;              DH = month (1 to 12)
  731. ;              CX = year (1980 to 2099)
  732. ;  Calls:      none
  733. ;  Changes:    AX, CX, DX
  734. ;-------------------------------------------------------------------------;
  735. PROC getdate
  736.  
  737.    mov      ah, 2ah                       ; MS-DOS get system date function
  738.    int      DOS
  739.    ret
  740.  
  741. ENDP getdate
  742.  
  743.  
  744. ;-------------------------------------------------------------------------;
  745. ;  Purpose:    Gets current system time, based on DOS's internal clock.
  746. ;  Notes:      none
  747. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  748. ;  Entry:      n/a
  749. ;  Exit:       CL = minutes (0 - 59)
  750. ;              CH = hour (0 - 23)
  751. ;              DL = hundredths of seconds (0 - 99)
  752. ;              DH = seconds (0 - 59)
  753. ;  Calls:      none
  754. ;  Changes:    CX, DX
  755. ;-------------------------------------------------------------------------;
  756. PROC gettime
  757.  
  758.    push     ax
  759.    mov      ah, 2ch                       ; MS-DOS get system time function
  760.    int      DOS
  761.    pop      ax
  762.    ret
  763.  
  764. ENDP gettime
  765.  
  766.  
  767. EVEN
  768. ;-------------------------------------------------------------------------;
  769. ;  Purpose:    Pauses execution until a specified time.
  770. ;  Notes:      If time is 12 or more hours in advance, execution aborts.
  771. ;              Range for hours is 0-23, for minutes is 0-59, etc...
  772. ;              This is as accurate as the DOS clock.
  773. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  774. ;  Entry:      AX:BX = time of day (hh.mm.ss.hs) at which to awaken.
  775. ;  Exit:       n/a
  776. ;  Calls:      gettime
  777. ;  Changes:    flags
  778. ;-------------------------------------------------------------------------;
  779. PROC at
  780.  
  781.    push     cx dx
  782.  
  783. @@TimeLoop:
  784.    call     gettime                       ; get current time
  785.    sub      cx, ax                        ; cx = current time - alarm time
  786.    jz       SHORT @@CheckSecs             ; if zero, check seconds
  787.    cmp      ah, 12                        ; adjust if alarm is a.m. ...
  788.    jae      SHORT @@TooFar?
  789.    cmp      ch, 0                         ;   and current time is p.m. ...
  790.    jl       SHORT @@TooFar?               ;      (signed comparison!!!)
  791.    sub      ch, 24                        ;   by subtracting 24 hours
  792.  
  793. @@TooFar?:
  794.    neg      cx                            ; cx = alarm time - current time
  795.    cmp      cx, 12 * 256                  ; more than 12 hours difference?
  796.    ja       SHORT @@Fin                   ;   yep (unsigned comparison!!!)
  797.    jmp      @@TimeLoop                    ;   nope (already checked if ==)
  798.  
  799. @@CheckSecs:
  800.    cmp      dx, bx                        ; wait a few more seconds?
  801.    jb       @@TimeLoop                    ;   yep
  802.    or       dl, 1                         ;   no, clear zf
  803.  
  804. @@Fin:
  805.    pop      dx cx
  806.    ret
  807.  
  808. ENDP at
  809.  
  810.  
  811. ;-------------------------------------------------------------------------;
  812. ;  Purpose:    Pauses execution for a specified number of seconds or 
  813. ;                 until a keypress is detected.
  814. ;  Notes:      Delay should be less than 12 hours (43200 seconds) to 
  815. ;                 avoid checks on date rollover yet ensure we haven't
  816. ;                 paused too long. Delay is not checked however!
  817. ;              This procedure works by adding the delay to the current
  818. ;                 time and waiting until then. If the system clock is
  819. ;                 adjusted in meantime, results are unpredictable. I
  820. ;                 tried looping and calling gettime(), but that was
  821. ;                 inaccurate due to roundoff of hundreths of secs.
  822. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  823. ;  Entry:      AX = delay time (in seconds).
  824. ;  Exit:       Zero flag set if input ready; cleared otherwise.
  825. ;  Calls:      gettime, kbhit
  826. ;  Changes:    flags
  827. ;-------------------------------------------------------------------------;
  828. PROC pause
  829.  
  830.    push     ax bx cx dx
  831.    ZERO     bx
  832.    mov      cx, 60                        ; 60 secs/min and 60 mins/hour
  833.    ZERO     dx
  834.    div      cx                            ; now ax = hours and minutes
  835.    mov      bh, dl                        ; and bh = seconds (dh == 0)
  836.    ZERO     dx
  837.    div      cx                            ; now ax = hours, dx = minutes
  838.    mov      ah, al                        ; hours
  839.    mov      al, dl                        ; minutes
  840.  
  841. ;
  842. ; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
  843. ;
  844.    call     gettime                       ; get current time
  845.    add      ax, cx                        ; compute alarm time
  846.    add      bx, dx
  847.    cmp      bh, 60                        ; too many seconds?
  848.    jb       SHORT @@CheckMins             ;   nope
  849.    sub      bh, 60                        ;   yep, adjust
  850.    inc      al
  851.  
  852. @@CheckMins:
  853.    cmp      al, 60                        ; too many minutes?
  854.    jb       SHORT @@CheckHours            ;   nope
  855.    sub      al, 60                        ;   yep, adjust
  856.    inc      ah
  857.  
  858. @@CheckHours:
  859.    cmp      ah, 24                        ; too many hours?
  860.    jb       SHORT @@TimeLoop              ;   nope
  861.    sub      ah, 24                        ;   yep, adjust
  862.  
  863. ;
  864. ; Here's the main loop. Check for both keypress and alarm time.
  865. ; NB: Because of overhead in the code it's possible to overshoot
  866. ; the alarm time by a few hundreths of a second so check for that.
  867. ;
  868. @@TimeLoop:
  869.    call     kbhit                         ; check for user input
  870.    jz       SHORT @@Fin                   ; and abort if present
  871.    call     gettime                       ; get current time
  872.    sub      cx, ax                        ; cx = current time - alarm time
  873.    jz       SHORT @@CheckSecs             ; if zero, check seconds
  874.    cmp      ah, 12                        ; adjust if alarm is a.m. ...
  875.    jae      SHORT @@TooFar?
  876.    cmp      ch, 0                         ;   and current time is p.m. ...
  877.    jl       SHORT @@TooFar?               ;      (signed comparison!!!)
  878.    sub      ch, 24                        ;   by subtracting 24 hours
  879.  
  880. @@TooFar?:
  881.    neg      cx                            ; cx = alarm time - current time
  882.    cmp      cx, 12 * 256                  ; more than 12 hours difference?
  883.    ja       SHORT @@Fin                   ;   yep (unsigned comparison!!!)
  884.    jmp      @@TimeLoop                    ;   nope (already checked if ==)
  885.  
  886. @@CheckSecs:
  887.    cmp      dx, bx                        ; wait a few more seconds?
  888.    jb       @@TimeLoop                    ;   yep
  889.    or       dl, 1                         ;   no, clear zf
  890.  
  891. @@Fin:
  892.    pop      dx cx bx ax
  893.    ret
  894.  
  895. ENDP pause
  896.  
  897.  
  898. ;-------------------------------------------------------------------------;
  899. ;  Purpose:    Pauses execution for a specified number of seconds.
  900. ;  Notes:      Delay should be less than 12 hours (43200 seconds) due
  901. ;                 to the way at() is implemented. This is not checked!
  902. ;              This procedure works by adding the delay to the current
  903. ;                 time and waiting until then. If the system clock is
  904. ;                 adjusted in meantime, results are unpredictable. I
  905. ;                 tried looping and calling gettime(), but that was
  906. ;                 inaccurate due to roundoff of hundreths of secs.
  907. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  908. ;  Entry:      AX = delay time (in seconds).
  909. ;  Exit:       n/a
  910. ;  Calls:      gettime, at
  911. ;  Changes:    none
  912. ;-------------------------------------------------------------------------;
  913. PROC sleep
  914.  
  915.    push     ax bx cx dx
  916.    ZERO     bx
  917.    mov      cx, 60                        ; 60 secs/min and 60 mins/hour
  918.    ZERO     dx
  919.    div      cx                            ; now ax = hours and minutes
  920.    mov      bh, dl                        ; and bh = seconds (dh == 0)
  921.    ZERO     dx
  922.    div      cx                            ; now ax = hours, dx = minutes
  923.    mov      ah, al                        ; hours
  924.    mov      al, dl                        ; minutes
  925.  
  926. ;
  927. ; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
  928. ;
  929.    call     gettime                       ; get current time
  930.    add      ax, cx                        ; compute alarm time
  931.    add      bx, dx
  932.    cmp      bh, 60                        ; too many seconds?
  933.    jb       SHORT @@CheckMins             ;   nope
  934.    sub      bh, 60                        ;   yep, adjust
  935.    inc      al
  936.  
  937. @@CheckMins:
  938.    cmp      al, 60                        ; too many minutes?
  939.    jb       SHORT @@CheckHours            ;   nope
  940.    sub      al, 60                        ;   yep, adjust
  941.    inc      ah
  942.  
  943. @@CheckHours:
  944.    cmp      ah, 24                        ; too many hours?
  945.    jb       SHORT @@Fin                   ;   nope
  946.    sub      ah, 24                        ;   yep, adjust
  947.  
  948. @@Fin:
  949.    call     at                            ; pause until alarm time
  950.    pop      dx cx bx ax
  951.    ret
  952.  
  953. ENDP sleep
  954.  
  955.  
  956. EVEN
  957. ;-------------------------------------------------------------------------;
  958. ;  Purpose:    Converts string of digits to an *unsigned* integer in
  959. ;              range [0, 65535].
  960. ;  Notes:      Conversion stops with first non-numeric character.
  961. ;  Requires:   8086-class CPU.
  962. ;  Entry:      DS:SI = pointer to string of digits.
  963. ;  Exit:       AX = unsigned integer (garbage if cf = 1),
  964. ;              DS:SI = pointer to first non-digit found,
  965. ;              cf = 1 if number is too big.
  966. ;  Calls:      none
  967. ;  Changes:    AX, SI
  968. ;              flags
  969. ;-------------------------------------------------------------------------;
  970. PROC atou
  971.  
  972.    push     bx cx dx                      ; DX destroyed by MUL below
  973.    ZERO     ax                            ; AX = digit to convert
  974.    ZERO     bx                            ; BX = integer word
  975.    mov      cx, 10                        ; CX = conversion factor
  976.  
  977. @@NextCh:
  978.    mov      bl, [si]                      ; get character
  979.    cmp      bl, '0'                       ; test if a digit
  980.    jb       SHORT @@Fin
  981.    cmp      bl, '9'
  982.    ja       SHORT @@Fin
  983.    inc      si                            ; bump up pointer
  984.    mul      cx                            ; multiply old result by 10
  985.    jc       SHORT @@Overflow
  986.    sub      bl, '0'                       ; convert digit
  987.    add      ax, bx                        ; add current value
  988.    jnc      @@NextCh                      ; continue unless result too big
  989.  
  990. @@Overflow:
  991.    ZERO     cx                            ; denotes overflow
  992.    jmp      @@NextCh
  993.  
  994. @@Fin:
  995.    cmp      cx, 10                        ; cf = (cx != 10)
  996.    pop      dx cx bx
  997.    ret
  998.  
  999. ENDP atou
  1000.  
  1001.  
  1002. EVEN
  1003. ;-------------------------------------------------------------------------;
  1004. ;  Purpose:    Tests if character is a valid ASCII digit.
  1005. ;  Notes:      none
  1006. ;  Requires:   8086-class CPU.
  1007. ;  Entry:      AL = character to be tested.
  1008. ;  Exit:       Zero flag set if true, cleared otherwise.
  1009. ;  Calls:      none 
  1010. ;  Changes:    flags
  1011. ;-------------------------------------------------------------------------;
  1012. PROC isdigit
  1013.  
  1014.    cmp      al, '0'                       ; if < '0' zf = 0
  1015.    jb       SHORT @@Fin
  1016.    cmp      al, '9'                       ; if > '9' zf = 0
  1017.    ja       SHORT @@Fin
  1018.    cmp      al, al                        ; set Z flag
  1019. @@Fin:
  1020.    ret
  1021.  
  1022. ENDP isdigit
  1023.  
  1024.  
  1025. ;-------------------------------------------------------------------------;
  1026. ;  Purpose:    Tests if character is lowercase.
  1027. ;  Notes:      none
  1028. ;  Requires:   8086-class CPU.
  1029. ;  Entry:      AL = character to be tested.
  1030. ;  Exit:       Zero flag set if true, cleared otherwise.
  1031. ;  Calls:      none 
  1032. ;  Changes:    flags
  1033. ;-------------------------------------------------------------------------;
  1034. PROC islower
  1035.  
  1036.    cmp      al, 'a'                       ; if < 'a' zf = 0
  1037.    jb       SHORT @@Fin
  1038.    cmp      al, 'z'                       ; if > 'z' zf = 0
  1039.    ja       SHORT @@Fin
  1040.    cmp      al, al                        ; set Z flag
  1041. @@Fin:
  1042.    ret
  1043.  
  1044. ENDP islower
  1045.  
  1046.  
  1047. ;-------------------------------------------------------------------------;
  1048. ;  Purpose:    Tests if character is uppercase.
  1049. ;  Notes:      none
  1050. ;  Requires:   8086-class CPU.
  1051. ;  Entry:      AL = character to be tested.
  1052. ;  Exit:       Zero flag set if true, cleared otherwise.
  1053. ;  Calls:      none 
  1054. ;  Changes:    flags
  1055. ;-------------------------------------------------------------------------;
  1056. PROC isupper
  1057.  
  1058.    cmp      al, 'A'                       ; if < 'A' zf = 0
  1059.    jb       SHORT @@Fin
  1060.    cmp      al, 'Z'                       ; if > 'Z' zf = 0
  1061.    ja       SHORT @@Fin
  1062.    cmp      al, al                        ; set Z flag
  1063. @@Fin:
  1064.    ret
  1065.  
  1066. ENDP isupper
  1067.  
  1068.  
  1069. ;-------------------------------------------------------------------------;
  1070. ;  Purpose:    Tests if character is an ASCII whitespace.
  1071. ;  Notes:      none
  1072. ;  Requires:   8086-class CPU.
  1073. ;  Entry:      AL = character to be tested.
  1074. ;  Exit:       Zero flag set if true, cleared otherwise.
  1075. ;  Calls:      none 
  1076. ;  Changes:    flags
  1077. ;-------------------------------------------------------------------------;
  1078. PROC iswhite
  1079.  
  1080.    cmp      al, SPACE                     ; if == SPACE then zf = 1
  1081.    jz       SHORT @@Fin
  1082.    cmp      al, TAB                       ; if == TAB then zf = 1
  1083.    jz       SHORT @@Fin
  1084.    cmp      al, LF                        ; if == LF then zf = 1
  1085.    jz       SHORT @@Fin
  1086.    cmp      al, CR                        ; if == CR then zf = 1
  1087. @@Fin:
  1088.    ret
  1089.  
  1090. ENDP iswhite
  1091.  
  1092.  
  1093. EVEN
  1094. ;-------------------------------------------------------------------------;
  1095. ;  Purpose:    Converts character to lowercase.
  1096. ;  Notes:      none
  1097. ;  Requires:   8086-class CPU.
  1098. ;  Entry:      AL = character to be converted.
  1099. ;  Exit:       AL = converted character.
  1100. ;  Calls:      none
  1101. ;  Changes:    AL
  1102. ;              flags
  1103. ;-------------------------------------------------------------------------;
  1104. PROC tolower
  1105.  
  1106.    cmp      al, 'A'                       ; if < 'A' then done
  1107.    jb       SHORT @@Fin
  1108.    cmp      al, 'Z'                       ; if > 'Z' then done
  1109.    ja       SHORT @@Fin
  1110.    or       al, 20h                       ; make it lowercase
  1111. @@Fin:
  1112.    ret
  1113.  
  1114. ENDP tolower
  1115.  
  1116.  
  1117. ;-------------------------------------------------------------------------;
  1118. ;  Purpose:    Converts character to uppercase.
  1119. ;  Notes:      none
  1120. ;  Requires:   8086-class CPU.
  1121. ;  Entry:      AL = character to be converted.
  1122. ;  Exit:       AL = converted character.
  1123. ;  Calls:      none
  1124. ;  Changes:    AL
  1125. ;              flags
  1126. ;-------------------------------------------------------------------------;
  1127. PROC toupper
  1128.  
  1129.    cmp      al, 'a'                       ; if < 'a' then done
  1130.    jb       SHORT @@Fin
  1131.    cmp      al, 'z'                       ; if > 'z' then done
  1132.    ja       SHORT @@Fin
  1133.    and      al, not 20h                   ; make it lowercase
  1134. @@Fin:
  1135.    ret
  1136.  
  1137. ENDP toupper
  1138.  
  1139.  
  1140. EVEN
  1141. ;-------------------------------------------------------------------------;
  1142. ;  Purpose:    Calculates length of an ASCIIZ string.
  1143. ;  Notes:      Terminal char is _not_ included in the count.
  1144. ;  Requires:   8086-class CPU.
  1145. ;  Entry:      ES:DI = pointer to string.
  1146. ;  Exit:       CX = length of string,
  1147. ;              cf = 0 and zf = 1 if EOS found,
  1148. ;              cf = 1 and zf = 0 if EOS not found within segment.
  1149. ;  Calls:      none
  1150. ;  Changes:    CX,
  1151. ;              flags
  1152. ;-------------------------------------------------------------------------;
  1153. PROC strlen
  1154.  
  1155.    push     ax di
  1156.    pushf
  1157.    cld                                    ; scan forward only
  1158.    mov      al, EOS                       ; character to search for
  1159.    mov      cx, di                        ; where are we now
  1160.    not      cx                            ; what's left in segment - 1
  1161.    push     cx                            ; save char count
  1162.    repne    scasb
  1163.    je       SHORT @@Done
  1164.    scasb                                  ; test final char
  1165.    dec      cx                            ; avoids trouble with "not" below
  1166.  
  1167. @@Done:
  1168.    pop      ax                            ; get original count
  1169.    sub      cx, ax                        ; subtract current count
  1170.    not      cx                            ; and invert it
  1171.    popf                                   ; restore df
  1172.    dec      di
  1173.    cmp      [BYTE PTR es:di], EOS
  1174.    je       SHORT @@Fin                   ; cf = 0 if equal
  1175.    stc                                    ; set cf => error
  1176.  
  1177. @@Fin:
  1178.    pop      di ax
  1179.    ret
  1180.  
  1181. ENDP strlen
  1182.  
  1183.  
  1184. END
  1185.