home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / utils / dirutl / protect.lbr / PROTECT.AQM / PROTECT.ASM
Assembly Source File  |  1985-10-31  |  25KB  |  1,126 lines

  1. *  PROGRAM:  PROTECT
  2. *  AUTHOR:  RICHARD CONN
  3. *  VERSION:  1.0
  4. *  DATE:  26 OCT 81
  5. *  PREVIOUS VERSIONS:  None
  6.  
  7. VERS    EQU    10    ; Version Number
  8.  
  9. *
  10. *  PROTECT --
  11. *    PROTECT is used to set the file protection and tag attribute bits
  12. * for CP/M 2.x files.  It is invoked via command lines of the following
  13. * forms:
  14. *
  15. *        PROTECT afn keys    <-- Set unconditionally
  16. *        PROTECT afn keys /I    <-- Inspect Mode
  17. *        PROTECT afn /S        <-- Set Each ufn Individually
  18. *
  19. *    In the above examples, the reference 'keys' is a string of zero or
  20. * more characters which may be any of the following:
  21. *
  22. *        R <-- Set R/O Attribute
  23. *        S <-- Set SYS Attribute
  24. *        n <-- Set Tag Attribute (1 <= n <= 4)
  25. *
  26. *    Examples:
  27. *        PROTECT *.COM RS    <-- Sets all COM files to R/O System
  28. *        PROTECT *.COM RS /I    <-- Same, with user approval
  29. *        PROTECT *.COM /S    <-- Allow user to specify for each file
  30. *        PROTECT MYPROG.COM 1R    <-- Sets Tag Bit 1 and R/O Attribute
  31. *
  32.  
  33. *
  34. *  PROT CONSTANTS
  35. *
  36. DELIM        EQU    '/'    ; OPTION DELIMITER CHAR
  37. INSP$OPT    EQU    'I'    ; OPTION LETTER FOR INSPECTION
  38. SET$OPT        EQU    'S'    ; OPTION LETTER FOR SETTING INDIVIDUAL FILES
  39. ENTRY$SIZE    EQU    12    ; NUMBER OF BYTES/DIRECTORY ENTRY STORED
  40.  
  41. *
  42. *  CP/M CONSTANTS
  43. *
  44. BDOS    EQU    5    ; BDOS ENTRY
  45. FCB    EQU    5CH    ; FIRST FCB
  46. FCB2    EQU    5CH+16    ; 2ND FCB
  47. TBUFF    EQU    80H    ; INPUT LINE
  48. CR    EQU    0DH    ; <CR>
  49. LF    EQU    0AH    ; <LF>
  50.  
  51.     ORG    100H
  52.  
  53. *
  54. *  SAVE OLD STACK PTR AND SET NEW
  55. *
  56.     LXI    H,0    ; SAVE STACK PTR
  57.     DAD    SP
  58.     SHLD    STACK    ; SAVE SP IN BUFFER
  59.     LXI    SP,STACK    ; RESET STACK PTR
  60.  
  61. *
  62. *  PRINT PROGRAM NAME
  63. *
  64.     CALL    PRINT$MESSAGE
  65.     DB    'PROTECT  Version '
  66.     DB    VERS/10+'0','.',(VERS MOD 10)+'0'
  67.     DB    0
  68.  
  69. *
  70. *  CHECK FOR USER-SPECIFIED DRIVE AND LOG IN IF SELECTED
  71. *
  72.     LDA    FCB    ; GET FROM FCB BYTE
  73.     STA    UDRIVE    ; SET FLAG
  74.     ORA    A    ; 0=DEFAULT
  75.     JZ    CK$VERS
  76.     DCR    A    ; ADJUST FOR LOGIN
  77.     PUSH    PSW    ; SAVE A
  78.     MVI    C,25    ; GET CURRENT DISK
  79.     CALL    BDOS
  80.     INR    A    ; ADJUST TO 1-16
  81.     STA    UDRIVE    ; SET FLAG
  82.     POP    PSW    ; GET NEW DISK
  83.     MOV    E,A    ; NUMBER IN E
  84.     MVI    C,14    ; SELECT DRIVE
  85.     CALL    BDOS
  86.  
  87.  
  88. *
  89. *  CHECK FOR CP/M VERSION NUMBER -- MUST BE 2.X
  90. *
  91. CK$VERS:
  92.     MVI    C,12    ; GET VERSION NUMBER
  93.     CALL    BDOS
  94.     MOV    A,H    ; HL=0 IF 1.4
  95.     ORA    L
  96.     JNZ    PROT1
  97.     CALL    PRINT$MESSAGE
  98.     DB    CR,LF,'ERROR -- PROT must run under CP/M 2.x',0
  99.  
  100. *
  101. *  RETURN TO OS
  102. *
  103. RETURN:
  104.     LDA    UDRIVE    ; GET SELECTED DRIVE
  105.     ORA    A    ; 0=DEFAULT
  106.     JZ    RETURN1
  107.     DCR    A    ; ADJUST TO 0-15
  108.     MOV    E,A
  109.     MVI    C,14    ; SELECT DISK
  110.     CALL    BDOS
  111. RETURN1:
  112.     LHLD    STACK    ; GET ORIGINAL STACK PTR
  113.     SPHL        ; SET IT
  114.     RET        ; RETURN TO OS
  115.  
  116. *
  117. *  CONTINUE PROCESSING
  118. *
  119. PROT1:
  120.     XRA    A    ; A=0
  121.     STA    SET$FLAG    ; CLEAR SET FLAG
  122.     STA    INSP$FLAG    ; CLEAR INSPECT FLAG
  123.  
  124.     LXI    H,TBUFF    ; PT TO INPUT LINE
  125.     MOV    B,M    ; CHAR COUNT IN B
  126.     MOV    A,B    ; CHECK FOR EMPTY LINE
  127.     ORA    A    ; 0 CHARS = HELP
  128.     JNZ    PROT2
  129.  
  130. *
  131. *  PRINT PROT HELP MESSAGE
  132. *
  133. PROT$HELP:
  134.     CALL    PRINT$MESSAGE
  135.     DB    CR,LF,'PROT is invoked by a command of the form:'
  136.     DB    CR,LF,'    PROT afn keys        <-- Set unconditional'
  137.     DB    CR,LF,'    PROT afn keys /I    <-- Inspect mode'
  138.     DB    CR,LF,'    PROT afn /S        <-- Set individual'
  139.     DB    CR,LF,'where "keys" may be any combination of:'
  140.     DB    CR,LF,'    R S 1 2 3 4'
  141.     DB    CR,LF,'to indicate R/O, SYS, or Tag Bits 1, 2, 3, or 4'
  142.     DB    CR,LF,'  Any bit not specified is cleared (R/W, DIR, etc)'
  143.     DB    CR,LF
  144.     DB    CR,LF,'  Examples:'
  145.     DB    CR,LF,'    PROT *.COM RS    <-- Set *.COM to R/O SYS'
  146.     DB    CR,LF,'    PROT *.COM 1R    <-- Set *.COM to R/O DIR'
  147.     DB    CR,LF,'                with Tag Bit 1 Set'
  148.     DB    CR,LF,'    PROT *.COM /I    <-- Set *.COM to R/W DIR'
  149.     DB    CR,LF,'                with user inspection'
  150.     DB    CR,LF,'    PROT *.COM /S    <-- Set *.COM to user-selected'
  151.     DB    CR,LF,'                attributes on '
  152.     DB    'individual basis'
  153.     DB    0
  154.     JMP    RETURN
  155.  
  156. *
  157. *  CONTINUE PROCESSING
  158. *
  159. PROT2:
  160.     INX    H    ; PT TO FIRST CHAR
  161.     MOV    A,M    ; GET IT
  162.     CPI    DELIM    ; OPTION?
  163.     JNZ    PROT3
  164.     DCR    B    ; COUNT DOWN
  165.     JZ    PROT$HELP
  166.     INX    H    ; PT TO NEXT
  167.     MOV    A,M    ; GET OPTION CHAR
  168.     CPI    SET$OPT    ; SET INDIVIDUAL FILES?
  169.     JZ    PROT2$SET
  170.     CPI    INSP$OPT    ; INSPECT?
  171.     JZ    PROT2$INSP
  172.     JMP    PROT$HELP    ; HELP OTHERWISE
  173. PROT2$SET:
  174.     MVI    A,0FFH    ; SET FLAG
  175.     STA    SET$FLAG
  176.     JMP    PROT3
  177. PROT2$INSP:
  178.     MVI    A,0FFH    ; SET FLAG
  179.     STA    INSP$FLAG
  180. PROT3:
  181.     DCR    B    ; COUNT DOWN
  182.     JNZ    PROT2
  183.  
  184. *
  185. *  CHECK FOR FILE NAME SPECIFIED
  186. *
  187.     LDA    FCB+1    ; GET FIRST LETTER OF FILE NAME
  188.     CPI    DELIM    ; DELIMITER CAUGHT?
  189.     JZ    FN$ERR
  190.     CPI    ' '    ; NO FILE SPECIFIED?
  191.     JNZ    PROT4
  192. FN$ERR:
  193.     CALL    PRINT$MESSAGE
  194.     DB    CR,LF,'ERROR -- File Name not specified',0
  195.     JMP    RETURN
  196. KEY$ERR:
  197.     CALL    PRINT$MESSAGE
  198.     DB    CR,LF,'ERROR -- Invalid Key specified',0
  199.     JMP    RETURN
  200. *
  201. *  CHECK FOR KEYS AND CLEAR IF OPTION CAUGHT
  202. *
  203. PROT4:
  204.     LXI    H,FCB2+1    ; PT TO FIRST BYTE OF NAME
  205.     MOV    A,M        ; GET IT
  206.     CPI    DELIM        ; OPTION CAUGHT?
  207.     JNZ    PROT5
  208.     MVI    B,11        ; CLEAR ALL OF FILE NAME/TYPE
  209.     MVI    A,' '        ; <SP> FILL
  210.     CALL    FILL
  211.  
  212. *
  213. *  COPY 2ND FCB INTO KEY$BUFFER
  214. *
  215. PROT5:
  216.     LXI    D,FCB2+1    ; PT TO FCB2
  217.     CALL    SET$KEY$BUFFER    ; LOAD KEY$BUFFER IN SORTED ORDER
  218.  
  219. *
  220. *  ALL SET TO GO --
  221. *    FCB CONTAINS FILE NAME/TYPE
  222. *    FCB2 AND KEY$BUFFER CONTAIN KEYS
  223. *    SET$FLAG IS SET CORRECTLY
  224. *    INSP$FLAG IS SET CORRECTLY
  225. *
  226.  
  227. *  LOAD DIRECTORY INTO DIR1 BUFFER
  228. DIR:
  229.     LXI    H,ENDALL        ; PT TO END OF PROGRAM
  230.     SHLD    DIR1            ;  AND SET PTR TO DIR1
  231.     LXI    H,0            ; HL=0
  232.     SHLD    FILE$COUNT        ; TOTAL SELECTED FILES = 0
  233. DIR$USER:
  234.     MVI    C,17    ; SEARCH FOR FILE
  235.     LXI    D,FCB    ; PT TO FILE NAME
  236.     CALL    BDOS
  237.     CPI    255    ; NO MATCH?
  238.     JZ    DIR$LOOP1
  239. DIR$LOOP:
  240.     CALL    PUT$ENTRY    ; PLACE ENTRY IN DIR
  241.     MVI    C,18    ; SEARCH FOR NEXT MATCH
  242.     CALL    BDOS
  243.     CPI    255    ; DONE?
  244.     JNZ    DIR$LOOP
  245.  
  246. *  CHECK FOR ANY SELECTIONS
  247. DIR$LOOP1:
  248.     LHLD    FILE$COUNT    ; GET COUNT
  249.     MOV    A,H        ; ZERO?
  250.     ORA    L
  251.     JNZ    COMP$ORDER
  252.     CALL    PRINT$MESSAGE
  253.     DB    CR,LF,'No Files Selected -- Aborting',0
  254.     JMP    RETURN
  255.  
  256. *  COMPUTE POINTER TO ORDER TABLE
  257. COMP$ORDER:
  258.     MVI    B,ENTRY$SIZE-1    ; B=NUMBER OF BYTES/ENTRY-1
  259.     MOV    D,H        ; DE=HL=NUMBER OF ENTRIES
  260.     MOV    E,L
  261. COMP$ORDER$LOOP:
  262.     DAD    D        ; HL=HL+DE
  263.     DCR    B        ; COUNT DOWN
  264.     JNZ    COMP$ORDER$LOOP
  265.     XCHG            ; DE=NUMBER OF BYTES OCCUPIED BY ENTRIES
  266.     LHLD    DIR1        ; HL PTS TO FIRST ENTRY
  267.     DAD    D        ; HL PTS TO AFTER LAST ENTRY
  268.     INR    H        ; HL PTS TO NEXT PAGE
  269.     MVI    L,0
  270.     SHLD    ORDER        ; ORDER PTR SET
  271.  
  272. *  ALPHABETIZE DIRECTORY ENTRIES
  273.     CALL    ALPHABETIZE
  274.  
  275. *  SET PROTECTION ATTRIBUTES
  276.     CALL    PROTECT
  277.  
  278. *  RETURN TO CP/M
  279.     JMP    RETURN
  280.  
  281. *
  282. *  LOAD KEY$BUFFER WITH KEYS PTED TO BY DE
  283. *
  284. SET$KEY$BUFFER:
  285.     MVI    B,'1'        ; CHECK FOR KEYS 1-4
  286.     LXI    H,KEY$BUFFER    ; PT TO KEY BUFFER
  287. PROT5A:
  288.     CALL    PROT$SCAN    ; SCAN FOR/SET KEY
  289.     INR    B        ; INCREMENT COUNTER
  290.     MOV    A,B        ; GET B INTO A FOR TEST
  291.     CPI    '5'        ; DONE?
  292.     JNZ    PROT5A
  293.     MVI    B,'R'        ; CHECK FOR R/O KEY
  294.     CALL    PROT$SCAN    ; SCAN FOR/SET KEY
  295.     MVI    B,'S'        ; CHECK FOR SYS KEY
  296.     CALL    PROT$SCAN    ; SCAN FOR/SET KEY
  297. *  CHECK FOR ERROR
  298.     MVI    B,11        ; 11 BYTES (ALL MUST BE <SP>)
  299. PROT5B:
  300.     LDAX    D        ; GET CHAR
  301.     CPI    ' '        ; <SP>
  302.     JNZ    KEY$ERR
  303.     INX    D        ; PT TO NEXT
  304.     DCR    B        ; COUNT DOWN
  305.     JNZ    PROT5B
  306.     RET
  307.  
  308. *
  309. *  SCAN FCB2+1 FOR KEY IN REG B
  310. *
  311. PROT$SCAN:
  312.     PUSH    D        ; SAVE PTR
  313.     MVI    C,11        ; 11 BYTES
  314.     MVI    M,' '        ; SET INITIAL <SP>
  315. PS1:
  316.     LDAX    D        ; GET KEY
  317.     CALL    CAPS        ; CAPITALIZE
  318.     CMP    B        ; MATCH?
  319.     JNZ    PS2
  320.     MOV    M,A        ; SET KEY
  321.     MVI    A,' '        ; <SP> KILL
  322.     STAX    D
  323. PS2:
  324.     INX    D        ; PT TO NEXT
  325.     DCR    C        ; COUNT DOWN
  326.     JNZ    PS1
  327.     POP    D        ; RESTORE PTR
  328.     INX    H        ; PT TO NEXT ATTRIBUTE
  329.     RET
  330.  
  331. *
  332. *  PLACE ENTRY IN DIR1 IF:
  333. *    1 -- NOT AN ERASED ENTRY
  334. *    2 -- SELECTED USER NUMBER
  335. *    3 -- MATCHES SPECIFICATION FCB
  336. *    4 -- ATTRIBUTES CORRESPOND
  337. *
  338. *  ON INPUT,  A=0-3 FOR ADR INDEX IN BUFF OF ENTRY FCB
  339. *          FILE$COUNT=NUMBER OF SELECTED FILES
  340. *  ON OUTPUT, FILE$COUNT=NUMBER OF SELECTED FILES
  341. *
  342. PUT$ENTRY:
  343.     PUSH PSW ! PUSH B ! PUSH D ! PUSH H
  344.     RRC        ; MULTIPLY BY 32 FOR OFFSET COMPUTATION
  345.     RRC
  346.     RRC
  347.     ANI    60H    ; A=BYTE OFFSET
  348.     LXI    D,TBUFF    ; PT TO BUFFER ENTRY
  349.     MOV    L,A    ; LET HL=OFFSET
  350.     MVI    H,0
  351.     DAD    D    ; HL=PTR TO FCB
  352.     MOV    A,M    ; GET USER NUMBER
  353.     CPI    0E5H    ; DELETED?
  354.     JZ    PE4    ; SKIP IT IF DELETED
  355.     XCHG        ; DE=PTR TO FCB
  356.     PUSH    D    ; SAVE IT
  357.     LHLD    FILE$COUNT    ; GET NUMBER OF ENTRIES SO FAR
  358.     SHLD    ECOUNTER
  359.     MOV    A,H    ; NONE?
  360.     ORA    L    ; ZERO FLAG SET IF SO
  361.     LHLD    DIR1    ; PT TO DIR1
  362.     JZ    PE2    ; IF NO ENTRIES, THIS IS THE FIRST
  363.     LXI    D,ENTRY$SIZE    ; HL PTS TO DIR1 BASE, DE=NUMBER OF BYTES/ENTRY
  364. PE1:
  365.     DAD    D    ; PT TO NEXT
  366.     CALL    ECOUNT    ; ECOUNTER=ECOUNTER-1
  367.     JNZ    PE1
  368. PE2:
  369.     POP    D    ; DE PTS TO FCB TO PLACE IN DIR1
  370.     XCHG
  371. *
  372. *    ON INPUT, DE=ADR TO PLACE ENTRY IN DIR1
  373. *          HL=ADR OF FCB IN BUFF
  374. *
  375.  
  376. *  COMPARE ENTRY AGAINST FILE SELECTION FCB
  377. PE2$COMP:
  378.     PUSH    H    ; SAVE HL, DE PTRS
  379.     PUSH    D
  380.     MVI    B,11    ; 11 BYTES IN FILE NAME AND FILE TYPE
  381.     LXI    D,FCB+1    ; PT TO FILE NAME IN FCB
  382.     INX    H    ; PT TO FILE NAME OF ENTRY
  383.  
  384. *  COMPARISON LOOP
  385. PE2$COMP1:
  386.     LDAX    D    ; GET FCB BYTE
  387.     ANI    7FH    ; MASK MSB
  388.     CPI    '?'    ; WILD?
  389.     JZ    PE2$COMP2
  390.     MOV    C,A    ; SAVE BYTE
  391.     MOV    A,M    ; GET ENTRY BYTE
  392.     ANI    7FH    ; MASK MSB
  393.     CMP    C    ; COMPARE
  394.     JZ    PE2$COMP2    ; MATCH
  395.     POP    D    ; RESTORE DE, HL
  396.     POP    H
  397.     JMP    PE4    ; ABORT
  398. PE2$COMP2:
  399.     INX    H    ; PARTIAL MATCH -- PT TO NEXT
  400.     INX    D
  401.     DCR    B    ; COUNT DOWN
  402.     JNZ    PE2$COMP1
  403.     POP    D    ; RESTORE DE, HL
  404.     POP    H
  405.  
  406. *  ENTRY COMPLETELY ACCEPTED -- HL PTS TO ENTRY, DE PTS TO DIRECTORY
  407. PE2$COPY:
  408.     PUSH    H    ; SAVE PTR
  409.     LXI    B,12    ; CHECK FOR ZERO EXTENT
  410.     DAD    B    ; HL PTS TO EXTENT
  411.     MOV    A,M    ; GET EXTENT
  412.     POP    H    ; RESTORE HL
  413.     ORA    A    ; ZERO?
  414.     JNZ    PE4    ; ABORT IF NOT
  415.     MVI    B,ENTRY$SIZE    ; B=NUMBER OF BYTES/ENTRY
  416.     CALL    MOVE    ; COPY INTO DIRECTORY
  417.  
  418. *  INCREMENT FILE COUNT
  419.     LHLD    FILE$COUNT    ; INCREMENT FILE COUNT
  420.     INX    H
  421.     SHLD    FILE$COUNT
  422.  
  423. *  DONE WITH PUT$ENTRY
  424. PE4:
  425.     POP H ! POP D ! POP B ! POP PSW
  426.     RET
  427.  
  428. *
  429. *  COUNT DOWN WITH 16-BIT COUNTER ECOUNTER; SET ZERO FLAG IF IT HITS ZERO
  430. *
  431. ECOUNT:
  432.     PUSH    H    ; SAVE HL
  433.     LHLD    ECOUNTER    ; GET COUNT
  434.     DCX    H    ; COUNT DOWN
  435.     SHLD    ECOUNTER    ; NEW COUNT
  436.     MOV    A,H    ; ZERO?
  437.     ORA    L    ; ZERO FLAG SET IF SO
  438.     POP    H    ; RESTORE HL
  439.     RET
  440.  
  441. *
  442. *  ALPHABETIZE -- ALPHABETIZES DIR1; FILE$COUNT CONTAINS
  443. *    THE NUMBER OF FILES IN DIR1
  444. *
  445. ALPHABETIZE:
  446.     LHLD    FILE$COUNT    ; GET FILE COUNT
  447.     MOV    A,H    ; ANY ENTRIES?
  448.     ORA    L
  449.     RZ
  450. *
  451. *  SHELL SORT --
  452. *    THIS SORT ROUTINE IS ADAPTED FROM "SOFTWARE TOOLS"
  453. *    BY KERNIGAN AND PLAUGHER, PAGE 106.  COPYRIGHT, 1976, ADDISON-WESLEY.
  454. *  ON ENTRY, HL=NUMBER OF ENTRIES
  455. *
  456. SORT:
  457.     MOV    B,H    ; COUNT IN BC
  458.     MOV    C,L
  459.     LHLD    DIR1    ; SET UP POINTERS TO DIRECTORY ENTRIES
  460.     XCHG        ; ... IN DE
  461.     LHLD    ORDER    ; PT TO ORDER TABLE
  462. *
  463. *  SET UP ORDER TABLE; HL PTS TO NEXT ENTRY IN ORDER TABLE, DE PTS TO NEXT
  464. *    ENTRY IN DIRECTORY, BC = NUMBER OF ELEMENTS REMAINING
  465. *
  466. SORT1:
  467.     MOV    M,E    ; STORE LOW-ORDER ADDRESS
  468.     INX    H    ; PT TO NEXT ORDER BYTE
  469.     MOV    M,D    ; STORE HIGH-ORDER ADDRESS
  470.     INX    H    ; PT TO NEXT ORDER ENTRY
  471.     PUSH    H    ; SAVE PTR
  472.     LXI    H,ENTRY$SIZE    ; HL=NUMBER OF BYTES/ENTRY
  473.     DAD    D    ; PT TO NEXT DIR1 ENTRY
  474.     XCHG        ; DE PTS TO NEXT ENTRY
  475.     POP    H    ; GET PTR TO ORDER TABLE
  476.     DCX    B    ; COUNT DOWN
  477.     MOV    A,B    ; DONE?
  478.     ORA    C
  479.     JNZ    SORT1
  480. *
  481. *  THIS IS THE MAIN SORT LOOP FOR THE SHELL SORT IN "SOFTWARE TOOLS" BY K&P
  482. *
  483.  
  484. *
  485. *  SHELL SORT FROM "SOFTWARE TOOLS" BY KERNINGHAN AND PLAUGER
  486. *
  487.     LHLD    FILE$COUNT    ; NUMBER OF ITEMS TO SORT
  488.     SHLD    GAP    ; SET INITIAL GAP TO N FOR FIRST DIVISION BY 2
  489.  
  490. *  FOR (GAP = N/2; GAP > 0; GAP = GAP/2)
  491. SRT$LOOP0:
  492.     ORA    A    ; CLEAR CARRY
  493.     LHLD    GAP    ; GET PREVIOUS GAP
  494.     MOV    A,H    ; ROTATE RIGHT TO DIVIDE BY 2
  495.     RAR
  496.     MOV    H,A
  497.     MOV    A,L
  498.     RAR
  499.     MOV    L,A
  500.  
  501. *  TEST FOR ZERO
  502.     ORA    H
  503.     JZ    SORT$DONE    ; DONE WITH SORT IF GAP = 0
  504.  
  505.     SHLD    GAP    ; SET VALUE OF GAP
  506.     SHLD    I    ; SET I=GAP FOR FOLLOWING LOOP
  507.  
  508. *  FOR (I = GAP + 1; I <= N; I = I + 1)
  509. SRT$LOOP1:
  510.     LHLD    I    ; ADD 1 TO I
  511.     INX    H
  512.     SHLD    I
  513.  
  514. *  TEST FOR I <= N
  515.     XCHG        ; I IS IN DE
  516.     LHLD    FILE$COUNT    ; GET N
  517.     MOV    A,L    ; COMPARE BY SUBTRACTION
  518.     SUB    E
  519.     MOV    A,H
  520.     SBB    D    ; CARRY SET MEANS I > N
  521.     JC    SRT$LOOP0    ; DON'T DO FOR LOOP IF I > N
  522.  
  523.     LHLD    I    ; SET J = I INITIALLY FOR FIRST SUBTRACTION OF GAP
  524.     SHLD    J
  525.  
  526. *  FOR (J = I - GAP; J > 0; J = J - GAP)
  527. SRT$LOOP2:
  528.     LHLD    GAP    ; GET GAP
  529.     XCHG        ; ... IN DE
  530.     LHLD    J    ; GET J
  531.     MOV    A,L    ; COMPUTE J - GAP
  532.     SUB    E
  533.     MOV    L,A
  534.     MOV    A,H
  535.     SBB    D
  536.     MOV    H,A
  537.     SHLD    J    ; J = J - GAP
  538.     JC    SRT$LOOP1    ; IF CARRY FROM SUBTRACTIONS, J < 0 AND ABORT
  539.     MOV    A,H    ; J=0?
  540.     ORA    L
  541.     JZ    SRT$LOOP1    ; IF ZERO, J=0 AND ABORT
  542.  
  543. *  SET JG = J + GAP
  544.     XCHG        ; J IN DE
  545.     LHLD    GAP    ; GET GAP
  546.     DAD    D    ; J + GAP
  547.     SHLD    JG    ; JG = J + GAP
  548.  
  549. *  IF (V(J) <= V(JG))
  550.     CALL    ICOMPARE    ; J IN DE, JG IN HL
  551.  
  552. *  ... THEN BREAK
  553.     JC    SRT$LOOP1
  554.  
  555. *  ... ELSE EXCHANGE
  556.     LHLD    J    ; SWAP J, JG
  557.     XCHG
  558.     LHLD    JG
  559.     CALL    ISWAP    ; J IN DE, JG IN HL
  560.  
  561. *  END OF INNER-MOST FOR LOOP
  562.     JMP    SRT$LOOP2
  563.  
  564. *
  565. *  SORT IS DONE -- RESTRUCTURE DIR1 IN SORTED ORDER IN PLACE
  566. *
  567. SORT$DONE:
  568.     LHLD    FILE$COUNT    ; NUMBER OF ENTRIES
  569.     MOV    B,H        ; ... IN BC
  570.     MOV    C,L
  571.     LHLD    ORDER    ; PTR TO ORDERED POINTER TABLE
  572.     SHLD    PTPTR    ; SET PTR PTR
  573.     LHLD    DIR1    ; PTR TO UNORDERED DIRECTORY
  574.     SHLD    PTDIR1    ; SET PTR DIR1
  575.  
  576. *  FIND PTR TO NEXT DIR1 ENTRY
  577. SRTDN:
  578.     LHLD    PTPTR    ; PT TO REMAINING POINTERS
  579.     XCHG        ; ... IN DE
  580.     LHLD    PTDIR1    ; HL PTS TO NEXT DIR1 ENTRY
  581.     PUSH    B    ; SAVE COUNT OF REMAINING ENTRIES
  582.  
  583. *  FIND PTR TABLE ENTRY
  584. SRTDN1:
  585.     LDAX    D    ; GET CURRENT POINTER TABLE ENTRY VALUE
  586.     INX    D    ; PT TO HIGH-ORDER POINTER BYTE
  587.     CMP    L    ; COMPARE AGAINST DIR1 ADDRESS LOW
  588.     JNZ    SRTDN2    ; NOT FOUND YET
  589.     LDAX    D    ; LOW-ORDER BYTES MATCH -- GET HIGH-ORDER POINTER BYTE
  590.     CMP    H    ; COMPARE AGAINST DIR1 ADDRESS HIGH
  591.     JZ    SRTDN3    ; MATCH FOUND
  592. SRTDN2:
  593.     INX    D    ; PT TO NEXT PTR TABLE ENTRY
  594.     DCX    B    ; COUNT DOWN
  595.     MOV    A,C    ; END OF TABLE?
  596.     ORA    B
  597.     JNZ    SRTDN1    ; CONTINUE IF NOT
  598.  
  599. *  FATAL ERROR -- INTERNAL XDIR ERROR; POINTER TABLE NOT CONSISTENT
  600. FERR$PTR:
  601.     CALL    PRINT$MESSAGE
  602.     DB    CR,LF,'PROTECT ERROR -- Pointer Table Not Consistent',0
  603.     JMP    RETURN
  604.  
  605. *  FOUND THE POINTER TABLE ENTRY WHICH POINTS TO THE NEXT UNORDERED DIR1 ENTRY
  606. *    MAKE BOTH POINTERS (PTR TO NEXT, PTR TO CURRENT UNORDERED DIR1 ENTRY)
  607. *    POINT TO SAME LOCATION (PTR TO NEXT DIR1 ENTRY TO BE ORDERED)
  608. SRTDN3:
  609.     LHLD    PTPTR    ; GET PTR TO NEXT ORDERED ENTRY
  610.     DCX    D    ; DE PTS TO LOW-ORDER POINTER ADDRESS
  611.     MOV    A,M    ; MAKE PTR TO NEXT UNORDERED DIR1 PT TO BUFFER FOR
  612.     STAX    D    ;   DIR1 ENTRY TO BE MOVED TO NEXT UNORDERED DIR1 POS
  613.     INX    H    ; PT TO NEXT PTR ADDRESS
  614.     INX    D
  615.     MOV    A,M    ; MAKE HIGH POINT SIMILARLY
  616.     STAX    D
  617.  
  618. *  COPY NEXT UNORDERED DIR1 ENTRY TO HOLD BUFFER
  619.     MVI    B,ENTRY$SIZE    ; B=NUMBER OF BYTES/ENTRY
  620.     LHLD    PTDIR1    ; PT TO ENTRY
  621.     LXI    D,HOLD    ; PT TO HOLD BUFFER
  622.     PUSH    B    ; SAVE B=NUMBER OF BYTES/ENTRY
  623.     CALL    MOVE
  624.     POP    B
  625.  
  626. *  COPY TO-BE-ORDERED DIR1 ENTRY TO NEXT ORDERED DIR1 POSITION
  627.     LHLD    PTPTR    ; POINT TO ITS POINTER
  628.     MOV    E,M    ; GET LOW-ADDRESS POINTER
  629.     INX    H
  630.     MOV    D,M    ; GET HIGH-ADDRESS POINTER
  631.     LHLD    PTDIR1    ; DESTINATION ADDRESS FOR NEXT ORDERED DIR1 ENTRY
  632.     XCHG        ; HL PTS TO ENTRY TO BE MOVED, DE PTS TO DEST
  633.     PUSH    B    ; SAVE B=NUMBER OF BYTES/ENTRY
  634.     CALL    MOVE
  635.     POP    B
  636.     XCHG        ; HL PTS TO NEXT UNORDERED DIR1 ENTRY
  637.     SHLD    PTDIR1    ; SET POINTER FOR NEXT LOOP
  638.  
  639. *  COPY ENTRY IN HOLD BUFFER TO LOC PREVIOUSLY HELD BY LATEST ORDERED ENTRY
  640.     LHLD    PTPTR    ; GET PTR TO PTR TO THE DESTINATION
  641.     MOV    E,M    ; GET LOW-ADDRESS POINTER
  642.     INX    H
  643.     MOV    D,M    ; HIGH-ADDRESS POINTER
  644.     LXI    H,HOLD    ; HL PTS TO HOLD BUFFER, DE PTS TO ENTRY DEST
  645.     CALL    MOVE    ; B=NUMBER OF BYTES/ENTRY
  646.  
  647. *  POINT TO NEXT ENTRY IN POINTER TABLE
  648.     LHLD    PTPTR    ; POINTER TO CURRENT ENTRY
  649.     INX    H    ; SKIP OVER IT
  650.     INX    H
  651.     SHLD    PTPTR
  652.  
  653. *  COUNT DOWN
  654.     POP    B    ; GET COUNTER
  655.     DCX    B    ; COUNT DOWN
  656.     MOV    A,C    ; DONE?
  657.     ORA    B
  658.     JNZ    SRTDN
  659.     RET        ; DONE
  660.  
  661. *
  662. *  SWAP (Exchange) the pointers in the ORDER table whose indexes are in
  663. *    HL and DE
  664. *
  665. ISWAP:
  666.     PUSH    H        ; SAVE HL
  667.     LHLD    ORDER        ; ADDRESS OF ORDER TABLE - 2
  668.     MOV    B,H        ; ... IN BC
  669.     MOV    C,L
  670.     POP    H
  671.     DCX    H        ; ADJUST INDEX TO 0...N-1 FROM 1...N
  672.     DAD    H        ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
  673.                 ;   OF ORIGINAL HL (1, 2, ...)
  674.     DAD    B        ; HL NOW PTS TO POINTER INVOLVED
  675.     XCHG            ; DE NOW PTS TO POINTER INDEXED BY HL
  676.     DCX    H        ; ADJUST INDEX TO 0...N-1 FROM 1...N
  677.     DAD    H        ; HL PTS TO OFFSET ADDRESS INDICATED BY INDEX
  678.                 ;   OF ORIGINAL DE (1, 2, ...)
  679.     DAD    B        ; HL NOW PTS TO POINTER INVOLVED
  680.     MOV    C,M        ; EXCHANGE POINTERS -- GET OLD (DE)
  681.     LDAX    D        ; -- GET OLD (HL)
  682.     XCHG            ; SWITCH
  683.     MOV    M,C        ; PUT NEW (HL)
  684.     STAX    D        ; PUT NEW (DE)
  685.     INX    H        ; PT TO NEXT BYTE OF POINTER
  686.     INX    D
  687.     MOV    C,M        ; GET OLD (HL)
  688.     LDAX    D        ; GET OLD (DE)
  689.     XCHG            ; SWITCH
  690.     MOV    M,C        ; PUT NEW (DE)
  691.     STAX    D        ; PUT NEW (HL)
  692.     RET
  693. *
  694. *  ICOMPARE compares the entry pointed to by the pointer pointed to by HL
  695. *    with that pointed to by DE (1st level indirect addressing); on entry,
  696. *    HL and DE contain the numbers of the elements to compare (1, 2, ...);
  697. *    on exit, Carry Set means ((DE)) < ((HL)), Zero Set means ((HL)) = ((DE)),
  698. *    and Non-Zero and No-Carry means ((DE)) > ((HL))
  699. *
  700. ICOMPARE:
  701.     PUSH    H        ; SAVE HL
  702.     LHLD    ORDER        ; ADDRESS OF ORDER - 2
  703.     MOV    B,H        ; ... IN BC
  704.     MOV    C,L
  705.     POP    H
  706.     DCX    H        ; ADJUST INDEX TO 0...N-1 FROM 1...N
  707.     DAD    H        ; DOUBLE THE ELEMENT NUMBER TO POINT TO THE PTR
  708.     DAD    B        ; ADD TO THIS THE BASE ADDRESS OF THE PTR TABLE
  709.     XCHG            ; RESULT IN DE
  710.     DCX    H        ; ADJUST INDEX TO 0...N-1 FROM 1...N
  711.     DAD    H        ; DO THE SAME WITH THE ORIGINAL DE
  712.     DAD    B
  713.     XCHG
  714.  
  715. *
  716. *  HL NOW POINTS TO THE POINTER WHOSE INDEX WAS IN HL TO BEGIN WITH
  717. *  DE NOW POINTS TO THE POINTER WHOSE INDEX WAS IN DE TO BEGIN WITH
  718. *    FOR EXAMPLE, IF DE=5 AND HL=4, DE NOW POINTS TO THE 5TH PTR AND HL
  719. * TO THE 4TH POINTER
  720. *
  721.     MOV    C,M        ; BC IS MADE TO POINT TO THE OBJECT INDEXED TO
  722.     INX    H        ; ... BY THE ORIGINAL HL
  723.     MOV    B,M
  724.     XCHG
  725.     MOV    E,M        ; DE IS MADE TO POINT TO THE OBJECT INDEXED TO
  726.     INX    H        ; ... BY THE ORIGINAL DE
  727.     MOV    D,M
  728.     MOV    H,B        ; SET HL = OBJECT PTED TO INDIRECTLY BY BC
  729.     MOV    L,C
  730.  
  731. *
  732. *  COMPARE DIR ENTRY PTED TO BY HL WITH THAT PTED TO BY DE;
  733. *    NO NET EFFECT ON HL, DE; RET W/CARRY SET MEANS DE<HL
  734. *    RET W/ZERO SET MEANS DE=HL
  735. *
  736. CMP$ENTRY:
  737.  
  738. *  COMPARE BY FILE NAME, FILE TYPE, EXTENSION, AND USER NUM (IN THAT ORDER)
  739. CMP$FN$FT:
  740.     PUSH D ! PUSH H
  741.     INX    H    ; PT TO FN
  742.     INX    D
  743.     MVI    B,11    ; COMPARE FN, FT
  744.     CALL    COMP
  745.     POP H ! POP D
  746.     RET
  747. *
  748. *  COMP COMPARES DE W/HL FOR B BYTES; RET W/CARRY IF DE<HL
  749. *    MSB IS DISREGARDED
  750. *
  751. COMP:
  752.     MOV    A,M    ; GET (HL)
  753.     ANI    7FH    ; MASK MSB
  754.     MOV    C,A    ; ... IN C
  755.     LDAX    D    ; COMPARE
  756.     ANI    7FH    ; MASK MSB
  757.     CMP    C
  758.     RNZ
  759.     INX    H    ; PT TO NEXT
  760.     INX    D
  761.     DCR    B    ; COUNT DOWN
  762.     JNZ    COMP
  763.     RET
  764.  
  765. *
  766. *  PERFORM PROTECTION BIT SETTING
  767. *
  768. PROTECT:
  769.     LHLD    FILE$COUNT    ; HL=NUMBER OF FILES
  770.     XCHG
  771.     LHLD    DIR1        ; HL PTS TO DIR1, DE=NUMBER OF FILES
  772.     LDA    SET$FLAG    ; MANUALLY SET EACH FILE?
  773.     ORA    A        ; 0=NO
  774.     JNZ    PROTECT$SET
  775.  
  776. *  PRINT FILE NAME, KEYS, AND PERFORM ATTRIBUTE SETTING
  777. PROTECT$LOOP:
  778.     CALL    PRINT$MESSAGE
  779.     DB    CR,LF,'File: ',0
  780.     CALL    PRINT$FN    ; PRINT FILE NAME
  781.     CALL    PRINT$MESSAGE
  782.     DB    ' Set to ',0
  783.     CALL    PRINT$KEYS    ; PRINT ATTRIBUTES
  784.     CALL    INSPECT        ; INSPECT IF FLAG SET ELSE SET ATTRIBUTES
  785.     LXI    B,ENTRY$SIZE    ; PT TO NEXT ENTRY
  786.     DAD    B        ; HL PTS TO NEXT ENTRY
  787.     DCX    D        ; COUNT DOWN
  788.     MOV    A,D        ; DONE?
  789.     ORA    E
  790.     JNZ    PROTECT$LOOP
  791.     RET
  792.  
  793. *  PRINT FILE NAME AND PROMPT USER FOR KEYS
  794. PROTECT$SET:
  795.     CALL    PRINT$MESSAGE
  796.     DB    CR,LF,'File: ',0
  797.     CALL    PRINT$FN    ; PRINT FILE NAME
  798.     CALL    GET$KEYS    ; GET KEYS
  799.     RZ            ; ABORT?
  800.     CALL    SET$KEYS    ; SET KEYS
  801.     LXI    B,ENTRY$SIZE    ; PT TO NEXT ENTRY
  802.     DAD    B        ; HL PTS TO NEXT ENTRY
  803.     DCX    D        ; COUNT DOWN
  804.     MOV    A,D        ; DONE?
  805.     ORA    E
  806.     JNZ    PROTECT$SET
  807.     RET
  808.  
  809. *
  810. *  PRINT FILE NAME PTED TO BY HL+1
  811. *
  812. PRINT$FN:
  813.     PUSH    H        ; SAVE PTR
  814.     INX    H        ; PT TO FIRST CHAR
  815.     MVI    B,8        ; 8 CHARS
  816.     CALL    PRFN        ; PRINT NAME PART
  817.     MVI    A,'.'        ; PRINT DOT
  818.     CALL    CHAR$OUT
  819.     MVI    B,3        ; 3 CHARS
  820.     CALL    PRFN        ; PRINT TYPE PART
  821.     POP    H        ; RESTORE PTR
  822.     RET
  823. PRFN:
  824.     MOV    A,M        ; GET CHAR
  825.     ANI    7FH        ; MASK OUT MSB
  826.     CALL    CHAR$OUT
  827.     INX    H        ; PT TO NEXT
  828.     DCR    B        ; COUNT DOWN
  829.     JNZ    PRFN
  830.     RET
  831.  
  832. *
  833. *  PRINT CONTENTS OF KEYS BUFFER
  834. *
  835. PRINT$KEYS:
  836.     PUSH    H        ; SAVE PTR
  837.     LXI    H,KEY$BUFFER    ; CHECK FOR ANY TAGS
  838.     XRA    A        ; SET FLAG
  839.     STA    TAG$FLAG
  840.     MVI    B,4
  841. PKEY0:
  842.     MOV    A,M        ; GET BYTE
  843.     INX    H        ; PT TO NEXT
  844.     CPI    ' '        ; <SP>
  845.     JZ    PKEY1
  846.     MVI    A,0FFH        ; SET FLAG
  847.     STA    TAG$FLAG
  848. PKEY1:
  849.     DCR    B        ; COUNT DOWN
  850.     JNZ    PKEY0
  851.     LDA    TAG$FLAG    ; ANY SET?
  852.     ORA    A        ; 0=NO
  853.     JZ    PKEY2
  854.     CALL    PRINT$MESSAGE
  855.     DB    'Tags: ',0
  856.     LXI    H,KEY$BUFFER    ; PT TO BUFFERS
  857.     MVI    B,4        ; PRINT 4 CHARS (1-4)
  858.     CALL    PRFN        ; USE COMMON ROUTINE
  859. PKEY2:
  860.     CALL    PRINT$MESSAGE
  861.     DB    ' R/',0
  862.     MOV    A,M        ; CHECK R/O FLAG
  863.     INX    H        ; PT TO NEXT
  864.     CPI    'R'        ; R/O?
  865.     JZ    PKRO
  866.     MVI    A,'W'        ; R/W
  867.     DB    1        ; LXI B,XXXX
  868. PKRO:    MVI    A,'O'        ; R/O (PART OF LXI B,XXXX IF R/W SELECTED)
  869.     CALL    CHAR$OUT
  870.     MOV    A,M        ; CHECK SYS FLAG
  871.     POP    H        ; RESTORE PTR
  872.     CPI    'S'        ; SYS?
  873.     JZ    PKSYS
  874.     CALL    PRINT$MESSAGE
  875.     DB    ' DIR',0
  876.     RET
  877. PKSYS:
  878.     CALL    PRINT$MESSAGE
  879.     DB    ' SYS',0
  880.     RET
  881.  
  882. *
  883. *  PERFORM INSPECTION IF OPTION SET -- ELSE, SET KEYS
  884. *
  885. INSPECT:
  886.     LDA    INSP$FLAG    ; GET FLAG
  887.     ORA    A        ; 0=NO
  888.     JZ    SET$KEYS    ; SET KEYS IF NOT
  889.     CALL    PRINT$MESSAGE
  890.     DB    ' -- Ok (Y/N)? ',0
  891. INSP1:
  892.     CALL    CHAR$IN
  893.     CALL    CAPS        ; CAPITALIZE
  894.     CPI    'Y'        ; OK?
  895.     JZ    SET$KEYS    ; SET IF SO
  896.     CPI    'N'        ; NOT OK?
  897.     RZ
  898.     CALL    PRINT$MESSAGE
  899.     DB    CR,LF,'Type Y or N -- Ok (Y/N)? ',0
  900.     JMP    INSP1
  901.  
  902. *
  903. *  SET KEYS OF FILE PTED TO BY HL
  904. *
  905. SET$KEYS:
  906.     PUSH    H        ; SAVE PTR
  907.     PUSH    D        ; SAVE COUNTER
  908.     INX    H        ; PT TO 1ST BYTE OF SOURCE FILE NAME
  909.     LXI    D,FCBX        ; CLEAR DRIVE
  910.     XRA    A        ; A=0
  911.     STAX    D
  912.     INX    D        ; PT TO 1ST BYTE OF DEST FILE NAME
  913.     MVI    B,11        ; 11 BYTES
  914.     CALL    MOVE
  915.     XCHG            ; HL PTS TO FCBX
  916.     MVI    B,24        ; CLEAR REST OF FCBX
  917.     XRA    A        ; A=0
  918.     CALL    FILL
  919.     LXI    D,KEY$BUFFER    ; PT TO BUFFER
  920.     LXI    H,FCBX+1    ; PT TO TYPE ATTRIBUTE
  921.     MVI    B,4        ; SET TAG BITS
  922.     CALL    SK$SET
  923.     INX    H        ; PT TO R/O BYTE
  924.     INX    H
  925.     INX    H
  926.     INX    H
  927.     MVI    B,2        ; SET R/O AND SYS BITS
  928.     CALL    SK$SET
  929.     LXI    D,FCBX        ; PT TO FCB
  930.     MVI    C,30        ; SET FILE ATTRIBUTES
  931.     CALL    BDOS
  932.     POP    D        ; RESTORE COUNTER
  933.     POP    H        ; RESTORE PTR
  934.     RET
  935. SK$SET:
  936.     MOV    A,M        ; CLEAR ATTRIBUTE
  937.     ANI    7FH
  938.     MOV    M,A
  939.     LDAX    D        ; GET TAG
  940.     CPI    ' '        ; NO SET?
  941.     JZ    SK$SET1
  942.     MOV    A,M        ; SET ATTRIBUTE
  943.     ORI    80H
  944.     MOV    M,A
  945. SK$SET1:
  946.     INX    D        ; PT TO NEXT
  947.     INX    H
  948.     DCR    B        ; COUNT DOWN
  949.     JNZ    SK$SET
  950.     RET
  951.  
  952. *
  953. *  GET KEYS SPECIFIED BY USER
  954. *    RETURN WITH ZERO FLAG SET IF ABORT GIVEN
  955. *
  956. GET$KEYS:
  957.     PUSH    H        ; SAVE PTR
  958.     PUSH    D        ; SAVE COUNTER
  959.     CALL    PRINT$MESSAGE
  960.     DB    ' Keys (1/2/3/4/R/S/A=Abort)? ',0
  961.     LXI    H,INLINE+2    ; PT TO INPUT BUFFER
  962.     MVI    A,' '        ; <SP> FILL
  963.     MVI    B,LINELEN    ; NUMBER OF CHARS POSSIBLE
  964.     CALL    FILL
  965.     LXI    D,INLINE    ; READ INTO BUFFER
  966.     MVI    C,10        ; BDOS READLN
  967.     CALL    BDOS
  968.     LXI    H,INLINE+2    ; SCAN FOR ABORT FLAG
  969.     MVI    B,11        ; SCAN 11 BYTES
  970. GK1:
  971.     MOV    A,M        ; GET IT
  972.     INX    H        ; PT TO NEXT
  973.     CALL    CAPS        ; CAPITALIZE
  974.     CPI    'A'        ; ABORT?
  975.     JZ    GK2
  976.     DCR    B        ; COUNT DOWN
  977.     JNZ    GK1
  978.     LXI    D,INLINE+2    ; PUT INTO KEY$BUFFER
  979.     CALL    SET$KEY$BUFFER
  980.     POP    D        ; RESTORE COUNTER
  981.     POP    H        ; RESTORE PTR
  982.     MVI    A,0FFH        ; CLEAR ZERO FLAG
  983.     ORA    A
  984.     RET
  985. GK2:
  986.     POP    D        ; RESTORE COUNTER
  987.     POP    H        ; RESTORE PTR
  988.     XRA    A        ; SET ZERO FLAG
  989.     RET
  990.  
  991. *
  992. *  CHARACTER INPUT ROUTINE
  993. *
  994. CHAR$IN:
  995.     PUSH    H    ; SAVE REGS
  996.     PUSH    D
  997.     PUSH    B
  998.     MVI    C,1    ; CONSOLE INPUT
  999.     CALL    BDOS
  1000.     POP    B    ; RESTORE REGS
  1001.     POP    D
  1002.     POP    H
  1003.     ANI    7FH    ; MASK OUT MSB
  1004.     RET
  1005.  
  1006. *
  1007. *  CHARACTER OUTPUT ROUTINE
  1008. *
  1009. CHAR$OUT:
  1010.     PUSH    H    ; SAVE REGS
  1011.     PUSH    D
  1012.     PUSH    B
  1013.     PUSH    PSW
  1014.     MOV    E,A    ; CHAR IN E
  1015.     MVI    C,2    ; CONSOLE OUTPUT
  1016.     CALL    BDOS
  1017.     POP    PSW    ; RESTORE REGS
  1018.     POP    B
  1019.     POP    D
  1020.     POP    H
  1021.     RET
  1022.  
  1023. *
  1024. *  PRINT MESSAGE PTED TO BY RET ADR ENDING IN 0
  1025. *
  1026. PRINT$MESSAGE:
  1027.     XTHL        ; SAVE HL AND SET HL TO MESSAGE
  1028. PM1:
  1029.     MOV    A,M    ; GET BYTE
  1030.     INX    H    ; PT TO NEXT
  1031.     ORA    A    ; DONE?
  1032.     JZ    PM2
  1033.     CALL    CHAR$OUT    ; PRINT
  1034.     JMP    PM1
  1035. PM2:
  1036.     XTHL        ; RESTORE HL AND RET ADR
  1037.     RET
  1038.  
  1039. *
  1040. *  CAPITALIZE CHAR IN A
  1041. *
  1042. CAPS:
  1043.     ANI    7FH    ; MASK OUT MSB
  1044.     CPI    61H    ; SMALL A
  1045.     RC
  1046.     CPI    7BH    ; SMALL B + 1
  1047.     RNC
  1048.     ANI    5FH    ; CAPITALIZE
  1049.     RET
  1050.  
  1051. *
  1052. *  MOVE (HL) TO (DE) FOR (B) BYTES
  1053. *
  1054. MOVE:
  1055.     MOV    A,M    ; GET
  1056.     STAX    D    ; PUT
  1057.     INX    H    ; PT TO NEXT
  1058.     INX    D
  1059.     DCR    B    ; COUNT DOWN
  1060.     JNZ    MOVE
  1061.     RET
  1062.  
  1063. *
  1064. *  FILL (HL) FOR (B) BYTES WITH (A)
  1065. *
  1066. FILL:
  1067.     MOV    M,A    ; PUT
  1068.     INX    H    ; PT TO NEXT
  1069.     DCR    B    ; COUNT DOWN
  1070.     JNZ    FILL
  1071.     RET
  1072.  
  1073. *
  1074. *  BUFFERS
  1075. *
  1076.     DS    100    ; WHY NOT?
  1077. STACK    DS    2    ; OLD SP
  1078.  
  1079. LINELEN    EQU    20    ; 20 CHARS/INPUT LINE
  1080. INLINE:    DB    LINELEN    ; LENGTH OF INPUT LINE
  1081.     DS    1    ; RETURNED LINE LENGTH
  1082.     DS    LINELEN    ; INPUT LINE
  1083.     DS    4    ; BUFFER
  1084.  
  1085. DIR1:
  1086.     DS    2    ; DIR1 PTR
  1087. ORDER:
  1088.     DS    2    ; ORDER TABLE PTR
  1089. UDRIVE:
  1090.     DS    1    ; USER-SELECTED DRIVE NUMBER (1-16)
  1091. TAG$FLAG:
  1092.     DS    1    ; TAG FLAG (0=NO TAGS SPECIFIED)
  1093. INSP$FLAG:
  1094.     DS    1    ; INSPECT FLAG (0=NO)
  1095. SET$FLAG:
  1096.     DS    1    ; MANUALLY SET ATTRIBUTES FLAG (0=NO)
  1097. FILE$COUNT:
  1098.     DS    2    ; NUMBER OF FILES SELECTED
  1099. ECOUNTER:
  1100.     DS    2    ; COUNTER FOR PUT$ENTRY
  1101. PTPTR:
  1102.     DS    2    ; SORT PTR
  1103. PTDIR1:
  1104.     DS    2    ; SORT DIR1 PTR
  1105. GAP:
  1106.     DS    2    ; SORT NUMBER
  1107. I:
  1108.     DS    2    ; SORT NUMBER
  1109. J:
  1110.     DS    2    ; SORT NUMBER
  1111. JG:
  1112.     DS    2    ; SORT NUMBER
  1113. KEY$BUFFER:
  1114.     DS    11    ; KEY BUFFER
  1115. HOLD:
  1116.     DS    ENTRY$SIZE    ; HOLD BUFFER FOR SORT
  1117. FCBX:
  1118.     DS    40    ; FCB BUFFER FOR CHANGE
  1119.  
  1120. *
  1121. *  BEGINNING OF DYNAMIC BUFFER REGION
  1122. *
  1123. ENDALL    EQU    $/256*256+256    ; PAGE BOUNDARY
  1124.  
  1125.     END
  1126.