;STICK.COM for the IBM Personal Computer - 1987 by Jeff Prosise
;     [d:][path]STICK [/L+|-] [/E+|-] [/B+|-] [/C-| fg bg]

bios_data     segment at 40h

              org 60h
bios_cursor   dw ?                          ;cursor mode
              org 63h
addr_6845     dw ?                          ;CRT Controller base address
              org 87h
ega_info      db ?                          ;EGA info byte

bios_data     ends

;======================================================================
code          segment para public 'code'
              assume cs:code
              org 100h

begin:        jmp stick

copyright     db "STICK 1.0 (c) 1987 Ziff Communications Co.",13,10,"$",1Ah
author        db "Jeff Prosise"

locking       db 0FFh                       ;state of cursor locking
emulation     db 0FFh                       ;state of EGA cursor emulation
foreground    db 0FFh                       ;selected foreground color
background    db 0                          ;selected background color

cursor_mode   dw 0                          ;desired cursor definition
adapter       db 2                          ;0=MDA or CGA, 1=EGA, 2=VGA
old_video     label dword
old10h        dw 0,0                        ;interrupt 10h vector

;-----------------------------------------------------------------------------
;VideoInt intercepts calls to interrupt 10h.
;-----------------------------------------------------------------------------
VideoInt      proc near

              sti                           ;enable interrupts
              cmp ah,6                      ;check for scroll function
              jb noscroll
              cmp ah,7
              ja pass
;
;Check the color selection for screen clear if color locking is on.
;
              or al,al                      ;AL = 0 (clear screen)?
              jne pass                      ;no, then pass thru to BIOS
              test cs:[foreground],80h      ;color locking active?
              jnz pass                      ;no, then pass thru to BIOS
              cmp bh,7                      ;white-on-black?
              jne pass                      ;no, then let it go
              mov bh,cs:[background]        ;reset attribute in BH
              push cx
              mov cl,4
              shl bh,cl
              pop cx
              or bh,cs:[foreground]
pass:         jmp old_video                 ;pass modified parm to BIOS
;
;See if the cursor mode is to be modified.
;
noscroll:     cmp ah,1                      ;cursor mode function?
              jne pass                      ;no, then pass to BIOS
              test ch,20h                   ;call to blank the cursor?
              jnz pass                      ;yes, then pass to BIOS
;
;Alter the cursor definition in CX if cursor locking is on.
;
              cmp cs:[locking],0FFh         ;locking function active?
              je nolock                     ;no, then check emulation function
              push ax                       ;save registers
              push cx
              push dx
              push es
              mov cx,cs:[cursor_mode]       ;get new cursor value
              call SetCursorMode
              pop es                        ;restore registers
              pop dx
              pop cx
              pop ax
              iret
;
;See if emulation should be performed.
;
nolock:       cmp cs:[emulation],0FFh       ;emulation function active?
              je pass                       ;no, then pass control to BIOS
              cmp cs:[adapter],1            ;EGA installed?
              jne pass                      ;no, then pass control to BIOS
              push ax                       ;save registers
              push es
              mov ax,bios_data              ;make sure EGA is active
              mov es,ax
        assume es:bios_data
              test es:[ega_info],8
              jnz exit                      ;exit if it's not
              test es:[ega_info],1          ;emulation bit set?
              jnz exit                      ;yes, then exit
              push bx                       ;save remaining registers
              push cx
              push dx
;
;Determine whether or not this call was intended for a CGA.
;
              cmp cl,7                      ;CGA-type cursor?
              ja setmode                    ;no, then don't alter values
              or cl,cl                      ;EGA ending line?
              je setmode                    ;yes, then don't touch it
;
;Scale the starting and ending scan lines.
;
              mov bx,cx                     ;transfer cursor mode to BX
              mov ax,1130h                  ;determine bytes per character
              int 10h                       ;points in CX
              mov al,cl                     ;transfer points to al
              dec al                        ;determine last scan line
              sub al,bl                     ;calculate adjustment value
              or bh,bh                      ;adjust starting scan line?
              je endline                    ;not if it's zero
              add bh,al                     ;adjust it
