;       title   'PASSWORD.ASM'
;       page    60
;
;
;               PASSWORD.ASM
;               Version 3.0
;        By Bo McCormick       8/6/81
;
; This is a program that adds password protection
; to programs. Format:
;
; PASSWORD name_of_file
;
; Then answer the prompt with the password to be
; applied to the program:
;
; Password?  enter password here
;
; If everything goes well, the program will be saved to disk.
; If not, a message is printed and control is passed
; to the CCP.
;
; The good part of this is, when you type in the program
; program name next time, instead of running the program
; right away, the program asks you for the password. If you
; reply with something other than the original password, the
; program doesn't run, and it returns to the ccp.
;
; 2/10/82: Set DMA before open because 1.4 CP/M uses buffer at 80H
; Removed "$" from labels so other assemblers can be used. Fixed
; "offset" to use tpa instead of 100H so it would work with
; modified CP/M.  Added encryption of password so dumping .COM
; file will not reveal it.  (Thanks to author of XYZZY.COM)
; Fixed test of supplied password to check all characters.
; Ted Shapin
;
; 12/10/81: changed ot$pw routine to input password one character
; at a time invisibly from BIOS rather than using BDOS's string input
; function.  This allows password to be typed in without anyone
; seeing what it was that was typed.  Jim Mills
;
;EQUATES
rdchar: equ     1
mesout: equ     9               ;BDOS functions
incon:  equ     10
open:   equ     15
close:  equ     16
delete: equ     19
read:   equ     20
write:  equ     21
setdma: equ     26
;
cr      equ     0dh             ;ascii values
lf      equ     0ah
eos     equ     '$'
;
boot    equ     0;4200H         ;0 for standard CP/M
                               ;4200H for ALT. CP/M;
bdos    equ     boot+5
fcb     equ     boot+5ch
defbuf  equ     boot+80h
tpa     equ     boot+100h
stack   equ     tpa             ;out of way of tpa
;
       org     tpa

;
;
start:  lxi     h,0             ;save stack pointer
       dad     sp              ;put stack in hl
       shld    oldstack-offset ;save it
       lxi     sp,stack        ;get new stack
;
; stack saved so program can return to CCP without
; intervening warm start.
;
       lda     fcb+9           ;get first char of extension
       cpi     ' '             ;if ' ' then change to .COM
       jz      notype
       cpi     'C'             ;If there is an extension,
       jnz     notright        ;make sure it's .COM
       lda     fcb+10          ;check second letter
       cpi     'O'
       jnz     notright
       lda     fcb+11
       cpi     'M'             ;last letter
       jz      iscom           ;if it is a COM, then cont.
notright:
       call    endmes          ;it's not a com file, so tell
;
       db      cr,lf,'Must be a command (.COM) file'
       db      cr,lf,eos
;
endmes:
       pop     d               ;get address of message
       mvi     c,mesout        ;PRINT STRING command
       call    bdos            ;print error message
;
finish: lhld    oldstack-offset ;get old stack
       sphl                    ;put it in HL
       ret                     ;return to CP/M
;
notype: mvi     a,'C'           ;if there was space, change
       sta     fcb+9           ;to COM
       mvi     a,'O'
       sta     fcb+10
       mvi     a,'M'
       sta     fcb+11
;
iscom:  lxi     d,buffer-offset ;point to where program goes
       mvi     c,setdma        ;SET DMA command
       push    d               ;save it
       call    bdos            ;and tell CP/M
       mvi     a,0             ;zero record count
       sta     fcb+32
       mvi     c,open          ;OPEN file command
       lxi     d,fcb           ;load address of FCB in DE
       call    bdos            ;Open file
       inr     a               ;successful?
       jnz     openok          ;if so, then continue
       call    endmes          ;if not, then tell
;
       db      cr,lf,'Cannot open file',cr,lf,eos
