home *** CD-ROM | disk | FTP | other *** search
- ;-------------------------------------------------------------------------------
- ; MODE -- Mode setting utility for Free-DOS
- ;
- ; Written for the Free-DOS project.
- ; (c) Copyright 1994-1995 by K. Heidenstrom.
- ;
- ; This program is free software. You may redistribute the source and
- ; executable in unmodified form and/or modify it for your own use only.
- ; If you make any changes, please include a note to that effect in the
- ; usage summary. Do not distribute modified versions of this program.
- ; If you make significant improvements, please consider sending the
- ; change details to the author, kheidens@actrix.gen.nz on the Internet
- ; or K. Heidenstrom c/- P.O. Box 27-103, Wellington, New Zealand, so
- ; that other Free-DOS users may benefit.
- ;
- ; This program is provided "as-is" without any warranty of any kind,
- ; including the implied warranty of merchantability or fitness for a
- ; particular purpose. In no event will the author be liable for any
- ; damages of any kind related to the use of this program.
- ;
- ; This program is based on, but not derived from, the DR-DOS MODE program.
- ; In writing this program, I used only the functionality and usage syntax
- ; of that program as a guideline. I have no knowledge of the internal
- ; operation of the original MODE program and I did not disassemble it to
- ; determine how it works, nor did I use any code from it. In other words,
- ; this program is an entirely original work, based only on the documented
- ; functionality and command syntax of the original MODE program. KH.941230.
- ;
- ; See the accompanying file MODE.TXT for more information.
- ;
- ;-------------------------------------------------------------------------------
- ;
- ; This program must be assembled with Borland's TASM. The syntax is:
- ;
- ; tasm /ml /t /w2 mode;
- ; tlink /t mode, mode, nul
- ;
- ;-------------------------------------------------------------------------------
- ;
- ; Modified:
- ;
- ; KH.941123.001 0.0.0 Started
- ; KH.941124.002 Design
- ; KH.941212.003 Design
- ; KH.941227.004 Parameter handlers working
- ; KH.941228.005 Typematic and COM functions working
- ; KH.941229.006 PARK functions implemented - still need LPT and video
- ; KH.941230.007 LPT infinite retries and redirection implemented
- ; KH.941231.008 1.0.0 First version, probably lots of bugs lurking here
-
- Ver EQU 1
- SubVer EQU 0
- ModVer EQU 0
- VerDate EQU "941231" ; YYMMDD format
-
- ;-------------------------------------------------------------------------------
-
- PAGE ,132
- IDEAL
- %TITLE "MODE -- Mode setting utility for Free-DOS"
- NAME MODE
-
- ; Equates
-
- AppName EQU "MODE"
-
- ; Shorthand
-
- MACRO point Reg,Label
- mov Reg,OFFSET Label
- ENDM
-
- MACRO DBBW Byte1,Byte2,Word
- DB Byte1,Byte2
- DW Word
- ENDM
-
- MACRO prexp Text1,Exp,Text2 ; Invoke in MASM mode only
- %OUT Text1 &Exp Text2
- ENDM
-
- NL EQU 13,10
-
- ; Program
-
- SEGMENT ComFile
- ASSUME cs:ComFile,ds:ComFile,es:nothing,ss:nothing
-
- ORG 0100h ; Com-type file
- PROC Main near
- jmp Main2
- ENDP Main
-
- Signature DB "SIG: Free-DOS MODE.COM version ",Ver+"0",".",SubVer+"0",".",ModVer+"0",", ",VerDate,": "
- ;---------------
- ;!! Change the following line if you create a derivative version of this program
- DB "Original"
- ;---------------
- DB " ",26 ; Ctrl-Z
- SigLen = $ - Main ; Length of program signature
-
- ; Resident data ================================================================
-
- Int8Busy DB 0 ; Flag whether int 8 is parking now
- Int13Busy DB 0 ; Flag whether disk request in progress
- Park_Ticks DW 0 ; Number of ticks or zero for Mode_Park
- ParkDrive0 DW 0 ; Control variable for parking drive 0
- ParkDrive1 DW 0 ; Control variable for parking drive 1
-
- ; Printer persistent behaviour and serial port redirection control bytes:
- ;
- ; 7 6 5 4 3 2 1 0
- ; * . . . . . . . Persistent mode: 0=Normal, 1=Persistent
- ; . * * * * . . . Not used
- ; . . . . . * * * Redirection: 0=None, 1-4 = COM1-COM4, 7=NUL
-
- LPT_Ctrl DB 0,0,0,0 ; Printer persistent/redirection control
-
- ; Resident code ================================================================
-
- ASSUME ds:nothing
-
- ; Int 8 intercepter ------------------------------------------------------------
-
- ; The int 8 intercepter hooks into the hardware timer tick interrupt and is
- ; used by the timeout park function. When a hard drive is accessed via int
- ; 13h, the ParkDrive0 or ParkDrive1 variable is reset to the timeout period
- ; (Park_Ticks). These two counter variables are handled separately. Each is
- ; decremented (if non-zero) on every tick by the int 8 handler. When the
- ; counter variable decrements to zero, the appropriate drive is parked. This
- ; is done by the int 8 handler.
- ; When the int 8 handler decides to park a drive, it chains to the original
- ; handler first. This causes an EOI to be issued, thus lower priority
- ; interrupts and other timer tick interrupts are enabled. During the time
- ; that the hard drive is being parked, the Int8Busy flag is set. If the
- ; int 8 intercepter is entered with this flag set, it will just chain to the
- ; original handler without performing its normal timeout function.
- ; If the ParkDrive0 or ParkDrive1 variables are zero, this indicates that they
- ; have timed out and the park has been performed; they will not be decremented.
- ; I know that on the tick when drive 0 is parked, the drive 1 tick count will
- ; not be decremented - I don't care; the timing error is insignificant.
-
- ASSUME ds:nothing
-
- PROC New08 far ; Int 08h (timer tick) intercepter
- pushf ; Preserve flags
- cmp [Int8Busy],0 ; Check whether we're already busy
- jnz GoOld08 ; If so, don't do anything
- cmp [ParkDrive0],0 ; Drive 0 already timed out?
- jnz DecrDrive0 ; If not
- CheckDrive1: cmp [ParkDrive1],0 ; Drive 1 already timed out?
- jnz DecrDrive1 ; If not
-
- ; Idle - just jump to old handler
-
- GoOld08: popf ; Fix stack
- DB 0EAh ; JMP xxxx:yyyy
- Old08Vec = THIS DWORD ; Vector to original handler
- Old08Ofs DW 0 ; Offset
- Old08Seg DW 0 ; Segment
-
- DecrDrive0: dec [ParkDrive0] ; Count down ticks for drive 0 park
- jnz CheckDrive1 ; If not zero yet
-
- ; Time to park drive 0 - make sure no disk operation is in progress first!
-
- cmp [Int13Busy],0 ; Check for disk operation in progress
- jz DoPark0 ; If idle
- inc [ParkDrive0] ; Try again next tick
- jmp SHORT CheckDrive1 ; Handle the drive 1 counter and exit
-
- ; Safe to park drive 0 - do it
-
- DoPark0: inc [Int8Busy] ; Set busy flag
- pushf ; Simulate stack for an INT
- call [Old08Vec] ; Chain to original handler (send EOI)
- sti ; Enable interrupts
- push dx ; Preserve
- push cx ; Preserve
- push ax ; Preserve
- mov dx,80h ; Drive I.D is 80h for first hard disk
- DB 0B9h ; MOV CX,nnnn
- MaxCylinder0 DW 0 ; Operand is modified by install code
- jmp SHORT DoSeek ; Go to common do-seek and return stuff
-
- ; Count down timer for drive 1
-
- DecrDrive1: dec [ParkDrive1] ; Count down ticks for drive 1 park
- jnz GoOld08 ; If not zero yet
-
- ; Time to park drive 1 - make sure no disk operation is in progress first!
-
- cmp [Int13Busy],0 ; Check for disk operation in progress
- jz DoPark1 ; If idle
- inc [ParkDrive1] ; Try again next tick
- jmp SHORT GoOld08 ; Nothing more we can do at the moment
-
- ; Safe to park drive 1 - do it
-
- DoPark1: inc [Int8Busy] ; Set busy flag
- pushf ; Simulate stack for an INT
- call [Old08Vec] ; Chain to original handler (send EOI)
- sti ; Enable interrupts
- push dx ; Preserve
- push cx ; Preserve
- push ax ; Preserve
- mov dx,81h ; Drive I.D is 81h for second hard disk
- DB 0B9h ; MOV CX,nnnn
- MaxCylinder1 DW 0 ; Operand is modified by install code
- DoSeek: jcxz NoSuchDrive ; If no such drive
- mov ah,0Ch ; Seek function
- pushf ; Simulate INT
- call [cs:Old13Vec] ; Don't issue int 13h - that would reset
- NoSuchDrive: pop ax ; the timeout counter!
- pop cx ; Restore
- pop dx ; Restore
- mov [Int8Busy],0 ; No longer busy
- popf ; Restore flags pushed at start of int 8
- iret ; Finally, return to application
- ENDP New08
-
- ; Int 13h intercepter ----------------------------------------------------------
-
- ; The int 13h intercepter hooks into the disk function interrupt and is
- ; responsible for resetting the park timeout counters used by the int 8
- ; intercepter when the hard drive(s) is/are accessed. The first two
- ; physical hard drives are supported. It also maintains the Int13Busy
- ; flag which is used by the int 8 intercepter to ensure that it does not
- ; call int 13h while another floppy or hard disk request is in progress.
-
- ASSUME ds:nothing
-
- PROC New13 far ; Int 13h (disk functions) intercepter
- pushf ; Preserve flags
- mov [Int13Busy],1 ; Set int 13h busy flag
- cmp dl,80h ; Check for hard drive 0
- je AccessDrive0 ; If so
- cmp dl,81h ; Check for hard drive 1
- je AccessDrive1 ; If so
-
- ; Jump to old handler
-
- GoOld13: popf ; Fix stack
- pushf ; Simulate interrupt
- DB 9Ah ; CALL xxxx:yyyy
- Old13Vec = THIS DWORD ; Vector to original handler
- Old13Ofs DW 0 ; Offset
- Old13Seg DW 0 ; Segment
- mov [Int13Busy],0 ; Clear busy flag
- retf 2 ; Return with flags returned by BIOS
-
- AccessDrive0: push [Park_Ticks] ; Get timeout count
- pop [ParkDrive0] ; Set timeout count for drive 0
- jmp SHORT GoOld13 ; Chain
-
- AccessDrive1: push [Park_Ticks] ; Get timeout count
- pop [ParkDrive1] ; Set timeout count for drive 1
- jmp SHORT GoOld13 ; Chain
- ENDP New13
-
- ; Int 16h intercepter ----------------------------------------------------------
-
- ; The int 16h intercepter provides the LOCK function on the typematic delay
- ; and rate. Function calls with AH=3 are related to typematic functions.
- ; The normal function to set the typematic parameters is AH=3, AL=5 with BL
- ; and BH containing the two parameters (delay and rate).
- ; According to version 39 of Ralf Brown's Interrupt List, only subfunctions
- ; 0-6 (in AL) exist. Subfunction 6 allows the typematic parameters to be
- ; requested on some machines; all other defined subfunctions relate to setting
- ; or modifying the typematic parameters.
- ; This intercepter intercepts all calls with AH=3 (i.e. all typematic related
- ; function calls) except AL=6 (request typematic parameters) and higher (these
- ; are currently undefined subfunctions) and replaces the call with AL=5 (set
- ; typematic parameters) using the 'locked' typematic parameters in BX. When
- ; it intercepts the function, it preserves the typematic parameters provided
- ; in BX and the function and subfunction numbers provided in AX so that they
- ; may be returned unchanged to the caller.
- ; There may be potential problems with intercepting the typematic functions if
- ; functions greater than 6 are added in future. At the moment, these will be
- ; passed through as normal. If they would result in a change to the typematic
- ; settings, then the lock function will not successfully lock the parameters.
- ; Alternative approaches would be:
- ; 1. Instead of replacing subfunctions 0-5 with subfunction 5, simply ignore
- ; these subfunctions. This would cause extra difficulty in setting the
- ; locked parameters from a transient copy of MODE.
- ; 2. Instead of passing undefined subfunctions (7 and higher) they could be
- ; intercepted in the same way or just ignored. This would cause trouble
- ; if the future subfunction was supposed to return values from the BIOS,
- ; because the BIOS function would not be called and the values would not
- ; be returned, though the caller is expecting them.
-
- ASSUME ds:nothing
-
- PROC New16 far ; Int 16h (keyboard funcs) intercepter
- pushf ; Preserve caller flags
- cmp ah,3 ; Check for typematic functions
- jne NoIntercept16 ; If not
- cmp al,6 ; Check for subfunctions 0-5
- jb YesIntercept16 ; If so
-
- ; Do not intercept this function call
-
- NoIntercept16: popf ; Fix stack
- DB 0EAh ; JMP xxxx:yyyy
- Old16Vec = THIS DWORD ; Vector to original handler
- Old16Ofs DW 0 ; Offset
- Old16Seg DW 0 ; Segment
-
- ; Intercept this function call
-
- YesIntercept16: popf ; Fix stack
- push bx ; Keep parameters supplied by caller
- push ax ; Keep caller-supplied function too
- mov al,5 ; Use standard set typematic function
- DB 0BBh ; MOV BX,nnnn
- Type_Parm = THIS WORD ; Both typematic parameters
- Type_Rate DB 0FFh ; Typematic rate 0-31 MUST BE
- Type_Delay DB 0FFh ; Typematic delay 0-3 ADJACENT
- pushf ; Simulate an interrupt
- call [Old16Vec] ; Call old handler
- pop ax ; Restore AX provided by caller
- pop bx ; Restore BX provided by caller
- retf 2 ; Return flags returned by BIOS
- ENDP New16
-
- ; Int 17h intercepter ----------------------------------------------------------
-
- ; The int 17h intercepter provides the persistent (infinite timeout) function
- ; and redirection function for printer I/O. Up to four parallel ports are
- ; supported. Each printer port (LPT1-4) has a control byte, which is allocated
- ; as listed in the variable declarations for LPT_Ctrl. The port may be set
- ; for persistent mode, in which case this intercepter will continually retry
- ; if a timeout occurs while trying to send a character, and/or may be redirected
- ; to a serial port or to NUL, in which case this intercepter translates the
- ; specified request and calls the BIOS serial firmware functions (int 14h) or
- ; ignores the request.
- ; Other programs (e.g. TSRs, BIOS extensions) may hook into int 17h, but
- ; calls to these programs will have AH and/or DX out of range. AH is the
- ; function number, which may be 0 (send character), 1 (initialise port), or
- ; 2 (get status). DX is the port number, normally 0-2 for LPT1-3 but MODE
- ; will support up to LPT4 although DOS may not provide an LPT4 device.
-
- ASSUME ds:nothing
-
- PROC New17 far ; Int 17h (printer funcs) intercepter
- pushf ; Preserve those flags
- cmp ah,3 ; Check for invalid function
- jae NoIntercept17 ; If so, probably a TSR function
- cmp dx,4 ; Check for invalid port
- jae NoIntercept17 ; If so, probably a TSR function too
-
- ; Get port function control byte for the specified parallel port
-
- push bx ; Preserve
- mov bx,dx ; Port number
- mov bl,[LPT_Ctrl+bx] ; Get control byte for this port
-
- ; First check for redirection. If the port is redirected, handle this
- ; further on.
-
- test bl,00000111b ; Redirected?
- jnz IsRedirected ; If so
-
- ; Port is not redirected. If persistent retries are not specified, just chain
- ; to the normal int 17h handler. Also, if it's just a status call or an init
- ; printer call, chain to the normal handler too. This leaves the case where
- ; the function is send-character and persistent operation is specified.
-
- test bl,10000000b ; Persistent?
- jz NoInter17BX ; If not, just go to old int 17h handler
- test ah,ah ; Sending a character?
- jnz NoInter17BX ; If not, do old thing too
-
- ; Persistent operation specified on non-redirected port - call the old int 17h
- ; handler repeatedly until it returns successful (no timeout)
-
- PersistLoop: xor ah,ah ; Set zero function number (send char)
- pushf ; Simulate stack for interrupt
- call [cs:Old17Vec] ; Call original BIOS handler
- test ah,00000001b ; Timeout flag set?
- jnz PersistLoop ; If so, try again (and again...)
- jmp SHORT Return17BX ; Exit
-
- ; Port is redirected. Check for redirected to NUL - if so, just ignore the
- ; request and return a faked 'happy' status. Otherwise, check the function
- ; number. The possible functions are 0 (send character), 1 (initialise
- ; printer), and 2 (get status). Function 1 (init printer) has no meaning
- ; when used with a serial port; it just reports the status, so it will be
- ; treated the same as function 2.
-
- IsRedirected: cmp bl,7 ; Redirecting to NUL?
- jne RedirNotNUL ; If not
-
- mov ah,10010000b ; Happy happy
- jmp SHORT Return17BX ; Exit
-
- RedirNotNUL: dec bx ; Get serial port (0-3) to redirect to
- push dx ; Keep original port number
- mov dl,bl ; Get serial port number
- and dl,01111111b ; Turn off the persistent bit in DL
- test ah,ah ; Check function number
- jnz Redir_GetStat ; If not zero (get status)
-
- ; Now sending a character to a redirected port. Convert the call first, and
- ; after it completes, if a timeout was flagged, check whether the port was set
- ; for persistent retries, and retry if so. During all this stuff, DX contains
- ; the redirected serial port number, not the original parallel port number.
-
- SerialPersist: mov ah,1 ; Function to send character (serial)
- int 14h ; Send it
- test ah,10000000b ; Successful?
- jz Redir_GetStat ; If so
- test bl,10000000b ; Persistent behaviour?
- jnz SerialPersist ; If so, just try again etc etc
- mov bh,10000000b ; Flag that we had a timeout
-
- ; Had an error sending a character to the redirected port - just return
- ; the status byte normally - fall through...
-
- ; Request status of redirected port. The persistence flag is not relevant.
- ; The status is returned in AH, in the form:
- ;
- ; 7 6 5 4 3 2 1 0
- ; * . . . . . . . Printer ready (1 = ready, 0 = not ready; busy)
- ; . * . . . . . . Acknowledgement from printer, returned as 0
- ; . . * . . . . . Out of Paper from printer, returned as 0
- ; . . . * . . . . Selected signal from printer, returned as 1
- ; . . . . * . . . I/O error, returned as 0
- ; . . . . . * * . Not used, returned as 0
- ; . . . . . . . * Timeout error flag (true if set)
-
- Redir_GetStat: push ax ; Keep whatever was in AL
- mov ah,3
- int 14h ; Request serial port status
-
- ; Serial function 3 gets the line status and modem status registers to AH
- ; and AL respectively. I will use the CTS line, reported in bit 4 of AL,
- ; to indicate whether the printer is ready. Bit 7 of AH reports the serial
- ; timeout error. Now construct the appropriate printer status: r001000t
- ; where 'r' is the ready flag and 't' is the timeout error flag.
-
- or ah,bh ; OR in timeout from serial function 1
- shl ah,1 ; Get timeout flag to carry
- mov ah,00001000b ; Get initial value shifted right once
- rcl ah,1 ; Incorporate timeout flag
- test al,00010000b ; Is CTS active?
- jz NoCTS ; If not
- or ah,10000000b ; If so, set the ready flag
- NoCTS: pop bx ; Get original AL value to BL
- mov al,bl ; Restore AL as provided to call
-
- ; Restore registers and return with result code in AH
-
- Return17DXBX: pop dx ; Restore DX
- Return17BX: pop bx ; Restore BX
- popf ; Restore flags
- iret ; Return to caller
-
- ; Pass the function call on to the BIOS int 17h handler
-
- NoInter17BX: pop bx ; Restore
- NoIntercept17: popf ; Fix stack
- DB 0EAh ; JMP xxxx:yyyy
- Old17Vec = THIS DWORD ; Vector to original handler
- Old17Ofs DW 0 ; Offset
- Old17Seg DW 0 ; Segment
- ENDP New17
-
- ; TSR support ------------------------------------------------------------------
-
- ; TSR residency detection is via int 2Fh. When the transient copy wants to
- ; locate a resident copy of itself, if one exists, it issues int 2Fh with
- ; registers set as follows: AX = 0F73Fh, BX = 484Bh, ES = segment-paragraph
- ; of transient copy.
- ; The resident int 2Fh handler compares its own signature to the signature of
- ; the transient copy (the segment of the transient copy is provided in the ES
- ; register by the caller), and if they do not match, chains to the previous
- ; owner of int 2Fh. If the signatures do match, the handler sets ES to the
- ; segment-paragraph of the resident copy, sets BX to 4D4Ah and returns. The
- ; signature is assumed to start at offset 100h into the segment and must at
- ; least contain the program name and version number.
-
- ASSUME ds:nothing
-
- PROC New2F far
- pushf
- sti ; Enable hardware interrupts
- cmp ax,0F73Fh ; Calling me?
- jne NotForMe ; If not
- cmp bx,484Bh ; Check BX too
- jne NotForMe ; If not
- push ds ; Preserve these regs
- push di
- push si
- push cx
- push cs ; Set DS to this segment
- pop ds
- mov si,OFFSET Main ; Point to our signature
- mov di,si ; Point to possible transient signature
- mov cx,SigLen ; Number of chars to compare
- cld ; Upwards compare direction
- repe cmpsb ; Compare signatures
- pop cx ; Restore registers
- pop si
- pop di
- pop ds
- jne NotForMe ; If signatures did not match
- mov bx,4D4Ah ; Flag acknowledgement
- push cs
- pop es ; Set ES to resident segment
- popf ; Restore flags
- iret ; Return to caller
- NotForMe: popf ; Restore flags
- DB 0EAh ; JMP xxxx:yyyy
- Old2FVec = THIS DWORD ; Vector to original handler
- Old2FOfs DW 0 ; Offset
- Old2FSeg DW 0 ; Segment
- ENDP New2F
-
- ; End of resident / start of transient =========================================
-
- MASM
- Discard = $ ; Start of transient portion
- TSRParas = (OFFSET (Discard-@curseg+15) SHR 4)
- prexp <Resident size:> %(TSRParas SHL 4) < bytes>
- IDEAL
-
- ; Transient messages -----------------------------------------------------------
-
- ; Note - I have used American spelling ("color") in the help text but not
- ; in the internal comments or labels.
-
- DOSVersMsg DB AppName,": Requires DOS version 2.0 or later",NL,"$"
- MemoryEM DB 162,AppName,": Insufficient memory",NL,0
- UsageEM DB 255,NL,AppName," -- Free-DOS mode setting and miscellaneous utility Version ",Ver+"0",".",SubVer+"0",".",ModVer+"0",", ",VerDate
- DB NL,9,"(c) Copyright 1994-1995 by K. Heidenstrom (kheidens@actrix.gen.nz)"
- DB NL
- DB NL,AppName," Videomode[,Lines] - Select video mode and lines, Videomode may be:"
- DB NL,9,9,"MONO",9,9,"- 80-column monochrome (MDA and Hercules)"
- DB NL,9,9,"BW40, BW80",9,"- 40-column and 80-column color-suppressed CGA"
- DB NL,9,9,"CO40, CO80",9,"- 40-column and 80-column color"
- DB NL,9,"Lines may be 25, 43, or 50 (43 requires EGA or VGA, 50 requires VGA)"
- DB NL,AppName," COMn:r,p,d,s - Set serial port parameters (not permanent):"
- DB NL,9,"n = port number (1-4)"
- DB NL,9,"r = baud rate (50, 110, 150, 300, 600, 1200, 2400, 4800, 9600,"
- DB NL,9,9,"14400, 19200, 28800, 38400, 57600, 115200, abbreviations)"
- DB NL,9,"p = parity (e = even, n = none, o = odd)"
- DB NL,9,"d = data bits (5-8)"
- DB NL,9,"s = stop bits (1-2)"
- DB NL,AppName," LPTn:P - Infinite timeout on parallel port (1-4) (",AppName," will go resident)"
- DB NL,AppName," LPTn:=COMx - Redirect printer output to COM port (",AppName," will go resident)"
- DB NL,AppName," LPTn:=NUL: - Redirect printer output to NUL (",AppName," will go resident)"
- DB NL,AppName," LPTn: - Remove redirection and infinite timeout on parallel port"
- DB NL,AppName," PARK[,minutes[:seconds]] - Park now or after idle (",AppName," will go resident)"
- DB NL,AppName," DELAY=d RATE=r [LOCK] - Set keyboard typematic delay and rate:"
- DB NL,9,"d = delay (1-4) r = rate (1-32) LOCK = permanent (will go resident)"
- DB NL,0
- BadCmdModeEM DB 246,AppName,": Only one command allowed per invocation",NL,0
- OutRangeEM DB 241,AppName,": Parameter out of range - check the help text",NL,0
- LinesVModeEM DB 242,AppName,": 43-line and 50-line modes are only usable with CO80 mode",NL,0
- BadBaudEM DB 243,AppName,": Unknown baud rate specified",NL,0
- TypeParamEM DB 244,AppName,": Must specify both DELAY and RATE for typematic setting",NL,0
- NoSuchSPortEM DB 115,AppName,": Specified serial port does not exist",NL,0
- SerialSetMsg DB AppName,": Serial port parameters set (not locked)",NL,"$"
- SetNormalMsg DB AppName,": LPT"
- SetNormalLPT DB "1 set to normal mode",NL,"$"
- NoHDiskEM DB 117,AppName,": No hard drive(s) found to park!",NL,0
- Parked1Msg DB AppName,": Hard drive has been parked, switch off or press Ctrl-C to return to DOS",NL,"$"
- Parked2Msg DB AppName,": Hard drives have been parked, switch off or press Ctrl-C to return to DOS",NL,"$"
- ParkInst1Msg DB AppName,": Timed park function installed for one hard drive, ",AppName," is resident",NL,"$"
- ParkInst2Msg DB AppName,": Timed park function installed for two hard drives, ",AppName," is resident",NL,"$"
- ParkResMsg DB AppName,": Timed park timeout parameter updated in resident copy",NL,"$"
- ParkEnab1Msg DB AppName,": Timed park function enabled in resident copy for one hard drive",NL,"$"
- ParkEnab2Msg DB AppName,": Timed park function enabled in resident copy for two hard drives",NL,"$"
- TypeSetMsg DB AppName,": Typematic parameters set (not locked)",NL,"$"
- LockInstMsg DB AppName,": Typematic parameters locked, resident portion of ",AppName," installed",NL,"$"
- ResLockedMsg DB AppName,": Typematic parameters now locked by resident portion of ",AppName,NL,"$"
- TResUpdateMsg DB AppName,": Locked typematic parameters updated",NL,"$"
- UnsuppVidEM DB 118,AppName,": Specified video mode is not supported on this machine",NL,0
- SetPersistMsg DB AppName,": Parallel port set for infinite retry on timeout",NL,"$"
- SetRedNULMsg DB AppName,": Parallel port redirected to NUL:",NL,"$"
- SetRedirMsg DB AppName,": Parallel port redirected to COM"
- SetRedirCOM DB "0",NL,"$"
- ResInstallMsg DB AppName,": Resident portion of MODE is now installed",NL,"$"
-
- ; Transient tables -------------------------------------------------------------
-
- ; Command table - all entries are in lower case and will be compared to the
- ; characters from the command line ORed with 00100000 binary.
-
- CmdTable: DBBW "mono",0,Param_MONO
- DBBW "bw40",0,Param_BW40
- DBBW "bw80",0,Param_BW80
- DBBW "co40",0,Param_CO40
- DBBW "co80",0,Param_CO80
- DBBW "com",0,Param_Com
- DBBW "lpt",0,Param_LPT
- DBBW "park",0,Param_PARK
- DBBW "delay=",0,Param_DELAY
- DBBW "rate=",0,Param_RATE
- DBBW "lock",0,Param_LOCK
- DB 0 ; End of table marker
-
- ; Despatch table
-
- ModeTbl DW BadUsage ; No parameters
- DW DoMode_Vid ; Set video mode (and lines)
- DW DoMode_Com ; Set serial port parameters
- DW DoMode_LPT ; Redirect parallel port or no timeout
- DW DoMode_Park ; Park now or after an idle period
- DW DoMode_Typematic ; Set typematic parameters
-
- ; Baud rate table - the value read from the command line is 16-bit therefore
- ; 115200 will appear as 49664 (115200 % 65536). Therefore if the user enters
- ; 49664 bps, this will be interpreted as 115200 bps rather than flagging an
- ; error.
- ; This table takes care of minimal text abbreviations, e.g. 96 for 9600 bps.
- ; The second number in each entry is the raw baud rate divisor value.
- ; I initially used DW 115200 / rate but TASM gives incorrect results (but no
- ; error message, though!) so I had to divide the 115200 and the baud rates
- ; by 10 in each calculation.
-
- BaudTbl: DW 12, 11520/120 ; 1200
- DW 14, 11520/1440 ; 14400
- DW 19, 11520/1920 ; 19200
- DW 24, 11520/240 ; 2400
- DW 28, 11520/2880 ; 28800
- DW 38, 11520/3840 ; 38400
- DW 48, 11520/480 ; 4800
- DW 50, 11520/5 ; 50
- DW 57, 11520/5760 ; 57600
- DW 96, 11520/960 ; 9600
- DW 110, 11520/11 ; 110
- DW 115, 11520/11520 ; 115200
- DW 144, 11520/1440 ; 14400
- DW 150, 11520/15 ; 150
- DW 192, 11520/1920 ; 19200
- DW 288, 11520/2880 ; 28800
- DW 300, 11520/30 ; 300
- DW 384, 11520/3840 ; 38400
- DW 576, 11520/5760 ; 57600
- DW 600, 11520/60 ; 600
- DW 1152, 11520/11520 ; 115200
- DW 1200, 11520/120 ; 1200
- DW 2400, 11520/240 ; 2400
- DW 4800, 11520/480 ; 4800
- DW 9600, 11520/960 ; 9600
- DW 14400, 11520/1440 ; 14400
- DW 19200, 11520/1920 ; 19200
- DW 28800, 11520/2880 ; 28800
- DW 38400, 11520/3840 ; 38400
- DW 49664, 11520/11520 ; 115200 % 65536 = 49664
- DW 57600, 11520/5760 ; 57600
- DW 65535, 11520/960 ; End of table
-
- ; LPT subfunction table - used after LPTn has been parsed to determine
- ; which of the LPT subfunctions are requested - persistent, back to normal,
- ; or redirect.
-
- LPTTable: DBBW ":=com",0,LPT_Redirect
- DBBW ":=nul:",0,LPT_RedNUL
- DBBW ":=nul",0,LPT_RedNUL
- DBBW ":p",0,LPT_Persist
- DBBW ":",0,LPT_Normal
- DB 0 ; End of table marker
-
- ; Video BIOS function 1A00h returns display combination codes in BL and BH.
- ; BL contains the DCC for the active display, BH contains the DCC for the
- ; inactive display. The following table maps DCC values to bitflags for the
- ; modes that are supported, which will be ORed into the VidHave variable that
- ; indicates what modes are acceptable. I'm not sure what the story is with
- ; the PGA and the MCGA. They may support 43-line mode.
- ;
- ; References:
- ;
- ; Programmer's Guide to PC & PS/2 Video Systems
- ; Copyright 1987 by Richard Wilton
- ; Published by Microsoft Press
- ; ISBN 1-55615-103-9
- ;
- ; EGA/VGA A Programmer's Reference Guide
- ; Copyright 1988 by Bradley Dyck Kliewer
- ; Published by Intertext Publications, McGraw-Hill Book Company, New York, NY
- ; ISBN 0-07-035089-2. Library of Congress Catalog Card number 87-83102.
-
- Vid_50 = 8 ; ┌───── Supports 50 lines in mode 3
- Vid_43 = 4 ; │┌──── Supports 43 lines in mode 3
- Vid_3 = 2 ; ││┌─── Supports mode 0-3
- Vid_7 = 1 ; │││┌── Supports mode 7
- ; ││││
- DCCTable DB 00000000b ; 0 No display adapter
- DB 00000001b ; 1 MDA/Hercules
- DB 00000010b ; 2 CGA
- DB 00000000b ; 3 Reserved
- DB 00000111b ; 4 EGA
- DB 00000111b ; 5 EGA with monochrome monitor
- DB 00000011b ; 6 PGA (Professional Grph. Adapter)
- DB 00001111b ; 7 VGA with monochrome monitor
- DB 00001111b ; 8 VGA with colour display
- DB 00000000b ; 9 Reserved
- DB 00000011b ; 10 MCGA with digital colour monitor
- DB 00000011b ; 11 MCGA with monochrome monitor
- DB 00000011b ; 12 MCGA with colour monitor
-
- ; Transient data ---------------------------------------------------------------
-
- Already DW 0 ; Segment-para of resident copy or zero
-
- ; Command line modes:
-
- Mode_None = 0
- Mode_Vid = 2 ; Set video mode (and lines)
- Mode_Com = 4 ; Set serial port parameters
- Mode_LPT = 6 ; Redirect parallel port or no timeout
- Mode_Park = 8 ; Park now or after an idle period
- Mode_Typematic = 10 ; Set typematic parameters
-
- ; Note - several of these byte parameters have an extra zero byte; this is
- ; because they are sometimes accessed as words.
-
- CmdMode DB 0,0 ; Command line mode - one of above
-
- Vid_Mode DB 0FFh,0 ; Video mode number for Mode_Vid
- Vid_Lines DB 0 ; Number of lines (0=25, 1=43, 2=50)
- VidHave DB 0 ; Video adapter mode flags
-
- Com_Port DB 0FFh,0 ; Port number for Mode_Com
- Com_BRD DW 0 ; Baud rate divisor
- Com_LCR DB 0 ; Line control register value
-
- LPT_Port DB 0FFh,0 ; Port number for Mode_LPT
- LPT_Func DB 0,0 ; 1-4 = redirect, 80h = persist,
- ; 0 = return to normal operation
-
- Type_Lock DB 0 ; Typematic lock flag (0/1)
-
- ; Transient code ===============================================================
-
- ASSUME ds:ComFile
-
- PROC Main2 near
- mov ah,30h
- int 21h
- cmp al,2 ; Expect DOS 2.0 or later
- jae DOS_Ok
- mov dx,OFFSET DOSVersMsg
- mov ah,9
- int 21h
- int 20h
-
- BadUsage: point bx,UsageEM ; Incorrect usage
-
- ; Errexit code - aborts the program with a specified message and errorlevel.
- ; On entry, BX points to a control string within the current code segment,
- ; which consists of a one-byte errorlevel followed by a null-terminated error
- ; message which may be blank. This code assumes DOS version 2.0 handle
- ; functions are supported. After writing the error message to StdErr, DOS
- ; function 30h (terminate with return code) is used to terminate the program.
-
- ErrExit: push [WORD cs:bx] ; Errorlevel onto stack
- inc bx ; Point to error message
- call ErrWriteMsg ; Display
- pop ax ; Errorlevel
- ErrExitQ: mov ah,4Ch
- int 21h ; Terminate with errorlevel in AL
-
- ErrWriteMsg: mov cx,2 ; Handle of Standard Error device
- WriteMsg: push cs
- pop ds ; Make DS valid
- mov dx,bx ; Point DX for later
- Err_Parse1: inc bx ; Bump pointer
- cmp [BYTE ds:bx-1],0 ; Hit null terminator yet?
- jnz Err_Parse1 ; If not, loop
- xchg cx,bx ; Get address of null terminator
- sub cx,dx ; Subtract offset of start of text
- dec cx ; Adjust to correct number of chars
- jz Err_Parse2 ; If no text present
- mov ah,40h ; Function to write to file or device
- int 21h ; Write error message to StdErr
- Err_Parse2: ret ; Return to exit code or whoever
-
- ; Check amount of available memory ---------------------------------------------
-
- DOS_Ok: mov ax,cs ; Get current segment
- DB 5 ; ADD AX,nnnn (avoid TASM warning!)
- DW MemParas ; Get paragraph past end of program
- cmp ax,[WORD ds:2] ; Check against paragraph of mem top
- mov bx,OFFSET MemoryEM ; Prepare for error
- jae ErrExit ; If not enough memory
- point sp,WorkStackTop ; Relocate stack to safe area
-
- ; Detect resident copy ---------------------------------------------------------
-
- ; This code locates the resident copy of this program, if any. It modifies
- ; the first three bytes of the program image then calls int 2Fh to look for
- ; a response from a resident handler for the same program. If a resident copy
- ; was found, ES contains the segment-paragraph of the resident copy. Note
- ; that the signature is completed manually here - this ensures that only
- ; copies of this program which have actually _executed_ will have the correct
- ; signature, and copies which are in disk buffers will not. Though this is not
- ; really required (due to the int 2Fh residency check method), it is tidy.
-
- mov [WORD Main],"TS" ; Complete the signature
- mov [BYTE Main+2],"D"
- mov ax,0F73Fh
- mov bx,484Bh
- int 2Fh ; TSR installation check
- cmp bx,4D4Ah
- jne NoResident
- mov [Already],es ; Store segment-para of resident copy
- NoResident:
-
- ; Command tail parsing ---------------------------------------------------------
-
- ; Scan the command tail for the start of a text token, then locate the token
- ; in the command table CmdTable. If not found, issue usage error message.
- ; If found, call the parameter handler, with SI pointing past the part of the
- ; token that matched the command table entry. The handler function will return
- ; if no error was found. The parameter handler may jump to BadUsage or ErrExit
- ; to abort the program with a syntax message or a specific error message.
- ; On return from the parameter handler (i.e. if no error was found), SI points
- ; past the text processed as part of the parameter, and should point to
- ; whitespace or the C/R that terminates the command tail.
-
- push cs
- pop es ; Make sure ES addresses to ComFile
- cld ; Upwards string direction
- mov si,81h ; Prepare for command tail parsing
- Parameter: lodsb ; Next character
- cmp al,13 ; Check for end of command tail
- je DoneParms ; If so
- cmp al," " ; Check for space or other whitespace
- jbe Parameter ; Skip it
- dec si ; Point to first char of parameter
- point di,CmdTable ; Point to start of command table
- call StringLCComp ; Find a match in the command table
- call bx ; Call appropriate handler or BadUsage
- jmp SHORT Parameter ; Next parameter
-
- ; Reached end of command line
-
- DoneParms: push cs
- pop es ; Make sure ES points to transient copy
- mov bx,[WORD CmdMode] ; Get mode format number
- jmp [ModeTbl+bx] ; Go to appropriate handler
- ENDP Main2
-
- ; Parameter handlers ===========================================================
-
- ; These handlers are called with SI pointing just past the end of the command
- ; token that was scanned. If the handler detects an error it may jump to
- ; BadUsage (syntax errors) or ErrExit (for specific errors). If no problems
- ; were found, it returns with SI pointing to the next character to be scanned,
- ; which may be whitespace or the final C/R at the end of the command tail.
- ; The handler is responsible for making sure that it doesn't scan past the
- ; terminating C/R unless this will cause an error, in other words if the
- ; handler returns, it must not scan past the final C/R - if it finds the end
- ; of the command tail during processing without an error, it must return with
- ; SI pointing to the terminating C/R.
-
- ; Video parameter handlers -----------------------------------------------------
-
- ; Video mode setting handlers - these check for more than one mode provided
- ; on the command line and parse the optional ,nn number of lines, but do not
- ; check that the specified mode is valid for the video adapter(s) present.
- ; If the ,nn parameter is present, the mode must be CO80 otherwise an error
- ; is reported.
-
- PROC VidParams near ; All video mode parameters
- Param_BW40: mov al,0 ; Colour-suppressed 40-column mode
- DB 3Dh ; Skip next two-byte instruction
- Param_CO40: mov al,1 ; Colour 40-column mode
- DB 3Dh ; Skip next two-byte instruction
- Param_BW80: mov al,2 ; Colour-suppressed 80-column mode
- DB 3Dh ; Skip next two-byte instruction
- Param_CO80: mov al,3 ; Colour 80-column mode (incl 43/50)
- DB 3Dh ; Skip next two-byte instruction
- Param_MONO: mov al,7 ; Monochrome video mode
-
- ; AL = desired video mode number - check that mode has not already been given
-
- xchg al,[Vid_Mode] ; Set video mode number
- inc al ; Make sure there wasn't one already
- jnz BadUsage1 ; If there was
-
- ; Check for optional number of lines
-
- cmp [BYTE si],"," ; Check for optional number of lines
- jne VidParam_Done ; If no error
- inc si ; Skip comma
- call ReadDecimal ; Get the number
- sub ax,25 ; Check for 25 lines
- jz LinesOK ; If so
- sub ax,43-25-1 ; Convert 43 to 1
- cmp ax,1 ; Check for it
- je LinesOK ; If correct
- sub ax,50-43-1 ; Convert 50 to 2
- cmp ax,2 ; Check for it
- je LinesOK ; If so
- jmp OutOfRange ; Go to out of range error stuff
- LinesOK: mov [Vid_Lines],al ; Store (0/1/2)
- test ax,ax ; Check for 25-line
- jz VidParam_Done ; If so, don't require CO80
- cmp [Vid_Mode],3 ; Only allow 43/50-line mode with CO80
- point bx,LinesVModeEM ; Prepare for error
- jne GoErrExit ; If error
- VidParam_Done: mov al,Mode_Vid ; Get mode
- jmp CheckMode ; Set command mode, check for conflict
- BadUsage1: jmp BadUsage
- ENDP VidParams
-
- ; Serial port parameter handler ------------------------------------------------
-
- PROC Param_Com near
- mov cx,4*256+1 ; Adjust by 1, check against 4
- call GetAdjCheck ; Get parameter, adjust and check
- xchg al,[Com_Port] ; Store, get old value
- inc al ; Check for multiple commands
- jz ComPortOK ; If alright
- jmp BadCmdMode ; If more than one port command
- ComPortOK: lodsb ; Get colon
- cmp al,":" ; Validate
- jne BadUsage1 ; If wrong
- call ReadDecimal ; Parse baud rate
- point bx,BaudTbl-4 ; Point to before baud rate table
- NextBaud: add bx,4 ; Bump pointer
- cmp ax,[bx] ; Does value match?
- ja NextBaud ; If not, keep looking
- je GotBaud ; If so
- BadBaudRate: point bx,BadBaudEM ; Scanned whole table - bad baud rate
- GoErrExit: jmp ErrExit ; Go to error exit
-
- GotBaud: mov ax,[bx+2] ; Get baud rate divisor value
- mov [Com_BRD],ax ; Store it
- call SkipComma ; Check for and skip a comma
- lodsb ; Get odd/even/none
- or al,00100000b ; Convert to lower case
- cmp al,"n" ; Check for none
- je GotParity ; If so
- cmp al,"o" ; Check for odd
- je SetParity ; If so
- cmp al,"e" ; Check for even
- jne BadUsage1 ; If bad syntax
- or [Com_LCR],00010000b ; Set even parity select
- SetParity: or [Com_LCR],00001000b ; Set parity enable
- GotParity: call SkipComma ; Skip next comma
- mov cx,4*256+5 ; Adjust by 5, check against 4
- call GetAdjCheck ; Get parameter, adjust and check it
- or [Com_LCR],al ; Combine into LCR value
- call SkipComma ; Skip next comma
- mov cx,2*256+1 ; Adjust by 1, check against 2
- call GetAdjCheck ; Get parameter, adjust and check it
- shl al,1 ; Shift bit
- shl al,1 ; to b2
- or [Com_LCR],al ; Combine into LCR value
- mov al,Mode_Com ; Get mode
- jmp CheckMode ; Set command mode, check for conflict
- ENDP Param_Com
-
- ; Parallel port parameter handler ----------------------------------------------
-
- PROC Param_LPT near
- mov cx,4*256+1 ; Adjust by 1, check against 4
- call GetAdjCheck ; Get parameter, adjust and check
- xchg al,[LPT_Port] ; Store, get old value
- inc al ; Check for multiple commands
- jnz BadCmdMode ; If more than one port command
- point di,LPTTable ; Point to subfunction table
- call StringLCComp ; Scan for subfunction
- jmp bx ; Go to appropriate handler or BadUsage
-
- LPT_Redirect: mov cx,4*256+1 ; Adjust by 1, check against 4
- call GetAdjCheck ; Get parameter, adjust and check it
- inc ax ; Convert 0-3 to 1-4, port num in b0-2
- DB 3Dh ; Skip next two-byte instruction
- LPT_RedNUL: mov al,7 ; Fake redirection to NUL as COM7
- DB 3Dh ; Skip next two-byte instruction
- LPT_Persist: mov al,10000000b ; Persistent
- DB 3Dh ; Skip next two-byte instruction
- LPT_Normal: mov al,0 ; 'Back to normal' action code
-
- GotLPTFunc: mov [LPT_Func],al ; Store LPT action code
- mov al,Mode_LPT ; Get command mode
- jmp SHORT CheckMode ; Set command mode, check for conflict
- ENDP Param_LPT
-
- ; Hard disk park parameter handler ---------------------------------------------
-
- Param_PARK: cmp [BYTE si],"," ; Check for optional idle timeout
- jne NoIdle ; If not
- inc si ; Skip comma
- mov cx,51*256 ; Limit it to 50 minutes
- call GetAdjCheck ; Get and check minutes
- mov dx,1092 ; Number of ticks in one minute (approx)
- mul dx ; Get number of ticks
- mov [Park_Ticks],ax ; Store
- cmp [BYTE si],":" ; Check for colon and seconds
- jne NoSeconds ; If not
- inc si ; Skip colon
- mov cx,60*256 ; Limit it to 59 seconds
- call GetAdjCheck ; Get and check seconds
- mov dx,18 ; Number of ticks in one second (approx)
- mul dx ; Get number of ticks
- add [Park_Ticks],ax ; Store
- NoSeconds: cmp [Park_Ticks],0 ; Check we got something
- jz OutOfRange ; If out of range
- NoIdle: mov al,Mode_Park ; Get mode
- jmp SHORT CheckMode ; Set command mode, check for conflict
-
- BadCmdMode: point bx,BadCmdModeEM ; Error - more than one command type
- jmp ErrExit ; Go to error exit stuff
-
- ; Typematic parameter handlers -------------------------------------------------
-
- Param_DELAY: mov cx,4*256+1 ; Adjust by 1, check against 4
- call GetAdjCheck ; Read, adjust and check parameter
- xchg al,[Type_Delay] ; Set it, get old value
- inc al ; Check for only DELAY= parameter
- jnz BadUsage2 ; If not
- jmp SHORT Typematic
-
- Param_RATE: mov cx,32*256+1 ; Adjust by 1, check against 32
- call GetAdjCheck ; Read, adjust and check parameter
- neg al ; Convert to negative
- add al,31 ; Convert 0-31 to 31-0
- xchg al,[Type_Rate] ; Set it, get old value
- inc al ; Check for only RATE= parameter
- jnz BadUsage2 ; If not
- jmp SHORT Typematic
-
- Param_LOCK: mov [Type_Lock],1 ; Set lock flag
- Typematic: mov al,Mode_Typematic ; Command mode
-
- ; Check and set command mode ---------------------------------------------------
-
- CheckMode: xchg al,[CmdMode] ; Set it
- test al,al ; Check that it was previously zero
- jz ARet1 ; If so
- cmp al,[CmdMode] ; Command mode same as before?
- je ARet1 ; If so
- jmp SHORT BadCmdMode ; If not, error!
- ARet1: ret
-
- BadUsage2: jmp BadUsage ; Go to bad usage exit point
-
- ; String matcher ---------------------------------------------------------------
-
- ; This function accepts a string at SI and a pointer to a table in DI, and
- ; scans the table for a matching string. Each entry in the table consists of
- ; a lower-case string, with a null-terminator, followed by a two-byte value
- ; that will be returned by this function if a match is found (successful).
- ; The end of the table is marked by a zero-length string. The strings in the
- ; table are in lower case and the case of the source string will be converted
- ; by ORing each character with 00100000b. Note that this will produce some
- ; unexpected effects on characters which are not alphabetic - for example, a
- ; space will be translated into a "0" (among others!)
-
- ; In: SI -> String to be matched
- ; DI -> Table of strings to match with, format as described above
- ; Out: CF = Success/failure: NC = Success, CY = Failure
- ; BX = Value from table if successful, otherwise points to BadUsage
- ; SI unchanged if unsuccessful, points past matched string if successful
- ; Note: ES and DS must be equal
-
- PROC StringLCComp near
- StrLCComp1: xor bx,bx ; Zero index
- StrLCComp2: mov al,[si+bx] ; Char from source string
- or al,00100000b ; Convert to lower case
- cmp al,[di+bx] ; Match?
- je StrLCComp4 ; If so
- xor al,al ; Null char
- StrLCComp3: scasb ; Check for terminator on table entry
- jnz StrLCComp3 ; If not yet
- inc di
- inc di ; Skip return parameter
- cmp [BYTE di],0 ; Scanned whole table?
- jnz StrLCComp1 ; If not, try next entry
- stc ; If so, set carry indicating no match
- point bx,BadUsage ; No match - set pointer to BadUsage
- ret
- StrLCComp4: inc bx ; Next position
- cmp [BYTE di+bx],0 ; Has whole parameter matched?
- jnz StrLCComp2 ; If not, keep checking
- add si,bx ; Skip matched string, clear carry
- mov bx,[di+bx+1] ; Get parameter from table
- ret ; Return carry clear - successful
- ENDP StringLCComp
-
- ; Decimal input ----------------------------------------------------------------
-
- ; This function calls ReadDecimal to parse a decimal formatted ASCII number
- ; at DS:SI, then adjusts the number by subtracting the value provided in CL
- ; and validates the result against the value provided in CH; if the value
- ; is greater than or equal to the value in CH, it aborts the program with the
- ; parameter out of range error message. It returns the result in AL.
- ; AX, BX, DX and SI are destroyed. The parameter before and after adjustment
- ; is not allowed to exceed 255.
-
- PROC GetAdjCheck near
- call ReadDecimal
- test ah,ah ; Greater than 255?
- jnz OutOfRange ; If so
- sub al,cl ; Adjust
- cmp al,ch ; Check against limit + 1
- jae OutOfRange ; If bad
- ret ; If alright
- OutOfRange: point bx,OutRangeEM ; If not, illegal number given
- jmp ErrExit ; Go to error exit stuff
- ENDP GetAdjCheck
-
- ; This function scans a number in ASCII format from the command tail and returns
- ; the binary equivalent of the number. It is limited to numbers in the range 0
- ; to 65535, and does not distinguish 'no number found' from 'zero found' (both
- ; return zero result). Before calling, SI must point to the first character to
- ; be scanned. On return, SI will point past the last valid character, i.e. to
- ; the character which terminated the evaluation, and AX will contain the result.
- ; The function destroys AX, BX, DX, and SI, and assumes that DF is clear.
-
- PROC ReadDecimal near
- xor bx,bx ; Clear result
- ReadNumLp: lodsb ; Get a character
- sub al,"0" ; Convert "0"-"9" to 0-9
- cmp al,10 ; Check for valid char
- jae ReadNumFin ; If not, terminator
- cbw ; Zero AH
- xchg ax,bx ; New digit to BL, old total to AX
- mov dx,10 ; Ten to unused register
- mul dx ; Multiply old value by ten
- add bx,ax ; Add to new digit
- jmp SHORT ReadNumLp ; Loop for more
- ReadNumFin: dec si ; Point to just past number
- xchg ax,bx ; Return value in AX
- ret ; Finished
- ENDP ReadDecimal
-
- ; Skip comma -------------------------------------------------------------------
-
- PROC SkipComma near
- cmp [BYTE si],"," ; Check for comma
- jne BadUsage2 ; If not
- inc si ; Skip it
- ret
- ENDP SkipComma
-
- ; Command mode handlers ========================================================
-
- ; After the command tail has been processed by the parameter handler functions,
- ; the appropriate command mode handler is called to perform the desired MODE
- ; operation. At this point, the command type has been determined, the command
- ; tail has been parsed and parameters have been processed and syntax-checked,
- ; and the appropriate variables have been set.
-
- ; Video command handler --------------------------------------------------------
-
- ; First, determine the hardware configuration. The following adapters will be
- ; supported: EGA or VGA, MDA/Hercules, CGA, and a system with both CGA and
- ; MDA/Hercules.
-
- PROC DoMode_Vid near
- mov ax,1A00h
- int 10h ; Get display combination code (EGA/VGA)
- cmp al,1Ah ; Will be equal if supported
- jne NoDCCFunc ; If unsupported
- push bx ; Keep inactive monitor parms
- xor bh,bh ; Zero hibyte
- cmp bl,13 ; Check for valid DCC
- jae BadDCC1 ; If bad
- mov al,[DCCTable+bx] ; Get flags for this display type
- or [VidHave],al ; Enable those features
- BadDCC1: pop bx ; Restore inactive monitor data in BH
- mov bl,bh ; To BL
- xor bh,bh ; Zero hibyte again
- cmp bl,13 ; Check for valid DCC
- jae BadDCC2 ; If bad
- mov al,[DCCTable+bx] ; Get flags for this display type
- or [VidHave],al ; Enable those features
- BadDCC2: jmp SHORT GotDisplay ; We know what displays are present
-
- ; Function 1A00h (get display combination) did not work - we have a CGA, MDA
- ; or Hercules, or old EGA card. Now try function 12h, subfunction 10h (in BL).
- ; This returns the amount of video RAM, default mode (colour/monochrome) and
- ; feature and configuration bits, for the EGA and VGA.
-
- NoDCCFunc: mov ax,1200h ; Function 12h
- mov bx,0FF10h ; Subfunction code in BL
- int 10h ; Get some EGA/VGA information
- inc bh ; See whether anything happened
- jz NoEGAFunc ; If nothing happened
- or [VidHave],Vid_43+Vid_3+Vid_7 ; 43-line, colour and mono
- jmp SHORT GotDisplay ; We know what display is present
-
- ; Oh dear, this poor user has a MDA/Hercules and/or CGA card! Look for a CRTC
- ; at the two standard addresses - 3B4h for MDA/Hercules, and 3D4h for CGA.
-
- NoEGAFunc: mov dx,3B4h ; Try for monochrome first
- call TestForCRTC ; Is there a CRTC there?
- jne NoCRTC1 ; If not
- or [VidHave],Vid_7 ; If so, we can support monochrome mode
- NoCRTC1: mov dx,3D4h ; Try for colour
- call TestForCRTC ; Is there a CRTC there?
- jne NoCRTC2 ; If not
- or [VidHave],Vid_3 ; If so, we can support colour modes
- NoCRTC2:
-
- ; We now know which video modes are allowed - this is determined by VidHave.
- ; Now make sure that the desired mode is allowed. First, check for 43-line
- ; and 50-line modes specified and reject the request if not appropriate.
-
- GotDisplay: cmp [Vid_Lines],1 ; 43-line or 50-line specified?
- jb Not43_50 ; If not
-
- ; Either 43-line or 50-line mode was specified - therefore it must be
- ; standard colour mode (mode 3, CO80). Now check that 43-line or 50-line
- ; mode is allowed on this machine.
-
- ja Test_50 ; If 50-line
- test [VidHave],Vid_43 ; Is 43-line - is it allowed?
- jnz VModeOK ; If so, continue
-
- Test_50: test [VidHave],Vid_50 ; Is 50-line - is it allowed?
- jnz VModeOK ; If so
-
- BadVideo: point bx,UnsuppVidEM ; Error - video mode is not supported
- jmp ErrExit ; Go to standard error exit stuff
-
- Not43_50: cmp [Vid_Mode],4 ; Check for colour mode specified
- jae NotColour ; If not
- test [VidHave],Vid_3 ; Are colour modes allowed?
- jz BadVideo ; If not
- jmp SHORT VModeOK ; Continue
-
- NotColour: test [VidHave],Vid_7 ; Is monochrome mode allowed?
- jz BadVideo ; If not
-
- ; If the system does not have EGA/VGA (i.e. the Vid_43 bit in VidHave is clear)
- ; it may be necessary to modify the equipment list byte at 0000:0410 according
- ; to the type of video mode required.
-
- VModeOK: test [VidHave],Vid_43+Vid_50 ; Have EGA or better?
- jnz LeaveEquip ; If so
-
- xor ax,ax
- mov ds,ax ; Address BDA with DS
- ASSUME ds:nothing
- mov al,[ds:410h] ; Get equipment list byte
- and al,11001111b ; Mask off adapter type bits
- or al,00100000b ; Set bit for CGA
- cmp [Vid_Mode],7 ; Check for mono
- jne NoEquipMono ; If not
- or al,00110000b ; Set bits for MONO
- NoEquipMono: mov [ds:410h],al ; Store it back
- push cs
- pop ds
- ASSUME ds:ComFile
-
- ; If this is a VGA system, we must select the number of scan lines first,
- ; using video BIOS function 12h (in AH) subfunction 30h (BL). This accepts
- ; a number in AL which determines the number of scan lines. This number
- ; will be 1 for 43-line mode, to select 350 scan lines, or 2 for 25-line
- ; and 50-line modes, to select 400 scan lines.
- ; There are three possible values for Vid_Lines. They map as follows:
- ;
- ; Vid_Lines Char lines Value in AL Scan lines
- ; 0 25 2 400
- ; 1 43 1 350
- ; 2 50 2 400
-
- LeaveEquip: cmp [Vid_Mode],3 ; Check for CO80 specified
- jne NoSetScanlines ; If not
- test [VidHave],Vid_50 ; Scan lines function supported?
- jz NoSetScanlines ; If not
-
- mov ah,12h ; Function for setting scan lines
- mov bl,30h ; For alphanumeric mode
- mov al,[Vid_Lines] ; 43-line = 1 = 350, 50-line = 2 = 400
- shr al,1 ; Set carry if 43-line
- mov al,2 ; Prepare for 25-line or 50-line
- sbb al,0 ; Decrement if it was 43-line
- int 10h ; Set number of scan lines
-
- ; Finally, select the specified mode. If 43-line or 50-line mode, load the
- ; 8x8 ROM character set.
-
- NoSetScanlines: mov ax,[WORD Vid_Mode] ; Get the specified mode
- xor bx,bx ; Page zero
- int 10h ; Set it
- cmp [Vid_Lines],0 ; 43-line or 50-line?
- jz NoLoad8x8 ; If not
- mov ax,1112h ; Load 8x8 ROM character set
- int 10h ; Do it
-
- NoLoad8x8: jmp Exit0 ; Finished
-
- ENDP DoMode_Vid
-
- ; The following function tries to determine whether a CRTC (CRT controller
- ; chip used in video cards) exists at the I/O base address specified in DX
- ; on entry. If a CRTC is found, it returns with the zero flag set, otherwise
- ; it returns with the zero flag clear.
-
- PROC TestForCRTC near
- mov al,0Fh ; Use cursor location low byte
- cli ; No nasty interrupts please
- out dx,al ; Select the register
- inc dx ; Point to the register
- in al,dx ; Get current value
- jmp SHORT $+2 ; Short delay
- jmp SHORT $+2 ; And another
- mov ah,al ; Save it
- not al ; Reverse all bits
- out dx,al ; Stick it back
- jmp SHORT $+2 ; Short delay
- jmp SHORT $+2 ; And another
- in al,dx ; Read the register again
- not al ; Reverse all bits
- jmp SHORT $+2 ; Short delay
- jmp SHORT $+2 ; And another
- out dx,al ; Put it back
- sti ; Interrupts back on
- cmp al,ah ; Same?
- ret
- ENDP TestForCRTC
-
- ; Serial port command handler --------------------------------------------------
-
- ; The functions here are simple - program the specified serial port with the
- ; specified line communication parameters (baud rate, parity, data bits, and
- ; stop bits).
-
- PROC DoMode_Com near
- xor bx,bx
- mov es,bx ; Address BIOS data area
- mov bx,[WORD Com_Port] ; Get port number
- shl bx,1 ; Double for word sized I/O addresses
- mov dx,[es:bx+400h] ; Get I/O address of specified port
- test dx,dx ; Does the port exist?
- point bx,NoSuchSPortEM ; Prepare for no
- jz Com_Error ; If it doesn't exist, abort with error
- push dx ; Keep base pointer
- add dx,3 ; Point to LCR
- mov al,10000000b ; Set DLAB (divisor latch access bit)
- out dx,al ; Set the LCR value
- pop dx ; Point back to lobyte of divisor
- mov ax,[Com_BRD] ; Get baud rate divisor
- out dx,al ; Set the lobyte
- inc dx ; Point to hibyte
- mov al,ah ; Get hibyte
- out dx,al ; Set the hibyte
- inc dx
- inc dx ; Point to the LCR again
- mov al,[Com_LCR] ; Get specified LCR value
- out dx,al ; Set it
-
- point dx,SerialSetMsg ; Message
- jmp MessageExit0 ; Issue message and terminate
-
- Com_Error: jmp ErrExit ; Go to error exit handler
- ENDP DoMode_Com
-
- ; Parallel port command handler ------------------------------------------------
-
- ; There are four parallel port commands - the LPTn:P command which specifies
- ; persistent behaviour, i.e. infinite timeout, on the specified parallel port,
- ; the LPTn:=COMx command, which redirects output from the specified parallel
- ; port to the specified serial port, the LPTn:=NUL command, which redirects
- ; output from the specified parallel port to nowhere, and the LPTn: command
- ; which removes the infinite timeout (if active) and the parallel port
- ; redirection (if active) for the specified parallel port.
- ; Note - the specified parallel port may be LPT1 through LPT4, and MODE does
- ; not ensure that the port physically exists. This allows (for example) LPT2
- ; to be redirected to a serial port when only LPT1 is present, giving an
- ; "extra" addressable port. However, if a parallel port is redirected to a
- ; serial port, MODE does check that the serial port exists. Here is the
- ; behaviour table:
- ;
- ; Func Inst? Hook? Action Message
- ;
- ; Norm N N/A None Port set to normal
- ; Norm Y N Set param in resident copy Port set to normal
- ; Norm Y Y Set param in resident copy Port set to normal
- ; Pers N N/A Hook this copy, go resident Port state, resident
- ; Pers Y N Hook resident copy, set param Port state
- ; Pers Y Y Set param in resident copy Port state
- ; Redr N N/A Hook this copy, go resident Port state, resident
- ; Redr Y N Hook resident copy, set param Port state, resident
- ; Redr Y Y Set param in resident copy Port state, resident
- ;
- ; Notes:
- ;
- ; Some of the messages are a bit misleading; this is due to the great number
- ; of possible combinations of installed/not installed, hooked/not hooked,
- ; existing port mode, new port mode, and command specified. For example, if
- ; MODE is not installed, or if the installed copy is not hooked into the
- ; printer function interrupt, and the LPTn: command (return to normal) is
- ; issued, MODE says that the port has been set for normal operation, even
- ; though it was never set to anything else. Also, if the mode is changed
- ; with any of the three command types, MODE does not take notice of the
- ; previous mode. Thus if you entered MODE LPT1:P twice, it would say the
- ; same thing both times - that the port is now configured for persistent
- ; retries - even though the second time it was already configured for
- ; persistent retries before MODE was invoked.
- ;
- ; Redirection to NUL is faked as redirection to COM7.
-
- PROC DoMode_LPT near
- mov bx,[WORD LPT_Port] ; Get port number
- push ds
- pop es ; Will address vars with ES
- cmp [Already],0 ; Is there an installed copy?
- jz NotInst ; If not
- mov es,[Already] ; Get resident copy
-
- ; Check for set-normal command
-
- NotInst: cmp [LPT_Func],0 ; Check for set-to-normal command
- jnz NotSetNormal ; If not
- mov [es:LPT_Ctrl+bx],0 ; Remove special settings
- add [SetNormalLPT],bl ; Set appropriate port number
- point dx,SetNormalMsg ; Message
- GoMsgExit0: jmp MessageExit0 ; Exit with message, errorlevel zero
-
- ; Was not LPTn: command - must be LPTn:P or LPTn:=COMx/NUL. If it's the
- ; latter, now check that the specified serial port (to be redirected to)
- ; exists. Treat a port value of 7 specially - it indicates redirection to
- ; NUL.
-
- NotSetNormal: js NoCheckRedir ; If redirect command
-
- mov bx,[WORD LPT_Func] ; Get serial port to redirect _to_
- cmp bl,7 ; Is it COM7?
- je NoCheckRedir ; If so, don't check!
- shl bx,1 ; Double for word sized I/O addresses
- xor ax,ax
- mov ds,ax ; Address BIOS data area with DS
- ASSUME ds:nothing
- mov dx,[ds:bx+3FEh] ; Get I/O address of serial port
- push cs
- pop ds ; DS back to normal
- ASSUME ds:ComFile
- test dx,dx ; Does the port exist?
- point bx,NoSuchSPortEM ; Prepare for no
- jz Com_Error ; If it doesn't exist, abort with error
-
- ; Was not LPTn: command - must be LPTn:P or LPTn:=COMx/NUL. For these
- ; commands, MODE must either already _be_ resident, or must _go_ resident.
- ; Check for installed copy and address either this copy, or the resident copy.
- ; Have ES pointing either to local segment, or to installed copy (if there
- ; is an installed copy).
-
- NoCheckRedir: mov ax,[es:Old17Ofs] ; Get offset of chain vector
- or ax,[es:Old17Seg] ; Has vector been hooked?
- jnz WasHooked ; If so
-
- ; Either this copy (which will go resident), or the resident copy, did not have
- ; int 17h hooked - so hook it now.
-
- push es
- pop ds
- ASSUME ds:nothing ; DS now addresses whichever copy
- mov ax,3517h
- int 21h ; Get int 17h vector
- mov [ds:Old17Ofs],bx
- mov [ds:Old17Seg],es ; Store it
- point dx,New17
- mov ax,2517h
- int 21h ; Set new vector
-
- push ds
- pop es ; ES back to this or resident copy
- push cs
- pop ds
- ASSUME ds:ComFile ; DS back to normal
-
- ; Either this copy, or the resident copy, is hooked in to int 17h and needs
- ; to have the appropriate parallel port parameter set. ES addresses whichever
- ; copy is or will be installed.
-
- WasHooked: mov bx,[WORD LPT_Port] ; Get port number again
- mov al,[LPT_Func] ; Get command again
- test al,10000000b ; Redirect or persistent?
- js SetPersist ; If persistent
- and [es:LPT_Ctrl+bx],11111000b ; Mask off old redirection
- SetPersist: or [es:LPT_Ctrl+bx],al ; Set new redirection or persistent
-
- ; Issue the appropriate message - either port set for infinite timeout or
- ; port redirected
-
- mov al,[LPT_Func] ; Get function
- test al,al ; Which is it?
- point dx,SetPersistMsg ; Prepare for set to persistent
- js GotPersRedir ; If so
- cmp al,7 ; Check for redirection to NUL
- point dx,SetRedNULMsg ; Prepare for it
- je GotPersRedir ; If it was
- point dx,SetRedirMsg ; Set to redirected
- add [SetRedirCOM],al ; Set serial port number in message
-
- ; Now have message, so do we need to install or not?
-
- GotPersRedir: cmp [Already],0 ; Already installed?
- jnz GoMsgExit0 ; If so, just issue the message
- mov ah,9
- int 21h ; Display message
- point dx,ResInstallMsg ; Inform user MODE is now resident
- jmp MessageTSR0 ; Issue message and TSR
- ENDP DoMode_LPT
-
- ; Park command handler ---------------------------------------------------------
-
- ; The park command is either immediate (i.e. no minutes:seconds value provided)
- ; or a timed park. First, check that at least one hard disk is present. If
- ; the park is immediate, just park the hard drives and issue a message then
- ; wait for a reboot, etc. In the second case, we either go resident with the
- ; timer and disk function interrupts hooked, or update the timed park parameter
- ; in the resident copy.
-
- PROC DoMode_Park near
- mov dl,80h ; Test first hard disk for Ready
- mov ah,10h
- int 13h
- point bx,NoHDiskEM ; Prepare for error
- jnc HaveHDisk ; If alright
- jmp ErrExit ; If no hard disk present!
-
- HaveHDisk: mov ax,[Park_Ticks] ; Get number of ticks specified
- test ax,ax ; Any park time specified?
- jnz TimedPark ; If so
-
- ; Immediate park was requested - ignore any resident copy, if present. Get
- ; end cylinder for drive 0 and park it, check for existence of a second drive
- ; and if it exists, do the same
-
- mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov dl,80h ; Reinstate drive number
- mov ah,0Ch ; Seek function
- int 13h ; Do it
- point si,Parked1Msg ; Prepare for only one hard disk parked
-
- inc dx ; Test second hard disk for Ready
- mov ah,10h
- int 13h
- jc NoDisk1 ; If no second hard disk
-
- mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov dl,80h ; Reinstate drive number
- mov ah,0Ch ; Seek function
- int 13h ; Do it
- point si,Parked2Msg ; Two hard drives were parked
-
- ; Display informative message and wait
-
- NoDisk1: mov dx,si ; Get message
- mov ah,9 ; Display message function
- int 21h ; Call DOS
-
- ParkLoop: mov ah,8 ; Keyboard input, no echo, break active
- int 21h ; Call DOS
- jmp SHORT ParkLoop ; Forever
-
- ; Timed park function has been requested - check whether a resident copy is
- ; present. If not, initialise the timeout counters in this copy, store the
- ; drive parameters, and go resident with the appropriate message. If there
- ; is a resident copy present, update the resident parameters, including
- ; resetting the two actual timeout counters, and check whether the resident
- ; copy has its timed park function active. This is true if the int 13h chain
- ; vector in the resident copy is nonzero. If so, exit with the message that
- ; the resident parameters have been modified. If not, determine the drive
- ; parameters and set them in the resident copy, hook the vectors into the
- ; resident copy and exit with the message that the timed park function has
- ; now been enabled in the resident copy.
-
- TimedPark: cmp [Already],0 ; Already have a resident copy?
- jnz WasAlready4 ; If so
-
- ; Timed park was requested and there is no resident copy - set some parameters
- ; and go resident
-
- mov [ParkDrive0],ax ; Initialise timeout
- mov [ParkDrive1],ax ; for both drives
-
- mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov [MaxCylinder0],cx ; Store number of last cylinder
- point si,ParkInst1Msg ; Prepare for only one hard disk found
-
- mov dl,81h ; Test second hard disk for Ready
- mov ah,10h
- int 13h
- jc NoDisk2 ; If no second hard disk
- mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov [MaxCylinder1],cx ; Store number of last cylinder
- point si,ParkInst2Msg ; Point to message for two drives
-
- ; Hook interrupts 8 and 13h
-
- NoDisk2: mov ax,3508h
- int 21h ; Get int 8 vector
- mov [Old08Ofs],bx
- mov [Old08Seg],es ; Store it
- point dx,New08
- mov ax,2508h
- int 21h ; Set new vector
-
- mov ax,3513h
- int 21h ; Get int 13h vector
- mov [Old13Ofs],bx
- mov [Old13Seg],es ; Store it
- point dx,New13
- mov ax,2513h
- int 21h ; Set new vector
-
- mov dx,si ; Appropriate message to DX
- jmp MessageTSR0 ; Issue message and go resident
-
- ; A resident copy exists
-
- WasAlready4: mov es,[Already] ; Get segment of installed copy
- mov [es:Park_Ticks],ax ; Update resident timeout parameter
- mov [es:ParkDrive0],ax ; Reset resident timeout counter
- mov [es:ParkDrive1],ax ; for both drives
-
- mov ax,[es:Old13Ofs] ; Get chain vector offset
- or ax,[es:Old13Seg] ; Check whether park function active
-
- jz ParkWasNot ; If not
-
- point dx,ParkResMsg ; If so, resident parameter modified
- jmp MessageExit0 ; Display message and terminate
-
- ; An installed copy is present but it does not have its park function enabled
-
- ParkWasNot: mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov [es:MaxCylinder0],cx ; Store number of last cylinder
- point si,ParkEnab1Msg ; Prepare for only one hard disk found
-
- mov dl,81h ; Test second hard disk for Ready
- mov ah,10h
- int 13h
- jc NoDisk3 ; If no second hard disk
- mov ah,8 ; Get drive parameters function
- int 13h ; Do it
- mov [MaxCylinder1],cx ; Store number of last cylinder
- point si,ParkEnab2Msg ; Point to message for two drives
-
- ; Hook interrupts 8 and 13h to resident copy
-
- NoDisk3: push es
- pop ds ; Set DS to resident copy
-
- ASSUME ds:nothing
-
- mov ax,3508h
- int 21h ; Get int 8 vector
- mov [ds:Old08Ofs],bx
- mov [ds:Old08Seg],es ; Store it
- point dx,New08
- mov ax,2508h
- int 21h ; Set new vector
-
- mov ax,3513h
- int 21h ; Get int 13h vector
- mov [ds:Old13Ofs],bx
- mov [ds:Old13Seg],es ; Store it
- point dx,New13
- mov ax,2513h
- int 21h ; Set new vector
-
- push cs
- pop ds ; DS back to local segment
-
- ASSUME ds:ComFile
-
- mov dx,si ; Appropriate message to DX
- jmp MessageExit0 ; Issue message and exit
- ENDP DoMode_Park
-
- ; Typematic command handler ----------------------------------------------------
-
- ; Typematic parameters were specified. First, check that both delay and rate
- ; were given - both are required. Then check whether a resident copy exists,
- ; and if so, copy the typematic settings into the resident copy, and check
- ; whether the resident copy has its typematic lock function enabled (this is
- ; true if the chain vector for int 16 in the resident copy is nonzero). Also
- ; check the lock flag in the transient copy. Behave according to the following
- ; table:
- ;
- ; Res? Locked? LOCK? Action Message(s)
- ;
- ; N N/A N Call BIOS Set
- ; N N/A Y Hook, go resident Locked, installed
- ; Y N N Update TSR, call BIOS Set
- ; Y N Y Update TSR, hook to resident Locked parms set
- ; Y Y N/A Update TSR, call BIOS Locked parms set
-
- PROC DoMode_Typematic near
- point bx,TypeParamEM ; Prepare for parameter error
- mov ax,[Type_Parm] ; Get delay and rate
- cmp ah,0FFh ; Make sure a delay was entered
- je Type_Error ; If not
- cmp al,0FFh ; Make sure a rate was entered
- jne TypeParamsOK ; If alright
-
- Type_Error: jmp ErrExit ; Go to error exit handler
-
- TypeParamsOK: cmp [Already],0 ; Are we already installed?
- jnz WasAlready5 ; If so
-
- ; Not resident - if no lock flag, just call the BIOS function and exit with
- ; the message 'typematic parameters set'. If lock flag is set, hook int 16h
- ; into transient copy and go resident, with an appropriate message.
-
- cmp [Type_Lock],0 ; Lock specified?
- jnz Type_IsLock ; If so
-
- TypeSet: point dx,TypeSetMsg ; Message
- jmp SetTypeExit ; Call BIOS, display message, and exit
-
- ; Was not resident but lock was specified - hook vector and go resident
-
- Type_IsLock: mov ax,3516h
- int 21h ; Get int 16h vector
- mov [Old16Ofs],bx
- mov [Old16Seg],es ; Store it
- point dx,New16
- mov ax,2516h
- int 21h ; Set new vector
-
- mov ax,305h ; Function to set typematic
- int 16h ; Set it - no parameter needed
- ; because lock code is resident
-
- point dx,LockInstMsg ; Message
-
- ; Entry point for use by other handlers - at this point, DX points to the
- ; message to be issued. The message will be sent via DOS function 9, and
- ; the program will hook int 2Fh (TSR locate vector), deallocate its copy of
- ; the environment, and terminate and stay resident with errorlevel zero.
-
- MessageTSR0: mov ah,9
- int 21h ; Display message
-
- TSR0: mov ax,352Fh
- int 21h ; Get int 2Fh vector
- mov [Old2FOfs],bx
- mov [Old2FSeg],es ; Store it
- point dx,New2F
- mov ax,252Fh
- int 21h ; Set new vector
-
- mov es,[ComFile:2Ch] ; Get segment of environment block
- mov ah,49h ; Free memory block function
- int 21h ; Deallocate our copy of environment
-
- mov dx,TSRParas ; Number of paragraphs to leave resident
- mov ax,3100h
- int 21h ; Go resident
-
- ; Resident copy was found - update resident typematic parameters, see whether
- ; resident copy has typematic lock enabled and whether lock was requested then
- ; behave according to above table.
-
- WasAlready5: mov es,[Already] ; Get resident segment
- mov [WORD es:Type_Parm],ax ; Update resident values
-
- mov ax,[es:Old16Ofs] ; Get old int 16h vector offset
- or ax,[es:Old16Seg] ; Is resident copy in locked mode?
- jnz Res_Locked ; If so
-
- ; Resident copy is not locked - did user specify locking?
-
- cmp [Type_Lock],0 ; Lock requested?
- jz TypeSet ; If not, just set typematic and exit
-
- ; Resident copy is not locked but user specified locking
-
- push es
- pop ds ; DS to resident copy
-
- ASSUME ds:nothing
-
- mov ax,3516h
- int 21h ; Get int 16h vector
- mov [ds:Old16Ofs],bx
- mov [ds:Old16Seg],es ; Store it in resident copy
- point dx,New16
- mov ax,2516h
- int 21h ; Set new vector
- push cs
- pop ds ; Restore to transient copy
-
- ASSUME ds:ComFile
-
- point dx,ResLockedMsg ; Message
- jmp SHORT SetTypeExit
-
- ; Resident copy was locked
-
- Res_Locked: point dx,TResUpdateMsg ; Resident locked parameters updated
-
- ; At this point - DX points to the message to be displayed. Get the typematic
- ; parameters and call the BIOS (this may actually set typematic, or may just
- ; cause the locked parameters to be set, depending on whether there is a
- ; hooked copy of MODE), then display the specified message and terminate
- ; with errorlevel zero.
-
- SetTypeExit: mov bx,[Type_Parm] ; Parameters to BX
- mov ax,305h ; Function to set typematic
- int 16h ; Set it
-
- ; Entry point for use by other handlers - at this point, DX points to the
- ; message to be issued. The message will be sent via DOS function 9, and
- ; the program will terminate and return to DOS with errorlevel zero.
-
- MessageExit0: mov ah,9 ; Display message
- int 21h
- Exit0: mov ax,4C00h ; Terminate with errorlevel zero
- int 21h
- ENDP DoMode_Typematic
-
- ; Working stack area -----------------------------------------------------------
-
- ALIGN 2
-
- WorkStack DB 512 DUP(?) ; Working stack
- WorkStackTop = $
-
- ; End of transient portion -----------------------------------------------------
-
- MASM
- FreeSpace = $
- MemParas = (OFFSET (FreeSpace-@curseg+15) SHR 4)
- prexp <Transient size:> %(MemParas SHL 4) < bytes>
- IDEAL
-
- ENDS ComFile
- END Main
-
- ;-------------------------------------------------------------------------------