endline:      add bl,al                     ;scale ending line
              cmp bx,0C0Dh                  ;normal EGA underline cursor?
              jne skip
              mov bx,0B0Ch                  ;yes, then raise it a line
skip:         inc bl                        ;adjust ending line for EGA
              cmp bl,cl                     ;wrap around if necessary
              jne nowrap
              xor bl,bl
nowrap:       or bx,bx                      ;full height cursor?
              jne notfull
              mov bl,1Eh                    ;adjust for full height
notfull:      mov cx,bx                     ;transfer value back to CX
;
;Set the cursor and exit.
;
setmode:      call SetCursorMode            ;set the cursor
              pop dx                        ;restore registers and exit
              pop cx
              pop bx
              pop es
              pop ax
              iret
;
;Exit to the BIOS interrupt handling code.
;
exit:         pop es                        ;exit to BIOS
              pop ax
              jmp cs:old_video
VideoInt      endp

;-----------------------------------------------------------------------------
;SetCursorMode sets the cursor to the scan lines indicated in CX.
;-----------------------------------------------------------------------------
SetCursorMode proc near
              mov ax,bios_data              ;address BIOS data area with ES
              mov es,ax
        assume es:bios_data
              mov es:[bios_cursor],cx       ;store cursor mode
              mov dx,es:[addr_6845]         ;get CRTC address
              mov al,10                     ;out CH and CL to cursor registers
              out dx,al
              inc dx
              mov al,ch
              out dx,al
              dec dx
              mov al,11
              out dx,al
              inc dx
              mov al,cl
              out dx,al
              ret
SetCursorMode endp

lastbyte      equ $

;-----------------------------------------------------------------------------
;Stick routine receives control when the program is run.
;-----------------------------------------------------------------------------
line1         db 13,10,"Cursor Locking:      $"
line2         db       "Color Locking:       $"
line3         db       "Emulation Mode:      $"
line4         db       "EGA Emulation Bit:   $"

errmsg        db 13,10,"Usage: [d:][path]STICK [/L+|-] [/E+|-] "
              db "[/B+|-] [/C-| fg bg]",13,10,"$"
on            db "On",13,10,"$"
off           db "Off",13,10,"$"
bitvalue      db "0",13,10,"$"

stick         proc near
              assume cs:code,ds:code,es:code,ss:code
;
;Determine what type of video adapter is installed.
;
              mov ax,1A00h                  ;look for a VGA
              int 10h
              cmp al,1Ah
              jne check_ega                 ;branch if not found
              cmp bl,7                      ;make sure it's a VGA
              je search
              cmp bl,8
              je search
check_ega:    dec adapter                   ;check for an EGA
              mov ah,12h
              mov bl,10h
              int 10h
              cmp bl,10h
              jne search                    ;branch if EGA is found
              dec adapter                   ;set ADAPTER for CGA or MDA
;
;See if the resident portion of the program is already installed.
;
              assume es:nothing
search:       cld                           ;string moves forward
              mov word ptr [begin],0        ;zero first two bytes of signature
              xor bx,bx                     ;initialize search segment
              mov ax,cs                     ;record current segment
nextseg:      inc bx                        ;increment search index
              mov es,bx
              cmp ax,bx                     ;reached the current segment?
              je endloop                    ;yes, then it's not installed
              mov si,offset begin           ;look for program signature
              mov di,si
              mov cx,16
              repe cmpsb
              jne nextseg                   ;loop if search failed
;
;Check the command line for entries.
;
endloop:      mov si,81h                    ;point DS:SI to command line
              cmp byte ptr [si-1],2         ;any parameters entered?
              jb noparms                    ;no, then branch

              call FindParm                 ;look for a parameter
              jnc getparm                   ;branch if one is found