;
openok: pop     d               ;get starting dma back
rloop:  mvi     c,setdma        ;and set it in loop
       push    d               ;save it
       call    bdos
       lxi     d,fcb           ;point to FCB
       mvi     c,read          ;READ sector command
       call    bdos            ;do it
       pop     d               ;get DMA address back
       ana     a               ;EOF?
       jnz     doneread        ;if so, then ask for password
       lxi     h,80h           ;length of sector
       dad     d               ;bump DMA
       xchg                    ;put new address in DE
       jmp     rloop           ;and read some more
;
doneread:
       xchg                    ;dma ==> hl
       shld    endprog-offset  ;save last address
gpasag  call    getpas          ;print password message
;
pasmes  db      'Password? ',eos
;
getpas  pop     d               ;get address of message
       mvi     c,mesout        ;PRINT STRING function
       call    bdos            ;print it
       lxi     d,defbuf        ;point to default buffer
       mvi     a,8             ;tell CP/M max chars
       stax    d               ;put it there
       mvi     c,incon         ;READ LINE command
       call    bdos            ;do it
       lxi     h,defbuf+1      ;point to length
       lxi     d,password-offset       ;point to storage
       mov     a,m             ;get length
       ana     a               ;set flags
       jz      gpasag          ;if 0 then ask again
       inr     a               ;plus 1 for length byte
       mov     b,a             ;put length in B
       xra     b               ;cancel first xra in loop
       mov     m,a             ;and put it back
mploop  mov     a,m             ;get char
       xra     b               ;encrypt it
       stax    d               ;save it
       inx     h               ;increment pointer
       inx     d               ;  "          "
       dcr     b               ;decrement length
       jnz     mploop          ;if not zero, then next char
       xra     a               ;zero a
       sta     fcb+12          ;zero bytes in FCB
       sta     fcb+14
       sta     fcb+32
       mvi     c,open          ;OPEN file command
       lxi     d,fcb           ;point to FCB
       call    bdos            ;open the file
       lxi     d,nstart        ;point to new program start
;
       push    d
