Category : Batch File Utilities - mostly for DOS
Archive   : ASK11C.ZIP
Filename : ASK.ASM
Output of file : ASK.ASM contained in archive : ASK11C.ZIP
; Program: Ask .Asm ;
; Purpose: Ask question in batch file and time out if desired. ;
; Notes: Compiles under TURBO Assembler, v3.0. Should work on any ;
; machine running MS-DOS, v2.xx or higher. ;
; Status: Released into the public domain. Enjoy! If you use it, ;
; let me know what you think. You don't have to send ;
; any money, just comments and suggestions. ;
; Updates: 23-Apr-90, v1.0, GAT ;
; - initial version. ;
; 13-May-90, GAT ;
; - fixed problem with return code if time-out reached. ;
; 08-Jul-90, GAT ;
; - added macros to push/pop registers. ;
; 28-Aug-90, v1.1a, GAT ;
; - put equates and macros in separate files. ;
; - put common routines in libs. ;
; 12-Oct-91, v1.1b, GAT ;
; - revised include file names. ;
; - replaced references to Push_M and Pop_M macros with ;
; calls to push and pop. ;
; - removed local stack: it's not necessary. ;
; 03-Jul-93, v1.1c, GAT ;
; - compiled with TASM v3.0. ;
; - version number now comes from makefile. ;
; - specified ??date in lowercase. ;
;--------------------------------------------------------------------------;
;--------------------------------------------------------------------------;
; Author: George A. Theall ;
; SnailMail: TifaWARE ;
; 610 South 48th St ;
; Philadelphia, PA. 19143 ;
; U.S.A. ;
; E-Mail: [email protected] ;
; [email protected] ;
; [email protected] ;
; [email protected] ;
;--------------------------------------------------------------------------;
;--------------------------------------------------------------------------;
; D I R E C T I V E S ;
;--------------------------------------------------------------------------;
DOSSEG
MODEL tiny
IDEAL
LOCALS
JUMPS
FALSE EQU 0
TRUE EQU NOT FALSE
BELL EQU 7
BS EQU 8
TAB EQU 9
CR EQU 13
LF EQU 10
ESCAPE EQU 27 ; nb: ESC is a TASM keyword
SPACE EQU ' '
KEY_F1 EQU 3bh
KEY_F2 EQU 3ch
KEY_F3 EQU 3dh
KEY_F4 EQU 3eh
KEY_F5 EQU 3fh
KEY_F6 EQU 40h
KEY_F7 EQU 41h
KEY_F8 EQU 42h
KEY_F9 EQU 43h
KEY_F10 EQU 44h
KEY_HOME EQU 47h
KEY_UP EQU 48h
KEY_PGUP EQU 49h
KEY_LEFT EQU 4bh
KEY_RIGHT EQU 4dh
KEY_END EQU 4fh
KEY_DOWN EQU 50h
KEY_PGDN EQU 51h
KEY_INS EQU 52h
KEY_DEL EQU 53h
KEY_C_F1 EQU 5eh
KEY_C_F2 EQU 5fh
KEY_C_F3 EQU 60h
KEY_C_F4 EQU 61h
KEY_C_F5 EQU 62h
KEY_C_F6 EQU 63h
KEY_C_F7 EQU 64h
KEY_C_F8 EQU 65h
KEY_C_F9 EQU 66h
KEY_C_F10 EQU 67h
KEY_C_LEFT EQU 73h
KEY_C_RIGHT EQU 74h
KEY_C_END EQU 75h
KEY_C_PGDN EQU 76h
KEY_C_HOME EQU 77h
KEY_C_PGUP EQU 84h
KEY_F11 EQU 85h
KEY_F12 EQU 86h
KEY_C_F11 EQU 89h
KEY_C_F12 EQU 8ah
@16BIT EQU (@Cpu AND 8) EQ 0
@32BIT EQU (@Cpu AND 8)
NOWARN RES
MACRO PUSHA ;; Pushs all registers
IF @Cpu AND 2 ;; if for 80186 or better
pusha ;; use regular opcode
ELSE ;; else
push ax cx dx bx sp bp si di ;; nb: order matters!
;; nb: SP is not original!
ENDIF
ENDM
MACRO POPA ;; Pops all registers
IF @Cpu AND 2 ;; if for 80186 or better
popa ;; use regular opcode
ELSE ;; else
pop di si bp bx bx dx cx ax ;; nb: order matters!
;; nb: don't pop SP!
ENDIF
ENDM
NOWARN RES
MACRO ZERO RegList ;; Zeros registers
IRP Reg,
xor Reg, Reg
ENDM
ENDM
DOS EQU 21h ; main MSDOS interrupt
STDIN EQU 0 ; standard input
STDOUT EQU 1 ; standard output
STDERR EQU 2 ; error output
STDAUX EQU 3 ; COM port
STDPRN EQU 4 ; printer
TSRMAGIC EQU 424bh ; magic number
STRUC ISR
Entry DW 10EBh ; short jump ahead 16 bytes
OldISR DD ? ; next ISR in chain
Sig DW TSRMAGIC ; magic number
EOIFlag DB ? ; 0 (80) if soft(hard)ware int
Reset DW ? ; short jump to hardware reset
Reserved DB 7 dup (0)
ENDS
STRUC ISRHOOK
Vector DB ? ; vector hooked
Entry DW ? ; offset of TSR entry point
ENDS
STRUC TSRSIG
Company DB 8 dup (" ") ; blank-padded company name
Product DB 8 dup (" ") ; blank-padded product name
Desc DB 64 dup (0) ; ASCIIZ product description
ENDS
GLOBAL at : PROC
GLOBAL errmsg : PROC
GLOBAL ProgName : BYTE ; needed for errmsg()
GLOBAL EOL : BYTE ; ditto
GLOBAL fgetc : PROC
GLOBAL fputc : PROC
GLOBAL fputs : PROC
GLOBAL getchar : PROC
GLOBAL getdate : PROC
GLOBAL getswtch : PROC
GLOBAL gettime : PROC
GLOBAL getvdos : PROC
GLOBAL getvect : PROC
GLOBAL isatty : PROC
GLOBAL kbhit : PROC
GLOBAL pause : PROC
GLOBAL putchar : PROC
GLOBAL setvect : PROC
GLOBAL sleep : PROC
GLOBAL find_NextISR : PROC
GLOBAL find_PrevISR : PROC
GLOBAL hook_ISR : PROC
GLOBAL unhook_ISR : PROC
GLOBAL free_Env : PROC
GLOBAL fake_Env : PROC
GLOBAL check_ifInstalled : PROC
GLOBAL install_TSR : PROC
GLOBAL remove_TSR : PROC
GLOBAL atoi : PROC
GLOBAL atou : PROC
GLOBAL utoa : PROC
EOS EQU 0 ; terminates strings
GLOBAL isalpha : PROC
GLOBAL isdigit : PROC
GLOBAL islower : PROC
GLOBAL isupper : PROC
GLOBAL iswhite : PROC
GLOBAL memcmp : PROC
GLOBAL strchr : PROC
GLOBAL strcmp : PROC
GLOBAL strlen : PROC
GLOBAL tolower : PROC
GLOBAL toupper : PROC
ERRH equ 255 ; errorlevel if help given
;--------------------------------------------------------------------------;
; C O D E S E G M E N T ;
;--------------------------------------------------------------------------;
CODESEG
ORG 80h ; commandline
LABEL CmdLen BYTE
db ?
LABEL CmdLine BYTE
db 127 dup (?)
ORG 100h ; start of .COM file
STARTUPCODE
jmp main ; skip over data and stack
;--------------------------------------------------------------------------;
; D A T A ;
;--------------------------------------------------------------------------;
LABEL ProgName BYTE
db 'ask: ', EOS
LABEL EOL BYTE
db '.', CR, LF, EOS
LABEL HelpMsg BYTE
db CR, LF
db 'TifaWARE ASK, v', VERS_STR, ', ', ??date
db ' - ask questions in batch files.', CR, LF
db 'Usage: ask [-options] [msgtxt]', CR, LF, LF
db 'Options:', CR, LF
db ' -l = convert response to lower case', CR, LF
db ' -tn = wait n seconds before timing out', CR, LF
db ' -u = convert response to upper case', CR, LF
db ' -? = display this help message', CR, LF, LF
db 'msgtxt is an optional message to display.', CR, LF, EOS
LABEL Err1Msg BYTE
db 'illegal option -- '
LABEL OptCh BYTE
db ?
db EOS
LABEL Err2Msg BYTE
db 'time-out value not specified', EOS
LABEL Err3Msg BYTE
db 'time-out value too large -- ', EOS
SwitCh db '-' ; char introducing options
HFlag db 0 ; flag for on-line help
LFlag db 0 ; flag for lowercase response
TFlag db 0 ; flag for time-out
UFlag db 0 ; flag for uppercase response
Delay dw ? ; time to pause for key
MsgLen db 0 ; length of message text
MsgTxt dw ? ; near pointer to message text
RCode db 0 ; program return code
;--------------------------------------------------------------------------;
; P R O C E D U R E S ;
;--------------------------------------------------------------------------;
;---- skip_Spaces -------------------------------------------------------;
; Purpose: Skips past spaces in a string. ;
; Notes: Scanning stops with either a non-space *OR* CX = 0. ;
; Entry: DS:SI = start of string to scan. ;
; Exit: AL = next non-space character, ;
; CX is adjusted as necessary, ;
; DS:SI = pointer to next non-space. ;
; Calls: none ;
; Changes: AL, CX, SI ;
;--------------------------------------------------------------------------;
PROC skip_Spaces
jcxz SHORT @@Fin
@@NextCh:
lodsb
cmp al, ' '
loopz @@NextCh
jz SHORT @@Fin ; CX = 0; don't adjust
inc cx ; adjust counters if cx > 0
dec si
@@Fin:
ret
ENDP skip_Spaces
;---- get_Opt -----------------------------------------------------------;
; Purpose: Get a commandline option. ;
; Notes: none ;
; Entry: AL = option character, ;
; CX = count of characters left in commandline, ;
; DS:SI = pointer to first option to process. ;
; Exit: CX = count of characters left _after_ processing, ;
; DS:SI = pointer to whitespace _after_ options, ;
; Calls: tolower, errmsg, isdigit, atou, fputs ;
; Changes: AX, BL, CX, DX, SI, ;
; [OptCh], [HFlag], [LFlag], [TFlag], [UFlag], [Delay] ;
;--------------------------------------------------------------------------;
PROC get_Opt
mov [OptCh], al ; save for later
call tolower ; use only lowercase in cmp.
cmp al, 'l'
jz SHORT @@OptL
cmp al, 't'
jz SHORT @@OptT
cmp al, 'u'
jz SHORT @@OptU
cmp al, '?'
jz SHORT @@OptH
mov dx, OFFSET Err1Msg ; unrecognized option
call errmsg ; then *** DROP THRU *** to OptH
;
; Various possible options.
;
@@OptH:
mov [HFlag], 1 ; set help flag
jmp SHORT @@Fin
@@OptL:
mov [LFlag], 1 ; set lowercase flag
jmp SHORT @@Fin
@@OptT:
mov [TFlag], 1 ; set time-out flag
mov al, [BYTE PTR si] ; get next character
call isdigit ; if not a digit, trouble!
jz SHORT @@GetDelay
mov dx, OFFSET Err2Msg ; no delay specified
call errmsg
jmp @@OptH
@@GetDelay:
mov dx, si ; save to adjust CX and if error
call atou
pushf ; preserve flags
add cx, dx ; adjust counter
sub cx, si
popf ; restore flags
jc SHORT @@BadDelay ; error in conversion?
cmp ax, 60*60*12 ; 12 or more hours?
jae SHORT @@BadDelay ; yes, bad delay
mov [Delay], ax
jmp SHORT @@Fin
@@BadDelay:
push dx
mov bx, STDERR
mov dx, OFFSET ProgName
call fputs
mov dx, OFFSET Err3Msg
call fputs
pop dx
mov al, [si] ; save next non-digit
mov [BYTE PTR si], EOS ; replace with EOS
call fputs
mov [si], al ; restore it
mov dx, OFFSET EOL
call fputs
jmp SHORT @@OptH
@@OptU:
mov [UFlag], 1 ; set uppercase flag
@@Fin:
ret
ENDP get_Opt
;---- get_Arg -----------------------------------------------------------;
; Purpose: Gets a non-option from the set of commandline arguments. ;
; Notes: Anything left on the commandline is user's message text. ;
; Entry: CX = count of characters left in commandline, ;
; DS:SI = pointer to argument to process. ;
; Exit: CX = zero ;
; DS:SI = points to CR after commandline. ;
; Calls: none ;
; Changes: CX, SI ;
; [MsgLen], [MsgTxt] ;
;--------------------------------------------------------------------------;
PROC get_Arg
mov [MsgLen], cl ; for safekeeping
mov [MsgTxt], si
add si, cx ; adjust so nothing's left
ZERO cl
mov [BYTE PTR si], EOS ; finish off string
ret
ENDP get_Arg
;---- process_CmdLine ---------------------------------------------------;
; Purpose: Processes commandline arguments. ;
; Notes: A switch character by itself is ignored. ;
; No arguments whatsoever causes help flag to be set. ;
; Entry: n/a ;
; Exit: n/a ;
; Calls: skip_Spaces, get_Opt, get_Arg ;
; Changes: AX, CX, SI, ;
; BL, DX (get_Opt), ;
; [HFlag], ;
; [OptCh], [LFlag], [TFlag], [UFlag], [Delay] (get_Opt), ;
; [MsgLen], [MsgTxt] (get_Arg), ;
; Direction flag is cleared. ;
;--------------------------------------------------------------------------;
PROC process_CmdLine
cld ; forward, march!
ZERO ch, ch
mov cl, [CmdLen] ; length of commandline
mov si, OFFSET CmdLine ; offset to start of commandline
call skip_Spaces ; check if any args supplied
or cl, cl
jnz SHORT @@ArgLoop
mov [HFlag], 1 ; if none, set help flag
jmp SHORT @@Fin
;
; For each blank-delineated argument on the commandline...
;
@@ArgLoop:
lodsb ; next character
dec cl
cmp al, [SwitCh] ; is it the switch character?
jnz SHORT @@NonOpt ; no
;
; Isolate each option and process it. Stop when a space is reached.
;
@@OptLoop:
jcxz SHORT @@Fin ; abort if nothing left
lodsb
dec cl
cmp al, ' '
jz SHORT @@NextArg ; abort when space reached
call get_Opt
jmp @@OptLoop
;
; Process the current argument, which is *not* an option.
; Then, *drop thru* to advance to next argument.
;
@@NonOpt:
dec si ; back up one character
inc cl
call get_Arg
;
; Skip over spaces until next argument is reached.
;
@@NextArg:
call skip_Spaces
or cl, cl
jnz @@ArgLoop
@@Fin:
ret
ENDP process_CmdLine
;--------------------------------------------------------------------------;
; E N T R Y P O I N T ;
;--------------------------------------------------------------------------;
;---- main --------------------------------------------------------------;
; Purpose: Main section of program. ;
; Notes: none ;
; Entry: Arguments as desired ;
; Exit: Return code as follows: ;
; 0 => program timed-out ;
; 255 => on-line help requested ;
; else => ASCII value of character pressed. ;
; Calls: process_CmdLine, fputs, pause, getchar, tolower, toupper ;
; Changes: n/a ;
;--------------------------------------------------------------------------;
main:
;
; Process commandline arguments. If the variable HFlag is set, then
; on-line help is displayed and the program immediately terminates.
;
call process_CmdLine ; process commandline args
cmp [HFlag], 0 ; is help needed?
jz SHORT @@NoHelp ; no
mov al, ERRH ; yes, so set return code
mov bx, STDERR
mov dx, OFFSET HelpMsg ; point to help message
call fputs
jmp SHORT @@Fin ; and jump to end of program
;
; Display any message from commandline then get keypress from user.
;
@@NoHelp:
mov bx, STDOUT ; everything to stdout
cmp [MsgLen], 0 ; anything to print out?
jz SHORT @@NoPrompt ; nope
mov dx, [MsgTxt]
call fputs
@@NoPrompt:
cmp [TFlag], 0 ; need to wait?
jz SHORT @@KeyIn ; no
mov ax, [Delay] ; yes, so...
call pause ; pause
jz SHORT @@KeyIn ; zf means a key is ready
ZERO al ; set return code to zero
jmp SHORT @@NewLine
@@KeyIn:
call getchar
;
; Convert character in AL as necessary. NB: if both '-l' and '-u' options
; are specified, the return value will be based on *uppercase* value.
;
cmp [LFlag], 0 ; convert to lowercase?
jz SHORT @@MaybeUpper ; no
call tolower
@@MaybeUpper:
cmp [UFlag], 0 ; convert to uppercase?
jz SHORT @@NewLine ; no
call toupper
;
; Add a final newline to keep things neat.
;
@@NewLine:
mov dx, OFFSET EOL + 1
call fputs
;
; Ok, let's terminate the program. Return code is already in AL.
;
@@Fin:
mov ah, 4ch
int DOS
EVEN
Buffer db ? ; space for single character
; nb: shared by fgetc() & fputc()
;-------------------------------------------------------------------------;
; Purpose: Reads a character from specified device.
; Notes: No checks are done on BX's validity.
; Buffer is shared by fputc(). Do *NOT* use in a
; multitasking environment like DESQview.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: BX = device handle.
; Exit: AL = character,
; Carry flag set on error (AX holds error code).
; Calls: none
; Changes: AX
; flags
;-------------------------------------------------------------------------;
PROC fgetc
push cx dx
IF @DataSize NE 0
push ds
mov ax, @data
mov ds, ax
ENDIF
mov dx, OFFSET Buffer ; point to storage
mov cx, 1 ; only need 1 char
mov ah, 3fh
int DOS ; get it
jc SHORT @@Fin ; abort on error
mov al, [Buffer]
@@Fin:
IF @DataSize NE 0
pop ds
ENDIF
pop dx cx
ret
ENDP fgetc
;-------------------------------------------------------------------------;
; Purpose: Writes a character to specified device.
; Notes: No checks are done on BX's validity.
; Buffer is shared by fputc(). Do *NOT* use in a
; multitasking environment like DESQview.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: AL = character to display,
; BX = device handle.
; Exit: AL = 1 if successful,
; Carry flag set on error (AX holds error code).
; Calls: none
; Changes: AX
;-------------------------------------------------------------------------;
PROC fputc
push cx dx
IF @DataSize NE 0
push ds
mov dx, @data
mov ds, ax
ENDIF
mov dx, OFFSET Buffer ; point to storage
mov [Buffer], al ; save char
mov cx, 1 ; only write 1 char
mov ah, 40h
int DOS
IF @DataSize NE 0
pop ds
ENDIF
pop dx cx
ret
ENDP fputc
;-------------------------------------------------------------------------;
; Purpose: Reads a character from STDIN.
; Notes: Character is echoed to display.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: AL = character.
; Calls: none
; Changes: AX
;-------------------------------------------------------------------------;
PROC getchar
mov ah, 1
int DOS
ret
ENDP getchar
;-------------------------------------------------------------------------;
; Purpose: Writes a character to STDOUT device.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: DL = character to display.
; Exit: n/a
; Calls: none
; Changes: none
;-------------------------------------------------------------------------;
PROC putchar
push ax
mov ah, 2
int DOS
pop ax
ret
ENDP putchar
;-------------------------------------------------------------------------;
; Purpose: Checks if a character is ready for input from STDIN.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: zf = 1 if character available.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC kbhit
push ax
mov ah, 0bh
int DOS
cmp al, 0ffh ; AL = FFh if character ready
pop ax
ret
ENDP kbhit
EVEN
;-------------------------------------------------------------------------;
; Purpose: Writes an ASCIIZ string to specified device.
; Notes: A zero-length string doesn't seem to cause problems when
; this output function is used.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: BX = device handle,
; DS:DX = pointer to string.
; Exit: Carry flag set if EOS wasn't found or handle is invalid.
; Calls: strlen
; Changes: none
;-------------------------------------------------------------------------;
PROC fputs
push ax cx di es
mov ax, ds
mov es, ax
mov di, dx
call strlen ; set CX = length of string
jc SHORT @@Fin ; abort if problem finding end
mov ah, 40h ; MS-DOS raw output function
int DOS
@@Fin:
pop es di cx ax
ret
ENDP fputs
EVEN
;-------------------------------------------------------------------------;
; Purpose: Writes an error message to stderr.
; Notes: none
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: DS:DX = pointer to error message.
; Exit: n/a
; Calls: fputs
; Changes: none
;-------------------------------------------------------------------------;
PROC errmsg
push bx dx
mov bx, STDERR
mov dx, OFFSET ProgName ; display program name
call fputs
pop dx ; recover calling parameters
push dx ; and save again to avoid change
call fputs ; display error message
mov dx, OFFSET EOL
call fputs
pop dx bx
ret
ENDP errmsg
EVEN
;-------------------------------------------------------------------------;
; Purpose: Gets current system date, based on DOS's internal clock.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: AL = day of week (0 = Sunday)
; DL = day (1 to 31)
; DH = month (1 to 12)
; CX = year (1980 to 2099)
; Calls: none
; Changes: AX, CX, DX
;-------------------------------------------------------------------------;
PROC getdate
mov ah, 2ah ; MS-DOS get system date function
int DOS
ret
ENDP getdate
;-------------------------------------------------------------------------;
; Purpose: Gets current system time, based on DOS's internal clock.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: CL = minutes (0 - 59)
; CH = hour (0 - 23)
; DL = hundredths of seconds (0 - 99)
; DH = seconds (0 - 59)
; Calls: none
; Changes: CX, DX
;-------------------------------------------------------------------------;
PROC gettime
push ax
mov ah, 2ch ; MS-DOS get system time function
int DOS
pop ax
ret
ENDP gettime
EVEN
;-------------------------------------------------------------------------;
; Purpose: Pauses execution until a specified time.
; Notes: If time is 12 or more hours in advance, execution aborts.
; Range for hours is 0-23, for minutes is 0-59, etc...
; This is as accurate as the DOS clock.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: AX:BX = time of day (hh.mm.ss.hs) at which to awaken.
; Exit: n/a
; Calls: gettime
; Changes: flags
;-------------------------------------------------------------------------;
PROC at
push cx dx
@@TimeLoop:
call gettime ; get current time
sub cx, ax ; cx = current time - alarm time
jz SHORT @@CheckSecs ; if zero, check seconds
cmp ah, 12 ; adjust if alarm is a.m. ...
jae SHORT @@TooFar?
cmp ch, 0 ; and current time is p.m. ...
jl SHORT @@TooFar? ; (signed comparison!!!)
sub ch, 24 ; by subtracting 24 hours
@@TooFar?:
neg cx ; cx = alarm time - current time
cmp cx, 12 * 256 ; more than 12 hours difference?
ja SHORT @@Fin ; yep (unsigned comparison!!!)
jmp @@TimeLoop ; nope (already checked if ==)
@@CheckSecs:
cmp dx, bx ; wait a few more seconds?
jb @@TimeLoop ; yep
or dl, 1 ; no, clear zf
@@Fin:
pop dx cx
ret
ENDP at
;-------------------------------------------------------------------------;
; Purpose: Pauses execution for a specified number of seconds or
; until a keypress is detected.
; Notes: Delay should be less than 12 hours (43200 seconds) to
; avoid checks on date rollover yet ensure we haven't
; paused too long. Delay is not checked however!
; This procedure works by adding the delay to the current
; time and waiting until then. If the system clock is
; adjusted in meantime, results are unpredictable. I
; tried looping and calling gettime(), but that was
; inaccurate due to roundoff of hundreths of secs.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: AX = delay time (in seconds).
; Exit: Zero flag set if input ready; cleared otherwise.
; Calls: gettime, kbhit
; Changes: flags
;-------------------------------------------------------------------------;
PROC pause
push ax bx cx dx
ZERO bx
mov cx, 60 ; 60 secs/min and 60 mins/hour
ZERO dx
div cx ; now ax = hours and minutes
mov bh, dl ; and bh = seconds (dh == 0)
ZERO dx
div cx ; now ax = hours, dx = minutes
mov ah, al ; hours
mov al, dl ; minutes
;
; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
;
call gettime ; get current time
add ax, cx ; compute alarm time
add bx, dx
cmp bh, 60 ; too many seconds?
jb SHORT @@CheckMins ; nope
sub bh, 60 ; yep, adjust
inc al
@@CheckMins:
cmp al, 60 ; too many minutes?
jb SHORT @@CheckHours ; nope
sub al, 60 ; yep, adjust
inc ah
@@CheckHours:
cmp ah, 24 ; too many hours?
jb SHORT @@TimeLoop ; nope
sub ah, 24 ; yep, adjust
;
; Here's the main loop. Check for both keypress and alarm time.
; NB: Because of overhead in the code it's possible to overshoot
; the alarm time by a few hundreths of a second so check for that.
;
@@TimeLoop:
call kbhit ; check for user input
jz SHORT @@Fin ; and abort if present
call gettime ; get current time
sub cx, ax ; cx = current time - alarm time
jz SHORT @@CheckSecs ; if zero, check seconds
cmp ah, 12 ; adjust if alarm is a.m. ...
jae SHORT @@TooFar?
cmp ch, 0 ; and current time is p.m. ...
jl SHORT @@TooFar? ; (signed comparison!!!)
sub ch, 24 ; by subtracting 24 hours
@@TooFar?:
neg cx ; cx = alarm time - current time
cmp cx, 12 * 256 ; more than 12 hours difference?
ja SHORT @@Fin ; yep (unsigned comparison!!!)
jmp @@TimeLoop ; nope (already checked if ==)
@@CheckSecs:
cmp dx, bx ; wait a few more seconds?
jb @@TimeLoop ; yep
or dl, 1 ; no, clear zf
@@Fin:
pop dx cx bx ax
ret
ENDP pause
;-------------------------------------------------------------------------;
; Purpose: Pauses execution for a specified number of seconds.
; Notes: Delay should be less than 12 hours (43200 seconds) due
; to the way at() is implemented. This is not checked!
; This procedure works by adding the delay to the current
; time and waiting until then. If the system clock is
; adjusted in meantime, results are unpredictable. I
; tried looping and calling gettime(), but that was
; inaccurate due to roundoff of hundreths of secs.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: AX = delay time (in seconds).
; Exit: n/a
; Calls: gettime, at
; Changes: none
;-------------------------------------------------------------------------;
PROC sleep
push ax bx cx dx
ZERO bx
mov cx, 60 ; 60 secs/min and 60 mins/hour
ZERO dx
div cx ; now ax = hours and minutes
mov bh, dl ; and bh = seconds (dh == 0)
ZERO dx
div cx ; now ax = hours, dx = minutes
mov ah, al ; hours
mov al, dl ; minutes
;
; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
;
call gettime ; get current time
add ax, cx ; compute alarm time
add bx, dx
cmp bh, 60 ; too many seconds?
jb SHORT @@CheckMins ; nope
sub bh, 60 ; yep, adjust
inc al
@@CheckMins:
cmp al, 60 ; too many minutes?
jb SHORT @@CheckHours ; nope
sub al, 60 ; yep, adjust
inc ah
@@CheckHours:
cmp ah, 24 ; too many hours?
jb SHORT @@Fin ; nope
sub ah, 24 ; yep, adjust
@@Fin:
call at ; pause until alarm time
pop dx cx bx ax
ret
ENDP sleep
EVEN
;-------------------------------------------------------------------------;
; Purpose: Converts string of digits to an *unsigned* integer in
; range [0, 65535].
; Notes: Conversion stops with first non-numeric character.
; Requires: 8086-class CPU.
; Entry: DS:SI = pointer to string of digits.
; Exit: AX = unsigned integer (garbage if cf = 1),
; DS:SI = pointer to first non-digit found,
; cf = 1 if number is too big.
; Calls: none
; Changes: AX, SI
; flags
;-------------------------------------------------------------------------;
PROC atou
push bx cx dx ; DX destroyed by MUL below
ZERO ax ; AX = digit to convert
ZERO bx ; BX = integer word
mov cx, 10 ; CX = conversion factor
@@NextCh:
mov bl, [si] ; get character
cmp bl, '0' ; test if a digit
jb SHORT @@Fin
cmp bl, '9'
ja SHORT @@Fin
inc si ; bump up pointer
mul cx ; multiply old result by 10
jc SHORT @@Overflow
sub bl, '0' ; convert digit
add ax, bx ; add current value
jnc @@NextCh ; continue unless result too big
@@Overflow:
ZERO cx ; denotes overflow
jmp @@NextCh
@@Fin:
cmp cx, 10 ; cf = (cx != 10)
pop dx cx bx
ret
ENDP atou
EVEN
;-------------------------------------------------------------------------;
; Purpose: Tests if character is a valid ASCII alphanumeric character.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC isalpha
push ax
or al, 20h ; lowercase it
cmp al, 'a' ; if < 'a' zf = 0
jb SHORT @@Fin
cmp al, 'z' ; if > 'z' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
pop ax
ret
ENDP isalpha
;-------------------------------------------------------------------------;
; Purpose: Tests if character is a valid ASCII digit.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC isdigit
cmp al, '0' ; if < '0' zf = 0
jb SHORT @@Fin
cmp al, '9' ; if > '9' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP isdigit
;-------------------------------------------------------------------------;
; Purpose: Tests if character is lowercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC islower
cmp al, 'a' ; if < 'a' zf = 0
jb SHORT @@Fin
cmp al, 'z' ; if > 'z' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP islower
;-------------------------------------------------------------------------;
; Purpose: Tests if character is uppercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC isupper
cmp al, 'A' ; if < 'A' zf = 0
jb SHORT @@Fin
cmp al, 'Z' ; if > 'Z' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP isupper
;-------------------------------------------------------------------------;
; Purpose: Tests if character is an ASCII whitespace.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC iswhite
cmp al, SPACE ; if == SPACE then zf = 1
jz SHORT @@Fin
cmp al, TAB ; if == TAB then zf = 1
jz SHORT @@Fin
cmp al, LF ; if == LF then zf = 1
jz SHORT @@Fin
cmp al, CR ; if == CR then zf = 1
@@Fin:
ret
ENDP iswhite
EVEN
;-------------------------------------------------------------------------;
; Purpose: Converts character to lowercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be converted.
; Exit: AL = converted character.
; Calls: none
; Changes: AL
; flags
;-------------------------------------------------------------------------;
PROC tolower
cmp al, 'A' ; if < 'A' then done
jb SHORT @@Fin
cmp al, 'Z' ; if > 'Z' then done
ja SHORT @@Fin
or al, 20h ; make it lowercase
@@Fin:
ret
ENDP tolower
;-------------------------------------------------------------------------;
; Purpose: Converts character to uppercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be converted.
; Exit: AL = converted character.
; Calls: none
; Changes: AL
; flags
;-------------------------------------------------------------------------;
PROC toupper
cmp al, 'a' ; if < 'a' then done
jb SHORT @@Fin
cmp al, 'z' ; if > 'z' then done
ja SHORT @@Fin
and al, not 20h ; make it uppercase
@@Fin:
ret
ENDP toupper
EVEN
;-------------------------------------------------------------------------;
; Purpose: Calculates length of an ASCIIZ string.
; Notes: Terminal char is _not_ included in the count.
; Requires: 8086-class CPU.
; Entry: ES:DI = pointer to string.
; Exit: CX = length of string,
; cf = 0 and zf = 1 if EOS found,
; cf = 1 and zf = 0 if EOS not found within segment.
; Calls: none
; Changes: CX,
; flags
;-------------------------------------------------------------------------;
PROC strlen
push ax di
pushf
cld ; scan forward only
mov al, EOS ; character to search for
mov cx, di ; where are we now
not cx ; what's left in segment - 1
push cx ; save char count
repne scasb
je SHORT @@Done
scasb ; test final char
dec cx ; avoids trouble with "not" below
@@Done:
pop ax ; get original count
sub cx, ax ; subtract current count
not cx ; and invert it
popf ; restore df
dec di
cmp [BYTE PTR es:di], EOS
je SHORT @@Fin ; cf = 0 if equal
stc ; set cf => error
@@Fin:
pop di ax
ret
ENDP strlen
END
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/