;
;No parameters were entered.  Display current switch settings.
;
noparms:      mov ah,9                      ;display cursor lock state
              mov dx,offset line1
              int 21h
              mov di,offset locking
              call ShowState

              mov ah,9                      ;display color lock state
              mov dx,offset line2
              int 21h
              mov di,offset foreground
              call ShowState
              cmp adapter,1                 ;EGA installed?
              jne terminate                 ;no, then exit now

              mov ah,9                      ;display cursor emulation state
              mov dx,offset line3
              int 21h
              mov di,offset emulation
              call ShowState

              mov ah,9                      ;display emulation bit setting
              mov dx,offset line4
              int 21h
              mov ax,bios_data              ;address BIOS data area with ES
              mov es,ax
              test es:[ega_info],1          ;emulation bit set?
              jz bitclear                   ;no, then branch
              inc bitvalue                  ;modify text string
bitclear:     mov ah,9
              mov dx,offset bitvalue
              int 21h

terminate:    mov ax,4C00h                  ;exit with ERRORLEVEL = 0
              int 21h
;
;Parse the remainder of the command line for parameters.
;
getparm:      cmp al,"/"                    ;slash character?
              jne badparm                   ;no, then signal error
              lodsb                         ;get current character
              and al,0DFh                   ;capitalize it
              cmp al,"L"                    ;Lock parameter?
              jne get1
              jmp DoLock
get1:         cmp al,"C"                    ;Color parameter?
              jne get2
              jmp DoColor
get2:         cmp al,"E"                    ;Emulation parameter?
              jne get3
              jmp DoEmul
get3:         cmp al,"B"                    ;Bit parameter?
              jne badparm                   ;no, then it's invalid
              jmp DoBit
continue:     call FindParm                 ;look for another entry
              jnc getparm                   ;loop back if one is found
;
;Exit and install resident code if it isn't already installed.
;
              mov ax,cs                     ;already installed?
              mov bx,es
              cmp ax,bx
              je install                    ;no, then install it
              mov ax,4C00h                  ;exit with ERRORLEVEL = 0
              int 21h

install:      mov ax,3510h                  ;save and replace int 10h vector
              int 21h
              mov old10h,bx
              mov old10h[2],es

              mov ax,2510h
              mov dx,offset VideoInt
              int 21h

              mov ah,9
              mov dx,offset copyright
              int 21h

              mov ax,word ptr ds:[2ch]
              mov es,ax
              mov ah,49h
              int 21h

              mov ax,3100h                  ;terminate-but-stay-resident
              mov dx,(offset lastbyte - offset code + 15) shr 4
              int 21h
;
;An invalid entry was encountered.  Display error message and exit.
;
badparm:      mov ah,9                      ;print error message
              mov dx,offset errmsg
              int 21h
              mov ax,4C01h                  ;exit with ERRORLEVEL = 1
              int 21h

;-----------------------------------------------------------------------------
;DoLock interprets the command line parameter /L.
;-----------------------------------------------------------------------------
DoLock:       lodsb                              ;get next character
              cmp al,"+"                         ;plus symbol?
              jne lock1
              mov byte ptr es:[locking],0        ;turn cursor locking on
              mov ah,3                           ;get cursor mode
              int 10h
              mov es:[cursor_mode],cx            ;store it
              jmp short continue                 ;exit
lock1:        cmp al,"-"                         ;minus symbol?
              jne badparm                        ;no, then it's invalid
              mov byte ptr es:[locking],0FFh     ;turn cursor locking off
              jmp short continue                 ;exit

;-----------------------------------------------------------------------------
;DoEmul interprets the command line parameter /E.
;-----------------------------------------------------------------------------
DoEmul:       lodsb                              ;get next character
              cmp al,"+"                         ;plus symbol?
              jne emul1
              mov byte ptr es:[emulation],0      ;turn emulation on
              jmp short continue