wloop1  pop     d               ;get DMA
       push    d               ;put it back on stack
       mvi     c,setdma        ;SET DMA command
       call    bdos            ;tell CP/M
       lxi     d,fcb           ;point to FCB
       mvi     c,write         ;WRITE SECTOR command
       call    bdos            ;do it
       pop     h               ;get DMA address from stack
       lxi     d,80h           ;length of sector
       dad     d               ;HL has new DMA
       push    h               ;put it on stack
       mov     a,h             ;this is to get 2's complement
       cma                     ;of address. We are subtracting
       mov     d,a             ;the current address from the
       mov     a,l             ;high address. If the high byte
       cma                     ;<1 , we are done
       mov     e,a             ;
       inx     d               ;Now 2's comp. of address in DE
       lhld    endprog-offset  ;get ending address
       dad     d               ;Subtract (add 2's comp)
       mov     a,h             ;get high byte
       inr     a               ;is it FF (-1)?
       ana     a               ;set flags
       jnz     wloop1          ;if not, write another sector
;
       mvi     c,close         ;That's it. Close the file
       lxi     d,fcb           ;point to FCB
       call    bdos            ;do it
       jmp     finish          ;goto finish
;
;
nstart:
offset  equ     tpa-nstart
;
;       %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;       %% WARNING -                                        %%
;       %% From now on, all labels are in                   %%
;       %% the form:                                        %%
;       %%      LABEL   EQU  $+OFFSET                       %%
;       %%  This is to allow the program to run at100H      %%
;       %% when it is saved by the earlier portion.         %%
;       %%  ALL new labels added MUST be in the form        %%
;       %% LABEL   EQU  $+OFFSET for this program to work   %%
;       %% properly.                                        %%
;       %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;
;This is portion of the program is placed at the beginning
;of the program to be PASSWORDed. When it is executed, it will
;ask for a password. If the password is incorrect, the program
;warm starts. If the password is correct, the program is moved
;to the TPA and executed.
;
       lxi     h,0             ;save stack pointer
       dad     sp              ;stack is in HL
       shld    oldstack        ;save it
       lxi     sp,stack        ;get new stack
       call    otpw            ;print password message
;
       db      cr,lf,'Password? '
       db      eos
;
otpw    equ     $+offset
       pop     d               ;get address of message
       mvi     c,mesout        ;PRINT STRING command
       call    bdos            ;print it

       lxi     d,newbuf+1      ;point to storage area
       xra     a
       mov     b,a             ;init counter
       stax    d               ;# of chars typed (for compare)
       inx     d               ;and point to string
;
; we don't want to allow 'lookers' to see the password, so call
; bios instead of bdos to prevent echo.  also allows control chars.
;
       lhld    boot+1          ;get addr of warm start routine
       push    d
       lxi     d,6             ;+ offset to kbd input routine
       dad     d
       pop     d
       shld    bconin+1        ;now init'd to bios conin

otpw1   equ     $+offset
       push    b               ;save counter..
       push    d               ;..& pointer
bconin  equ     $+offset
       call    $-$             ;to be filled in by above routine
       pop     d               ;restore pointer..
       pop     b               ;..& counter
       stax    d               ;store char
       inx     d               ;bump pointer..
       inr     b               ;..& counter
       cpi     cr
       jnz     otpw1           ;loop until cr typed
; B has the length of password furnished + 1 for count.
       lxi     h,password      ;point to actual password
       lxi     d,newbuf+1      ;point to user's input
       xra     a               ;this 0 xra with length in B
       stax    d               ;should equal count in passwd
;
; first char compared is a count of # of chars in password,
; followed by password itself.
;
clp     equ     $+offset
       ldax    d               ;get char
       xra     b               ;encrypt it
       cmp     m               ;are they the same?
       jnz     boot            ;if not, restart
       inx     h               ;point to next characters
       inx     d               ;  "    "  "        "
       dcr     b               ;decrement length
       jnz     clp             ;if not done, then loop
;
; Now we move a segment of code to a part of the default
; buffer. This segment moves the actual program down to the
; TPA
;
       lxi     h,nmv           ;point to code
       lxi     d,defbuf+20h    ;point to new postion
       mvi     b,nmlen ;length
;
move    equ     $+offset
       mov     a,m             ;get byte
       stax    d               ;save it
       inx     d               ;point to next addresses
       inx     h               ;  "   "    "      "
       dcr     b               ;decrement length
       jnz     move            ;if not done, loop
       jmp     defbuf+20h      ;go to segment
;
nmv     equ     $+offset        ;segment that gets moved
       lhld    oldstack        ;get stack pointer
       push    h               ;save it on stack
       lxi     h,buffer        ;get start of actual program
       mov     a,h             ;We have to compute the length
       cma                     ;and because X-Y equals
       mov     d,a             ;X + Two's complent(Y), we have
       mov     a,l             ;to find the 2's comp. of the
       cma                     ;first address
       mov     e,a             ;
       inx     d               ;Y is in DE
       lhld    endprog ;get last address
       dad     d               ;subtract (add 2's comp)
       mov     b,h             ;put length in BC
       mov     c,l             ; "    "     "  "
       lxi     d,tpa           ;point to TPA
       lxi     h,buffer        ;point to first address
nmlp    equ     defbuf+20h+$+offset-nmv
       mov     a,m             ;get byte
       stax    d               ;save byte
       inx     h               ;increment address
       inx     d               ;    "        "
       dcx     b               ;decrement length
       mov     a,b             ;check for zero left
       ora     c               ;Are we done?
       jnz     nmlp            ;if not, loop some more
       pop     h               ;get stack from stack
       sphl                    ;put stack in SP
       jmp     tpa             ;run program
;
nmlen   equ     $+offset-nmv    ;length of segment
;
;
password        equ     $+offset ;password storage
       db      0,'         '
;
;12/10/81 changed to allow use of tbuf as input area
;
newbuf  equ     $+offset        ;Users input buffer
       db      10H,0,'                '
;
oldstack        equ     $+offset ;place for stack
       ds      2
;
endprog equ     $+offset ;place for address
       ds      2
;
buffer  equ     $+offset        ;where actual program goes
       end