emul1:        cmp al,"-"                         ;minus symbol?
              jne badparm                        ;no, then it's invalid
              mov byte ptr es:[emulation],0FFh   ;turn emulation off
              jmp continue

;-----------------------------------------------------------------------------
;DoColor interprets the command line parameter /C.
;-----------------------------------------------------------------------------
DoColor:      lodsb                         ;get next character
              cmp al,"-"                    ;minus symbol?
              jne color1                    ;no, then read hex digits
              mov es:[foreground],0FFh      ;yes, then toggle color off
              jmp continue
color1:       call Convert                  ;get foreground color
              jc badparm                    ;branch on error
              mov ah,al                     ;save it in AH
              inc si                        ;skip space
              call Convert                  ;get background color
              jc badparm                    ;branch on error
              cmp al,7                      ;attribute in range?
              ja badparm                    ;no, then signal error
              mov es:[foreground],ah        ;store color values
              mov es:[background],al
              jmp continue                  ;exit

;-----------------------------------------------------------------------------
;DoBit interprets the command line parameter /B.
;-----------------------------------------------------------------------------
DoBit:        cmp adapter,1                 ;is an EGA or VGA installed?
              jb bit1                       ;no, then ignore the entry
              lodsb                         ;get next character
              cmp al,"+"                    ;plus symbol?
              jne bit2
              push es                       ;save ES
              mov ax,bios_data              ;point ES to BIOS data area
              mov es,ax
              or es:[ega_info],1            ;set bit 1 of EGA info byte
              pop es                        ;restore ES
bit1:         jmp continue
bit2:         cmp al,"-"                    ;minus symbol?
              jne reject                    ;no, then it's invalid
              push es                       ;save ES
              mov ax,bios_data              ;point ES to BIOS data area
              mov es,ax
              and es:[ega_info],0FEh        ;clear bit 0 of EGA info byte
              pop es
              jmp continue
reject:       jmp badparm                   ;goto error handler
stick         endp

;-----------------------------------------------------------------------------
;FindParm scans the command line for the first non-space character.
;Entry:  DS:SI - command line          | Exit:  CF clear - text found
;                                      |        CF set   - none found
;-----------------------------------------------------------------------------
FindParm      proc near
              lodsb                         ;get next character
              cmp al,13                     ;end-of-line?
              je empty                      ;yes, then end search
              cmp al,32                     ;space?
              je FindParm                   ;yes, then continue the scan
              clc                           ;clear CF
              ret
empty:        stc                           ;set CF
              ret
FindParm      endp

;-----------------------------------------------------------------------------
;Convert reads a hex digit and converts it to binary.
;Entry:  DS:SI - character             | Exit:  AL - value
;                                      |        CF clear - no error
;                                      |        CF set   - error
;-----------------------------------------------------------------------------
Convert       proc near
              lodsb                         ;skip a character
              cmp al,"0"                    ;less than "0"?
              jb conerror
              cmp al,"9"                    ;greater than "9"?
              ja alpha
              sub al,30h                    ;convert to binary
              clc                           ;clear CF
              ret                           ;and exit
alpha:        and al,0DFh                   ;capitalize character
              cmp al,"A"                    ;less than "A"?
              jb conerror
              cmp al,"F"                    ;greater than "F"?
              ja conerror
              sub al,37h                    ;convert to binary
              clc
              ret
conerror:     stc                           ;set CF
              ret
Convert       endp

;-----------------------------------------------------------------------------
;ShowState displays the state of the indicated byte (0FFh = "Off")
;Entry:  ES:DI - byte value
;-----------------------------------------------------------------------------
ShowState     proc near
              mov ah,9                      ;prepare for output
              mov dx,offset off             ;assume it's off
              cmp byte ptr es:[di],0FFh     ;is it really off?
              je show1                      ;yes, then proceed
              mov dx,offset on              ;no, then reset string address
show1:        int 21h                       ;print "On" or "Off"
              ret
ShowState     endp

code          ends
              end begin
