home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / PROGRAMS / SORT / BSORT.LBR / BSORT.AQM / BSORT.ASM
Assembly Source File  |  2000-06-30  |  40KB  |  2,057 lines

  1. ;Array exchange sort in place in memory.
  2. ;Copyright 1979,1980,1981 by C. E. Duncan. Written 1979 June 30.
  3. ;Taken from an original program written by E. W. Dijkstra on
  4. ;  the back of my business card at the international meeting
  5. ;  on software reliability at Los Angeles in 1975.
  6. ;Permission granted to copy for any non-commercial use.
  7. ;Revised 17:55 1981 February 10.
  8. ;
  9. ;This program, BSORT, is called as a CP/M .COM routine as follows:
  10. ;
  11. ;    BSORT <input file name> <output file name>
  12. ;
  13. ;  where file names are "[d:]name.typ" as usual in CP/M.
  14. ;  The user will be asked for record length and sort parameters
  15. ;  through a console dialog.
  16. ;
  17.     PAGE    0        ;defeats CP/M page count
  18.     ORG    0100H        ;program origin
  19. BSORT:
  20. ;
  21. ;Set internal stacks
  22.     LXI    H,BSTACK        ;bounds stack
  23.     SHLD    BSAVE
  24.     LXI    H,PSTACK        ;program stack
  25.     SHLD    PSAVE
  26.     SPHL
  27. ;Initialize
  28.     CALL    INIT1
  29. ;Save default disk
  30.     MVI    C,RTCDK        ;return current disk number
  31.     CALL    BDOS
  32.     STA    CDSKSAV
  33. ;Set default disk to input file
  34.     LDA    SFDN        ;input file disk number
  35.     MOV    E,A
  36.     CALL    ASGDSK
  37. ;Read file and check further
  38.     CALL    INIT2
  39. ;Do the sort
  40.     CALL    PARTIT
  41. ;Assign output disk as default
  42.     LDA    DFDN
  43.     MOV    E,A
  44.     CALL    ASGDSK
  45. ;Write output file
  46.     CALL    WRTARY
  47. ;Close output
  48.     LXI    D,DFCB
  49.     MVI    C,CLOSE
  50.     CALL    BDOS
  51. ;Restore default disk
  52.     LDA    CDSKSAV
  53.     MOV    E,A
  54.     CALL    ASGDSK
  55.     JMP    QUIT        ;return to CP/M-CCP
  56. ;
  57. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  58. INIT1:
  59. ;Initialize variables, read parameters, check values.
  60. ;Revised 20:10 1981 January 26.
  61. ;
  62. ;Move output file name to output FCB.
  63.     MVI    A,12        ;character count
  64.     LXI    D,SFDA        ;from
  65.     LXI    H,DFDN        ;to
  66.     CALL    SMOVE
  67. ;Set current (next) record pointer to 0.
  68.     XRA    A
  69.     STA    SFCR        ;input
  70.     STA    DFCR        ;output
  71.     STA    DFEX        ;file extent
  72.     STA    ABRTF        ;abort flags
  73.     STA    STLV        ;bounds stack level
  74. ;Check input file name
  75.     LXI    D,SFCB        ;input FCB
  76.     CALL    CHKFN
  77.     JNC    OK01
  78.     LXI    H,ABRTF        ;set abort flag
  79.     INR    M
  80.     LXI    D,FNIMSG
  81.     CALL    PUTMSG
  82. OK01:
  83. ; Check output file name
  84.     LXI    D,DFCB        ;output FCB
  85.     CALL    CHKFN
  86.     JNC    OK02
  87.     LXI    H,ABRTF        ;set abort flag
  88.     INR    M
  89.     LXI    D,FNOMSG
  90.     CALL    PUTMSG
  91. ;
  92. OK02:
  93. ;Abort if these names do not check
  94.     LDA    ABRTF
  95.     ORA    A
  96.     JNZ    ABORT
  97. ; Calculate storage available
  98.     LXI    H,AR        ;array base address
  99.     SHLD    ARBASE
  100.     XCHG            ;to DE
  101.     LHLD    BDOS+1        ;BDOS base
  102.     CALL    DIFF2        ;subtract
  103.     DCR    H        ;make room for temporary storage
  104.     SHLD    MARSIZ        ;available memory
  105. ; Check size of input file
  106.     MVI    A,03FH        ; "?" to insure match of
  107.     STA    SFEX        ;   all extents
  108.     LXI    H,0        ; Reset sector count
  109.     SHLD    FSCNT
  110.     LXI    D,SBUF        ; Prepare a buffer for
  111.     MVI    C,STDMAAD    ;   directory information
  112.     CALL    BDOS
  113.     MVI    C,SRCHFST    ; Bring in directory for first
  114.     LXI    D,SFCB        ;   extent. Returns 0,1,2 or 3
  115.     CALL    BDOS        ;   in 2.2, 0-3F in 1.4
  116.     CPI    0FFH
  117.     JNZ    OK03
  118.     LXI    D,FNPMSG    ;  not found so quit
  119.     CALL    PUTMSG
  120.     JMP    ABORT
  121. OK03:
  122.     ANI    3        ; MOD 4 (needed by CP/M 1.4 only)
  123. ;Address directory entry (one of four in buffer), then
  124. ;  get sector count.  32 bytes per entry.
  125.     ADD    A        ; *2
  126.     ADD    A        ; *4
  127.     ADD    A        ; *8
  128.     ADD    A        ; *16
  129.     ADD    A        ; *32
  130.     MVI    D,15        ; plus offset to count byte
  131.     ADD    D
  132.     MOV    E,A        ; add buffer base address
  133.     MVI    D,0
  134.     LXI    H,SBUF
  135.     DAD    D
  136.     MOV    A,M        ; sector count
  137.     MOV    E,A
  138.     LHLD    FSCNT        ; accumulate
  139.     DAD    D
  140.     SHLD    FSCNT
  141.     CPI    080H        ; Full track?
  142.     JNZ    OK04        ;   no, go on
  143.     MVI    C,SRCHNXT    ; Get information on
  144.     LXI    D,SFCB        ;   next extent
  145.     CALL    BDOS
  146.     CPI    0FFH        ; No more entries when FF hex
  147.     JNZ    OK03        ; Get next entry
  148. OK04:
  149.     XRA    A        ; Reset extent byte
  150.     STA    SFEX        ;   to zero
  151. ; Deduce input file size
  152.     LHLD    FSCNT        ; number of sectors
  153.     MOV    A,H        ; check for empty
  154.     ORA    L
  155.     JZ    ABORT        ; nothing here
  156. ;Multiply by 128 bytes per sector
  157.     DAD    H        ; *2
  158.     JC    OK05
  159.     DAD    H        ; *4
  160.     JC    OK05
  161.     DAD    H        ; *8
  162.     JC    OK05
  163.     DAD    H        ; *16
  164.     JC    OK05
  165.     DAD    H        ; *32
  166.     JC    OK05
  167.     DAD    H        ; *64
  168.     JC    OK05
  169.     DAD    H        ; *128
  170.     JNC    OK06
  171. OK05:
  172.     LXI    D,MULMSG    ; multiply error
  173.     CALL    PUTMSG
  174.     JMP    ABORT
  175. OK06:
  176. ; Last sector may have less than 128 bytes, will check later
  177.     SHLD    BYIF
  178. ; Check that there is enough memory
  179.     XCHG
  180.     LHLD    MARSIZ        ;memory available
  181.     CALL    DIFF2
  182.     ORA    A
  183.     JP    OK07
  184.     LXI    D,FSZMSG    ; report file larger than memory
  185.     CALL    PUTMSG
  186.     JMP    ABORT
  187. OK07:
  188. ;Calculate address of temporary record storage area
  189.     LHLD    BYIF
  190.     LXI    D,AR
  191.     DAD    D
  192.     SHLD    AWTP
  193. ; Open input file
  194.     MVI    C,OPEN
  195.     LXI    D,SFCB
  196.     CALL    BDOS
  197.     INR    A
  198.     JNZ    OK08
  199.     LXI    D,FNPMSG    ; file not present
  200.     CALL    PUTMSG
  201.     JMP    ABORT
  202. ;Open output file
  203. OK08:
  204.     MVI    C,DELETE    ;delete file of same name
  205.     LXI    D,DFCB
  206.     CALL    BDOS
  207.     MVI    C,CREATE    ;make new file
  208.     LXI    D,DFCB
  209.     CALL    BDOS
  210.     INR    A
  211.     JNZ    OK09
  212.     LXI    D,NDSMSG    ; signal no directory space
  213.     CALL    PUTMSG
  214.     JMP    ABORT
  215. OK09:
  216. ; Ask for record length
  217.     LXI    D,RCLMSG
  218.     CALL    PUTMSG
  219.     CALL    READCON        ;read console input
  220.     LXI    D,CONSIZ    ;string response
  221.     LXI    B,AR        ;temporary buffer
  222.     CALL    SCANBR        ;extract number
  223.     JC    OK09        ;try again
  224.     LDAX    B        ;count
  225.     INX    B        ;1st character
  226.     CALL    ROW1NBR        ;convert to binary
  227.     JC    OK09        ;trouble
  228.     MOV    A,L
  229.     STA    RLEN
  230. ; calculate twos complement
  231.     CMA
  232.     INR    A
  233.     STA    MRLEN
  234. OK10:
  235. ; Ask for sort parameters.
  236.     XRA    A        ; reset parameter count
  237.     STA    NBRFND
  238.     LXI    D,PARMSG
  239.     CALL    PUTMSG
  240.     CALL    READCON        ;read console input
  241.     LDA    CONSIZ        ;number of characters read
  242.     ORA    A
  243.     JZ    OK10        ;no input, try again
  244.     CALL    RDPARM        ;read, convert and store sort parms
  245.     JC    OK10        ;try again
  246.     CALL    CKPARM        ;check parameters
  247.     JC    OK10        ;ask again
  248.     RET            ;end of INIT1
  249. ;
  250. ;* * * * * * * * * * * * * * * * * * * * * * * * * * *
  251. ;
  252. INIT2:
  253. ;Read input file to array, correct file size, final checks.
  254. ;Written by C. E. Duncan 1979 June 30.
  255. ;Revised 08:15 1981 February 4.
  256. ; Read input file to array
  257.     CALL    RDARRAY
  258. ; Check and possibly correct file size calculation BYIF
  259. ; HL points to byte after last sector read.
  260.     LDA    SFS1        ; bytes remain in last sector
  261.     ORA    A
  262.     JNZ    OK12
  263.     MVI    A,01AH        ; must remove eof (1A) bytes
  264.     LXI    B,0        ; clear counter
  265. OK11:
  266.     DCX    H        ; char in file
  267.     CMP    M        ; is it EOF?
  268.     JNZ    OK13        ; no
  269.     INX    B        ; count
  270.     JMP    OK11
  271. OK12:
  272.     CMA            ; subtract off eofs
  273.     MOV    C,A
  274.     MVI    B,0FFH        ; minus sign
  275.     JMP    OK14
  276. OK13:
  277.     MOV    A,B        ; get twos complement
  278.     CMA            ;   to subtract
  279.     MOV    B,A
  280.     MOV    A,C
  281.     CMA            ; subtract off unused bytes
  282.     MOV    C,A
  283. OK14:
  284.     INX    B        ; twos complement
  285.     LHLD    BYIF
  286.     DAD    B        ; subtract
  287.     SHLD    BYIF
  288. ; Check that file size is multiple of record length
  289. ;  and calculate upper and lower bounds
  290.     MOV    A,H        ; check that there is a record
  291.     ORA    L
  292.     JZ    ABORT        ; nothing here
  293.     LDA    RLEN
  294.     CALL    DIV12
  295.     JNC    OK15
  296.     LXI    D,DIVMSG
  297.     CALL    PUTMSG
  298.     JMP    ABORT
  299. OK15:
  300.     MOV    A,L        ; check remainder
  301.     CMP    H
  302.     JZ    OK16
  303.     LXI    D,RLMSG        ;abort msg
  304.     CALL    PUTMSG
  305.     JMP    ABORT
  306. OK16:
  307.     MOV    H,B        ; store quotient
  308.     MOV    L,C        ;   as UPB
  309.     SHLD    AUPB
  310.     SHLD    CUPB
  311.     LXI    H,1        ; LWB = 1
  312.     SHLD    ALWB
  313.     SHLD    CLWB
  314. ;Initialize array index calculation
  315. ;  A[I] has address = ARRAY BASE - (LWB A)*RLEN + I*RLEN
  316. ;                   = ARIF + I*RLEN
  317. ;
  318.     LHLD    ALWB        ;LWB of A
  319.     XCHG            ;   to DE
  320.     LDA    RLEN
  321.     CALL    MUL12
  322.     JC    ABORT        ;overflow
  323.     XCHG
  324.     LHLD    ARBASE        ; calculate HL - DE
  325.     CALL    DIFF2
  326.     ORA    A        ; check sign
  327.     JP    OK17        ; positive, ok
  328.     MOV    A,H        ; complement if negative
  329.     CMA
  330.     MOV    H,A
  331.     MOV    A,L
  332.     CMA
  333.     MOV    L,A
  334.     INX    H
  335. OK17:
  336.     SHLD    ARIF
  337. ;Addresses of sort strings in temporary area
  338.     LHLD    AWTP        ; temporary record store
  339.     LDA    POOF1        ; 1st sort offset
  340.     MVI    B,0
  341.     MOV    C,A
  342.     DAD    B
  343.     SHLD    KWTP1        ; address of awtp[m:n]
  344.     LDA    PARM3        ; is there a 2nd sort?
  345.     ORA    A
  346.     RZ
  347.     LDA    POOF2        ; 2nd sort offset
  348.     MOV    C,A
  349.     LHLD    AWTP
  350.     DAD    B
  351.     SHLD    KWTP2        ; address of awtp[u:v]
  352.     RET
  353. ;
  354. ;* * * * * * * * * * * * * * * * * * * * * * * * * * *
  355. ;
  356. PARTIT:
  357. ;Partition sort based on a program by Dijkstra.
  358. ;Written by C. E. Duncan 1979 June 30.
  359. ;Revised 17:30 1981 February 8.
  360. ;
  361. ;   p = LWB a,  q = UPB a
  362. ;
  363. ;Algorithm:  partition and sort until q < p
  364. ;    WHILE p <= q
  365. ;    DO
  366. ;      IF q = p
  367. ;      THEN
  368. ;        unstack
  369. ;      ELSE
  370. ;        IF q - p <= slim
  371. ;        THEN
  372. ;            shorts        {insertion sort}
  373. ;        ELSE
  374. ;          parta        {partition left}
  375. ;        {makes two partitions: a[p] to a[s] and a[r] to a[q]}
  376. ;        FI
  377. ;      FI;
  378. ;      IF s = p
  379. ;      THEN
  380. ;        p := r
  381. ;      ELSE
  382. ;        IF s < p
  383. ;        THEN
  384. ;          partb        {partition right}
  385. ;        {required if parta has no "small" element}
  386. ;        ELSE
  387. ;          IF q = r
  388. ;          THEN
  389. ;        q := s
  390. ;          ELSE
  391. ;        IF q < r
  392. ;        THEN
  393. ;          unstack
  394. ;        ELSE
  395. ;          IF q - r > s - p
  396. ;          THEN
  397. ;            stack right;
  398. ;            q := s
  399. ;          ELSE
  400. ;            stack left;
  401. ;            p := r
  402. ;          FI
  403. ;        FI
  404. ;          FI
  405. ;        FI
  406. ;      FI
  407. ;    OD
  408. ;
  409.     LHLD    CLWB        ; P = LWB current partition 
  410.     XCHG        
  411.     LHLD    CUPB        ; Q = UPB current partition
  412.     CALL    DIFF2        ; compare
  413.     ORA    A    
  414.     RM            ;sort complete when Q < P
  415.     JZ    UNSTACK        ; only one element
  416.     XCHG
  417.     LXI    H,SLIM        ;low size limit
  418.     CALL    DIFF2        ;SLIM - (P - Q)
  419.     ORA    A
  420.     PUSH    PSW
  421.     CP    SHORTS        ;use insertion sort, small partition
  422.     POP    PSW
  423.     JP    UNSTACK        ;this partition completed
  424.     CM    PARTA        ;partition leftward
  425. ; Check size of lower partition
  426. STAR01:
  427.     LHLD    CLWB        ; P = LWB left
  428.     XCHG
  429.     LHLD    PS        ; S = UPB left
  430.     CALL    DIFF2        ; S - P
  431.     ORA    A
  432.     JZ    STAR02        ; only one element, finished
  433.     JM    STAR04        ; no small element
  434.     SHLD    SMP
  435. ; Upper partition.
  436.     LHLD    PR        ; R = LWB right
  437.     XCHG
  438.     LHLD    CUPB        ; Q = UPB right
  439.     CALL    DIFF2        ; Q - R
  440.     ORA    A
  441.     JZ    STAR03        ; only one element, finished
  442.     JM    UNSTACK        ; finished with this partition
  443.                 ;  because no large element after
  444.                 ;  having no small element.
  445.     SHLD    QMR
  446. ; Save bounds of larger partition.
  447. ; If Q - R > S - P then upper part is larger.
  448.     LXI    H,0        ; save program stack
  449.     DAD    SP
  450.     SHLD    PSAVE
  451.     LHLD    BSAVE        ; retrieve bounds stack
  452.     SPHL
  453. ;
  454.     LHLD    QMR        ; Q - R
  455.     XCHG
  456.     LHLD    SMP        ; S - P
  457.     CALL    DIFF2        ; (S-P) - (Q-R)
  458.     ORA    A
  459.     JM    STHI        ; stack bunds for high side
  460. STLO:
  461.     LHLD    CLWB        ; P, new lower bound
  462.     PUSH    H
  463.     LHLD    NUBL        ; S, new upper bound
  464.     PUSH    H
  465.     LHLD    NLBH        ; R, set new LWB for high side
  466.     SHLD    CLWB
  467.     JMP    REST        ; restore program stack
  468. STHI:
  469.     LHLD    NLBH        ; R, new lower bound
  470.     PUSH    H
  471.     LHLD    CUPB        ; Q, new upper bound
  472.     PUSH    H
  473.     LHLD    NUBL        ; S, new upper bouond for low side
  474.     SHLD    CUPB
  475. REST:
  476.     LXI    H,0        ; restore program stack
  477.     DAD    SP
  478.     SHLD    BSAVE
  479.     LHLD    PSAVE
  480.     SPHL
  481.     LXI    H,STLV        ; increment stack level
  482.     INR    M
  483.     JMP    PARTIT        ; process next partition
  484. ;
  485. ; Process upper part
  486. STAR02:
  487.     LHLD    PR        ; R is new lower bound
  488.     SHLD    CLWB        ;
  489.     JMP     PARTIT        ;
  490. STAR03:
  491. ; Process lower part
  492.     LHLD    PS        ; S is new upper bound
  493.     SHLD    CUPB        ;
  494.     JMP    PARTIT        ;
  495. STAR04:
  496. ; Partition again, using R <= T and S > T in place of
  497. ;   R < T and S >= T respectively.
  498.     CALL    PARTB        ;
  499.     JMP    STAR01        ;
  500. ;
  501. UNSTACK:
  502. ; Recover bounds of next section to be partitioned
  503.     LXI    H,STLV        ; check level
  504.     DCR    M        ; 
  505.     RM            ; stack empty, sort completed
  506.     LXI    H,0        ; save program stack
  507.     DAD    SP        ;
  508.     SHLD    PSAVE        ;
  509.     LHLD    BSAVE        ; get bounds stack
  510.     SPHL            ;
  511.     POP    H        ;
  512.     SHLD    CUPB        ; UPB
  513.     POP    H        ;
  514.     SHLD    CLWB        ; LWB
  515.     LXI    H,0        ; restore program stack
  516.     DAD    SP        ;
  517.     SHLD    BSAVE        ;
  518.     LHLD    PSAVE        ;
  519.     SPHL            ;
  520.     JMP    PARTIT        ; return, do next section
  521. ;
  522. PARTA:
  523. ;Re-arrange array AR into two partitions the left of which contains
  524. ;  elements which precede a pivot element, and the right contains
  525. ;  those which do not.
  526. ;Written by C. E. Duncan 1979 June 30.
  527. ;Revised 15:06 1981 January 31.
  528. ;
  529. ;   R = LWB A,  S = UPB A,  T = (R+S) OVER 2.
  530. ;
  531. ;Algorithm:
  532. ;    WHILE LWB A <= R < S <= UPB A
  533. ;    DO
  534. ;      SWAP A[R] and A[S];
  535. ;      WHILE A[R] precedes A[T]
  536. ;      DO
  537. ;        R +:= 1
  538. ;      OD;
  539. ;      WHILE A[S] does not precede A[T]
  540. ;      DO
  541. ;        S -:= 1
  542. ;      OD
  543. ;    OD
  544. ;
  545. ;Calculate addresses
  546.     LDA    POOF1        ; 1st sort parameter offset
  547.     MVI    B,0
  548.     MOV    C,A
  549.     LHLD    CLWB        ; current LWB
  550.     SHLD    PR        ; R
  551.     XCHG
  552.     CALL    INDXR        ; calculate address
  553.     SHLD    ACR        ; .A[R]
  554.     DAD    B
  555.     SHLD    AQR1        ; .A[R][M:N], 1st sort string
  556.     LHLD    CUPB        ; current LWB
  557.     SHLD    PS        ; S
  558.     XCHG
  559.     CALL    INDXR
  560.     SHLD    ACS        ; .A[S]
  561.     DAD    B
  562.     SHLD    AQS1        ; .A[S][M:N]
  563.     LHLD    PR        ; R
  564.     XCHG
  565.     LHLD    PS        ; S
  566.     DAD    D        ; R + S
  567.     CALL    SHRHL        ; divide by 2
  568.     XCHG
  569.     CALL    INDXR        ; .A[T]
  570.     XCHG            ; move A[T], the pivot element, to
  571.     LHLD    AWTP        ;   a safe place
  572.     LDA    RLEN
  573.     CALL    SMOVE
  574. ; Take care of possible 2nd sort substring
  575.     LDA    PARM3
  576.     ORA    A
  577.     JZ    PAR01        ;not needed
  578.     LDA    POOF2        ;2nd ss offset
  579.     MVI    B,0
  580.     MOV    C,A
  581.     LHLD    ACR        ; .A[R]
  582.     DAD    B
  583.     SHLD    AQR2        ; .A[R][V:W]
  584.     LHLD    ACS
  585.     DAD    B
  586.     SHLD    AQS2        ; .A[S][V:W]
  587. PAR01:
  588. ;Check if finished
  589.     LHLD    PS        ; S
  590.     XCHG
  591.     LHLD    PR        ; R
  592.     CALL    DIFF2        ; R - S
  593.     ORA    A
  594.     JP    PAR03        ; finished
  595. ; Update addresses of A[R] and A[S]
  596.     LHLD    PR        ; R
  597.     XCHG
  598.     CALL    INDXR
  599.     SHLD    ACR        ; .A[R]
  600.     LHLD    PS        ; S
  601.     XCHG
  602.     CALL    INDXR
  603.     SHLD    ACS        ; .A[S]
  604. ; Swap
  605.     LDA    RLEN
  606.     LHLD    ACR        ; .A[R]
  607.     XCHG
  608.     LHLD    ACS        ; .A[S]
  609.     CALL    SWAP
  610. ;  While A[R] precedes A[T], etc.
  611.     LHLD    AQR1        ; .A[R][M:N]
  612.     XCHG
  613. PAR01A:
  614.     LDA    SPL1        ;1st sort length
  615.     LHLD    KWTP1        ; .A[T][M:N]
  616.     XCHG
  617.     CALL    CMPSRW
  618.     ORA    A
  619.     JZ    PAR04        ; check 2nd sort substring
  620. PAR01B:
  621.     PUSH    PSW
  622.     LDA    SSEQ1        ; check direction
  623.     ORA    A
  624.     JZ    PAR01C        ; ascending
  625.     POP    PSW        ; descending
  626.     JZ    PAR02
  627.     JP    PAR01D        ; A[R] precedes A[T], down
  628.     JMP    PAR02
  629. PAR01C:
  630.     POP    PSW
  631.     JP    PAR02        ; A[R] does not precede A[T], up
  632. PAR01D:
  633.     LHLD    PR        ; increment R
  634.     INX    H
  635.     SHLD    PR
  636.     LDA    RLEN
  637.     MVI    B,0
  638.     MOV    C,A
  639.     LDA    PARM3        ;2nd sort?
  640.     ORA    A
  641.     JZ    PAR01E        ;no
  642.     LHLD    AQR2        ;update .A[R][V:W], 2nd sort string
  643.     DAD    B
  644.     SHLD    AQR2
  645. PAR01E:
  646.     LHLD    AQR1        ;update .A[R][M:N] 1st sort
  647.     DAD    B
  648.     SHLD    AQR1
  649.     XCHG
  650.     JMP    PAR01A
  651. PAR02:
  652. ; While A[S] does not precede A[T] etc.
  653.     LHLD    AQS1        ; .A[S][M:N]
  654.     XCHG
  655. PAR02A:
  656.     LDA    SPL1        ; length of 1st sort
  657.     LHLD    KWTP1        ; 1st sort string address
  658.     XCHG
  659.     CALL    CMPSRW
  660.     ORA    A
  661.     JZ    PAR05        ; check 2nd sort
  662. PAR02B:
  663.     PUSH    PSW
  664.     LDA    SSEQ1        ; check direction
  665.     ORA    A
  666.     JZ    PAR02C        ; ascending
  667.     POP    PSW
  668.     JM    PAR02D
  669.     JZ    PAR02D
  670.     JMP    PAR01        ; S precedes T
  671. PAR02C:
  672.     POP    PSW
  673.     JM    PAR01
  674. PAR02D:
  675.     LHLD    PS        ; decrement S
  676.     DCX    H
  677.     SHLD    PS
  678. ; Check array bound at lower limit, S < LWB
  679.     XCHG
  680.     LHLD    CLWB        ; P = LWB A
  681.     XCHG
  682.     CALL    DIFF2        ; S - P
  683.     ORA    A
  684.     JM    PAR03        ; no small element
  685. ;Update addresses for next comparison
  686.     LDA    MRLEN        ; minus RLEN
  687.     MVI    B,0FFH
  688.     MOV    C,A
  689.     LDA    PARM3        ;check for 2nd sort
  690.     ORA    A
  691.     JZ    PAR02E        ;no
  692.     LHLD    AQS2
  693.     DAD    B
  694.     SHLD    AQS2
  695. PAR02E:
  696.     LHLD    AQS1
  697.     DAD    B        ; reduce address by RLEN
  698.     SHLD    AQS1
  699.     XCHG
  700.     JMP    PAR02A
  701. PAR03:
  702.     LHLD    PR
  703.     SHLD    NLBH        ; new LWB for right partition
  704.     LHLD    PS
  705.     SHLD    NUBL        ; new UPB for left partition
  706.     RET
  707. PAR04:
  708.     LDA    PARM3
  709.     ORA    A
  710.     JZ    PAR01B        ;no 2nd sort
  711.     LHLD    KWTP2
  712.     XCHG
  713.     LHLD    AQR2
  714.     LDA    SPL2
  715.     CALL    CMPSRW
  716.     ORA    A
  717.     PUSH    PSW
  718.     LDA    SSEQ2
  719.     ORA    A
  720.     JZ    PAR04A        ; ascending
  721.     POP    PSW
  722.     JZ    PAR02
  723.     JP    PAR01D
  724.     JMP    PAR02
  725. PAR04A:
  726.     POP    PSW
  727.     JM    PAR01D
  728.     JMP    PAR02        ; this one is out of order
  729. ;
  730. PAR05:
  731.     LDA    PARM3        ; is there a 2nd sort?
  732.     ORA    A
  733.     JZ    PAR02B        ; no
  734.     LHLD    KWTP2
  735.     XCHG
  736.     LHLD    AQS2
  737.     LDA    SPL2
  738.     CALL    CMPSRW
  739.     ORA    A
  740.     PUSH    PSW
  741.     LDA    SSEQ2
  742.     ORA    A
  743.     JZ    PAR05A
  744.     POP    PSW
  745.     JZ    PAR02D
  746.     JM    PAR02D
  747.     JMP    PAR01
  748. PAR05A:
  749.     POP    PSW
  750.     JP    PAR02D
  751.     JMP    PAR01
  752. ;
  753. PARTB:
  754. ;Re-arrange array A into two partitions, the right of which contains
  755. ;  elements which follow a pivot element, and the left contains those
  756. ;  which do not.
  757. ;Written by C. E. Duncan 1979 June 30.
  758. ;Revised 18:50 1981 February 8.
  759. ;
  760. ;   R = LWB A,  S = UPB A,  T = (R+S) OVER 2
  761. ;
  762. ;Algorithm:
  763. ;    WHILE LWB A <= R < S <= UPB A
  764. ;    DO
  765. ;      SWAP A[R] and A[S];
  766. ;      WHILE A[R] does not follow A[T]
  767. ;      DO
  768. ;        R +:= 1
  769. ;      OD;
  770. ;      WHILE A[S] follows A[T]
  771. ;      DO
  772. ;        S -:= 1
  773. ;      OD
  774. ;    OD
  775. ;
  776. ; Calculate addresses
  777.     LDA    POOF1        ; 1st sort offset
  778.     MVI    B,0
  779.     MOV    C,A
  780.     LHLD    CLWB        ; current LWB A
  781.     SHLD    PR        ; R
  782.     XCHG
  783.     CALL    INDXR
  784.     SHLD    ACR        ; .A[R]
  785.     DAD    B        ; .A[R][M:N]
  786.     SHLD    AQR1
  787.     LHLD    CUPB
  788.     SHLD    PS        ; S
  789.     XCHG
  790.     CALL    INDXR
  791.     SHLD    ACS        ; .A[S]
  792.     DAD    B
  793.     SHLD    AQS1        ; .A[S][M:N]
  794.     LHLD    PR        ; R
  795.     XCHG
  796.     LHLD    PS        ; S
  797.     DAD    D        ; R+S
  798.     CALL    SHRHL        ; shift right, OVER 2
  799.     XCHG
  800.     CALL    INDXR        ; address of A[T]
  801.     XCHG            ; move A[T] to a safe place
  802.     LHLD    AWTP
  803.     LDA    RLEN
  804.     CALL    SMOVE
  805. ; Take care of 2nd sort substring
  806.     LDA    PARM3        ; is there one?
  807.     ORA    A
  808.     JZ    PAB01        ; no
  809.     LDA    POOF2        ; offset
  810.     MVI    B,0
  811.     MOV    C,A
  812.     LHLD    ACR
  813.     DAD    B
  814.     SHLD    AQR2        ; .A[R][V:W]
  815.     LHLD    ACS
  816.     DAD    B
  817.     SHLD    AQS2        ; .A[S][V:W]
  818. PAB01:
  819. ; Check completion
  820.     LHLD    PS        ; S
  821.     XCHG
  822.     LHLD    PR        ; R
  823.     CALL DIFF2        ; R-S
  824.     ORA    A
  825.     JP    PAB03        ; finished
  826. ; Update addresses of A[R] and A[S]
  827.     LHLD    PR        ; R
  828.     XCHG
  829.     CALL    INDXR
  830.     SHLD    ACR        ; .A[R]
  831.     LHLD    PS        ; S
  832.     XCHG
  833.     CALL    INDXR
  834.     SHLD    ACS        ; .A[S]
  835. ; Swap Elements with indices R and S
  836.     LDA    RLEN
  837.     LHLD    ACR        ; .A[R]
  838.     XCHG
  839.     LHLD    ACS        ; .A[S]
  840.     CALL    SWAP
  841. ; While A[R] does not follow A[T] increment R.
  842.     LHLD    AQR1        ; .A[R][M:M]
  843.     XCHG
  844. PAB01A:
  845.     LDA    SPL1        ; length sort 1
  846.     LHLD    KWTP1        ; .A[T][M:N]
  847.     CALL    CMPSRW
  848.     ORA    A
  849.     JZ    PAB04        ; check 2nd sort
  850. PAB01B:
  851.     PUSH    PSW
  852.     LDA    SSEQ1        ; direction
  853.     ORA    A
  854.     JZ    PAB01C
  855.     POP    PSW        ; descending
  856.     JM    PAB01D
  857.     JZ    PAB01D
  858.     JMP    PAB02
  859. PAB01C:
  860.     POP    PSW
  861.     JM    PAB02
  862. PAB01D:
  863.     LHLD    PR        ; R
  864.     INX    H
  865.     SHLD    PR
  866. ; Check upper bound in case no large element
  867.     XCHG
  868.     LHLD    CUPB        ; Q = UPB A
  869.     CALL    DIFF2
  870.     ORA    A
  871.     JM    PAB03        ; upper limit, no large element
  872. ; Update addresses, etc.
  873.     LDA    RLEN
  874.     MVI    B,0
  875.     MOV    C,A
  876.     LDA    PARM3
  877.     ORA    A        ; 2nd sort
  878.     JZ    PAB01E        ; no
  879.     LHLD    AQR2        ; .A[R][V:W]
  880.     DAD    B
  881.     SHLD    AQR2
  882. PAB01E:
  883.     LHLD    AQR1
  884.     DAD    B
  885.     SHLD    AQR1
  886.     XCHG
  887.     JMP    PAB01A
  888. PAB02:
  889. ;While A[S] follows A[T] decrease S, etc.
  890.     LHLD    AQS1        ; .A[S][M:N]
  891.     XCHG
  892. PAB02A:
  893.     LDA    SPL1
  894.     LHLD    KWTP1
  895.     CALL    CMPSRW
  896.     ORA    A
  897.     JZ    PAB05        ; check for 2nd sort
  898. PAB02B:
  899.     PUSH    PSW
  900.     LDA    SSEQ1
  901.     ORA    A
  902.     JZ    PAB02C
  903.     POP    PSW
  904.     JZ    PAB01
  905.     JP    PAB02D
  906.     JMP    PAB01        ; A[S] <= A[T]
  907. PAB02C:
  908.     POP    PSW
  909.     JP    PAB01
  910. PAB02D:
  911.     LHLD    PS        ; decrement S
  912.     DCX    H
  913.     SHLD    PS
  914.     LDA    MRLEN
  915.     MVI    B,0FFH
  916.     MOV    C,A
  917.     LDA    PARM3        ; 2nd sort?
  918.     ORA    A
  919.     JZ    PAB02E        ; no
  920.     LHLD    AQS2
  921.     DAD    B
  922.     SHLD    AQS2
  923. PAB02E:
  924.     LHLD    AQS1
  925.     DAD    B
  926.     SHLD    AQS1
  927.     XCHG
  928.     JMP    PAB02A
  929. ;
  930. PAB03:
  931.     LHLD    PR        
  932.     SHLD    NLBH        ; new LWB for right partition
  933.     LHLD    PS
  934.     SHLD    NUBL        ; new UPB for left partition
  935.     RET
  936. PAB04:
  937.     LDA    PARM3        ; 2nd sort?
  938.     ORA    A
  939.     JZ    PAB01B        ; no
  940.     LHLD    AQR2
  941.     XCHG
  942.     LHLD    KWTP2
  943.     LDA    SPL2
  944.     CALL    CMPSRW
  945.     ORA    A
  946.     PUSH    PSW
  947.     LDA    SSEQ2
  948.     ORA    A
  949.     JZ    PAB04A
  950.     POP    PSW
  951.     JM    PAB01D
  952.     JZ    PAB01D
  953.     JMP    PAB02
  954. PAB04A:
  955.     POP    PSW
  956.     JP    PAB01D
  957.     JMP    PAB02
  958. ;
  959. PAB05:
  960.     LDA    PARM3
  961.     ORA    A
  962.     JZ    PAB02B
  963.     LHLD    AQS2
  964.     XCHG
  965.     LHLD    KWTP2
  966.     LDA    SPL2
  967.     CALL    CMPSRW
  968.     ORA    A
  969.     PUSH    PSW
  970.     LDA    SSEQ2
  971.     ORA    A
  972.     JZ    PAB05A
  973.     POP    PSW
  974.     JZ    PAB01
  975.     JP    PAB02D
  976.     JMP    PAB01
  977. PAB05A:
  978.     POP    PSW
  979.     JM    PAB02D
  980.     JMP    PAB01
  981. ;
  982. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  983. ;
  984. SHORTS:
  985. ;Insertion sort for small partitions.
  986. ;Written by C.  E. Duncan 1980 February 16, from Knuth volume 3
  987. ;  (Searching and Sorting) page 81.
  988. ;Revised 12:30 1981 February 8.
  989. ;
  990. ;Algorithm:
  991. ;
  992. ;    FOR j FROM 2 TO UPB(a)
  993. ;    DO
  994. ;       IF a[j] < a[j - 1]
  995. ;       THEN
  996. ;          awtp := a[j];
  997. ;          FOR i FROM j - 1 BY -1 TO LWB(a)
  998. ;            WHILE at < a[i]
  999. ;          DO
  1000. ;          a[i + 1] := a[i];
  1001. ;          k := i
  1002. ;          OD;
  1003. ;          a[k] := awtp
  1004. ;       FI
  1005. ;    OD
  1006. ;
  1007. ;Initialize indices and addresses.
  1008.     LHLD    CLWB        ;LWB of current partition
  1009.     SHLD    PS        ; j
  1010.     XCHG
  1011.     CALL    INDXR
  1012.     SHLD    ACS        ; address of a[j] = a[LWB]
  1013.     LDA    POOF1        ;1st sort offset
  1014.     MVI    B,0
  1015.     MOV    C,A
  1016.     DAD    B
  1017.     SHLD    AQS1        ;address of a[LWB][m:n]
  1018.     LDA    PARM3
  1019.     ORA    A
  1020.     JZ    SH01
  1021.     LDA    POOF2        ;2nd sort offset
  1022.     MOV    C,A
  1023.     LHLD    ACS
  1024.     DAD    B
  1025.     SHLD    AQS2        ;address of a[LWB][u:v]
  1026. SH01:
  1027. ;Increment j, compare to UPB, set i := j - 1
  1028.     LHLD    PS        ; j - 1
  1029.     SHLD    PR        ; i := j - 1
  1030.     INX    H        ; j +:= 1
  1031.     SHLD    PS        ; j
  1032. ;Check that j <= UPB
  1033.     XCHG
  1034.     LHLD    CUPB        ; UPB of parttion
  1035.     CALL    DIFF2        ;UPB - j
  1036.     ORA    A
  1037.     RM            ;finished when J > UPB
  1038. ;Update addresses
  1039.     LHLD    ACS        ;old .a[j]
  1040.     SHLD    ACR        ;new .a[i]
  1041.     LDA    RLEN
  1042.     MVI    B,0
  1043.     MOV    C,A
  1044.     DAD    B
  1045.     SHLD    ACS        ; new a[j]
  1046. ;Update sort string addresses
  1047.     LHLD    AQS1
  1048.     SHLD    AQR1        ; a[i][m:n]
  1049.     DAD    B
  1050.     SHLD    AQS1        ; a[j][m:n]
  1051.     LDA    PARM3
  1052.     ORA    A
  1053.     JZ    SH02
  1054.     LHLD    AQS2
  1055.     SHLD    AQR2        ; a[i][u:v]
  1056.     DAD    B
  1057.     SHLD    AQS2        ; a[j][u:v]
  1058. SH02:
  1059. ;Compare a[j] with a[j - 1] = a[i]
  1060.     LHLD    AQR1
  1061.     XCHG
  1062.     LHLD    AQS1
  1063.     LDA    SPL1
  1064.     CALL    CMPSRW
  1065.     ORA    A
  1066.     JZ    SH05        ; check 2nd sort
  1067. SH03:
  1068.     PUSH    PSW
  1069.     LDA    SSEQ1        ; check direction
  1070.     ORA    A
  1071.     JNZ    SH04
  1072.     POP    PSW
  1073.     JM    SH07        ; have to do some moves
  1074.     JMP    SH01        ; ok where it is, go to next j
  1075. SH04:
  1076.     POP    PSW
  1077.     JM    SH01        ; ok as is
  1078.     JZ    SH01        ;  ditto
  1079.     JMP    SH07
  1080. SH05:
  1081. ;Second compare for a[j] and a[j - 1]
  1082.     LDA    PARM3
  1083.     ORA    A
  1084.     JZ    SH03        ; no 2nd compare
  1085.     LHLD    AQR2
  1086.     XCHG
  1087.     LHLD    AQS2
  1088.     LDA    SPL2
  1089.     CALL    CMPSRW
  1090.     ORA    A
  1091.     PUSH    PSW
  1092.     LDA    SSEQ2
  1093.     ORA    A
  1094.     JNZ    SH06
  1095.     POP    PSW
  1096.     JM    SH07
  1097.     JMP    SH01
  1098. SH06:
  1099.     POP    PSW
  1100.     JM    SH01
  1101.     JZ    SH01
  1102.     JMP    SH07
  1103. ;
  1104. SH07:
  1105. ;Move a[j] to a safe place: awtp := a[j]
  1106.     LHLD    ACS
  1107.     XCHG            ; from
  1108.     LHLD    AWTP        ; to
  1109.     LDA    RLEN
  1110.     CALL    SMOVE
  1111. SH08:
  1112. ;Move a[i] up one place to position i + 1
  1113.     LDA    RLEN
  1114.     MVI    B,0
  1115.     MOV    C,A
  1116.     LHLD    ACR        ; a[i]
  1117.     MOV    D,H        ;   to DE, from
  1118.     MOV    E,L
  1119.     DAD    B        ; a[i + 1]
  1120.     CALL    SMOVE
  1121. ;Decrement i, check against LWB
  1122.     LHLD    CLWB        ; LWB
  1123.     XCHG
  1124.     LHLD    PR        ; i
  1125.     DCX    H        ; i -:= 1
  1126.     SHLD    PR
  1127.     CALL    DIFF2        ; i - LWB
  1128.     ORA    A
  1129.     JM    SH14        ; at LWB, hence a[LWB] := awtp
  1130. ;Decrement addresses and compare again
  1131.     LDA    MRLEN        ; negative record length
  1132.     MVI    B,0FFH
  1133.     MOV    C,A
  1134.     LHLD    ACR
  1135.     DAD    B
  1136.     SHLD    ACR        ; new address of a[i]
  1137.     LHLD    AQR1
  1138.     DAD    B
  1139.     SHLD    AQR1        ; new 1st sort address
  1140.     LDA    PARM3
  1141.     ORA    A
  1142.     JZ    SH09
  1143.     LHLD    AQR2
  1144.     DAD    B
  1145.     SHLD    AQR2        ; new 2nd sort address
  1146. SH09:
  1147. ;Compare awtp = a[j] with a[i]
  1148.     LHLD    AQR1
  1149.     XCHG
  1150.     LHLD    KWTP1
  1151.     LDA    SPL1
  1152.     CALL    CMPSRW
  1153.     ORA    A
  1154.     JZ    SH12
  1155. SH10:
  1156.     PUSH    PSW
  1157.     LDA    SSEQ1
  1158.     ORA    A
  1159.     JNZ    SH11
  1160.     POP    PSW
  1161.     JM    SH08        ; keep trying and comparing
  1162.     JMP    SH15        ; found place for at in a[i + 1]
  1163. SH11:
  1164.     POP    PSW
  1165.     JM    SH15
  1166.     JZ    SH15
  1167.     JMP    SH08
  1168. ;
  1169. SH12:
  1170. ;Second compare for awtp = a[j] and a[i]
  1171.     LDA    PARM3
  1172.     ORA    A
  1173.     JZ    SH10
  1174.     LHLD    AQR2
  1175.     XCHG
  1176.     LHLD    KWTP2
  1177.     LDA    SPL2
  1178.     CALL    CMPSRW
  1179.     ORA    A
  1180.     PUSH    PSW
  1181.     LDA    SSEQ2
  1182.     ORA    A
  1183.     JNZ    SH13
  1184.     POP    PSW
  1185.     JM    SH08
  1186.     JMP    SH15
  1187. SH13:
  1188.     POP    PSW
  1189.     JM    SH15
  1190.     JZ    SH15
  1191.     JMP    SH08
  1192. ;
  1193. SH14:
  1194. ;Move awtp = a[j] into slot at a[LWB]
  1195.     LHLD    AWTP
  1196.     XCHG
  1197.     LDA    RLEN
  1198.     LHLD    ACR
  1199.     CALL    SMOVE
  1200.     JMP    SH01
  1201. SH15:
  1202. ;Move awtp = a[j] into slot at a[i + 1]
  1203.     LHLD    AWTP
  1204.     XCHG
  1205.     LDA    RLEN
  1206.     MVI    B,0
  1207.     MOV    C,A
  1208.     LHLD    ACR
  1209.     DAD    B
  1210.     CALL    SMOVE
  1211.     JMP    SH01
  1212. ;
  1213. ;* * * * * * * * * * * * * * * * * * * * * *
  1214. ;
  1215. ABORT:
  1216. ;Return to CP/M
  1217.     LXI    D,ABMSG
  1218.     CALL    PUTMSG
  1219.     JMP    QUIT
  1220. ;
  1221. ASGDSK:
  1222. ;Assign default disk for faster input and output.
  1223. ;  Must have desired disk number in E, and default disk number
  1224. ;  in location CDSKSAV.
  1225. ;Written by C. E. Duncan 1981 January 28.
  1226.     XRA    A        ;get zero
  1227.     CMP    E
  1228.     JNZ    ASGD1
  1229.     LDA    CDSKSAV        ;need default disk
  1230.     MOV    E,A
  1231.     JMP    ASGD2
  1232. ASGD1:
  1233.     DCR    E        ;A-P = 1-16 become 0-15
  1234. ASGD2:
  1235.     MVI    C,SELDK        ;select disk
  1236.     CALL    BDOS
  1237.     RET
  1238. ;
  1239. CHAROW:
  1240. ;Reset carry if character in C is present in row of character
  1241. ;  whose address is in DE, length in B, else set carry.
  1242. ;  Return position number in B.
  1243. ;Written by C. E. Duncan 1981 January 23.
  1244. ;Revised 09:00 1981 January 28.
  1245.     MOV    A,B            ;row length
  1246.     CPI    0            ;check zero length
  1247.     JZ    CHAR2
  1248.     XCHG                ;row address in HL
  1249.     MOV    A,C            ;character sought
  1250.     MVI    D,0            ;position count
  1251. CHAR1:
  1252.     INR    D            ;count
  1253.     CMP    M            ;is this it?
  1254.     JZ    CHAR3            ;yes
  1255.     DCR    B            ;count off row
  1256.     INX    H            ;next permitted
  1257.     JNZ    CHAR1            ;more
  1258. CHAR2:
  1259.     STC                ;signal not found
  1260.     RET
  1261. CHAR3:
  1262.     MOV    B,D            ;position number
  1263.     ORA    A            ;found, reset carry
  1264.     RET
  1265. ;
  1266. CHKFN:
  1267. ; Check file name for legal characters, FCB address in DE.
  1268. ; Written by C. E. Duncan 1980 February 7.
  1269. ; Revised 05:30 1981 February 4.
  1270.     LDAX    D        ;drive
  1271.     CPI    5        ;no more than 4 drives
  1272.     JNC    CHKFN2        ;out of limits
  1273.     MVI    B,11        ; Number of characters to check
  1274.     INX    D        ;first character
  1275.     LDAX    D        ; must be non-blank
  1276.     CPI    021H        ;
  1277.     JC    CHKFN2        ; not acceptable
  1278.     JMP    CHKFN3        ;
  1279. CHKFN1:                ;
  1280.     INX    D        ; next character
  1281.     LDAX    D        ;
  1282.     CPI    020H        ; blank
  1283.     JC     CHKFN2        ; control character
  1284. CHKFN3:
  1285.     CPI    05BH        ; [
  1286.     JNC    CHKFN2        ; also unacceptable
  1287.     DCR    B        ; count
  1288.     JNZ    CHKFN1        ; return for next
  1289.     XRA    A        ; signal ok
  1290.     RET            ;
  1291. CHKFN2:                ;
  1292.     STC            ; signal presence of
  1293.     RET            ;   unacceptable character
  1294. ;
  1295. CKPARM:
  1296. ;Check sort parameters. Each parameter one byte from PARM1.
  1297. ;Written by C. E. Duncan 1981 January 21.
  1298. ;Revised 13:37 1981 January 28.
  1299. ;
  1300. ;get parameters in registers
  1301.     LXI    H,PARM1        ;address parameters
  1302.     MOV    B,M
  1303.     INX    H
  1304.     MOV    C,M
  1305.     INX    H
  1306.     MOV    D,M
  1307.     INX    H
  1308.     MOV    E,M
  1309.     LDA    RLEN
  1310.     MOV    H,A
  1311. ;check parameters <= RLEN
  1312.     MOV    L,B
  1313.     CALL    KPR        ;check range of parm1
  1314.     RC            ;out of limits
  1315.     MOV    L,C
  1316.     CALL    KPR        ;check parm2
  1317.     RC
  1318.     MOV    A,D        ;is there a 2nd sort range?
  1319.     CPI    0
  1320.     JZ    KRR        ;no
  1321.     MOV    L,D
  1322.     CALL    KPR        ;check parm3
  1323.     RC
  1324.     MOV    L,E
  1325.     CALL    KPR
  1326.     RC
  1327.     JMP    KRR
  1328. KPR:
  1329.     MOV    A,L
  1330.     CPI    1
  1331.     RC            ;< 1
  1332.     MOV    A,H
  1333.     SUB    L
  1334.     RET            ;carry set if > RLEN
  1335. KRR:
  1336. ;Calculate sort string lengths and check them
  1337.     MOV    A,C        ;1st
  1338.     SUB    B
  1339.     RC            ;negative length
  1340.     INR    A
  1341.     STA    SPL1        ;length of 1st sort substring
  1342.     MOV    L,A
  1343.     MOV    A,H        ;RLEN
  1344.     SUB    L
  1345.     RC            ;substring longer than record
  1346.     MOV    A,B        ;PARM1
  1347.     DCR    A
  1348.     STA    POOF1        ;offset of sort substring in record
  1349.     MOV    A,D        ;PARM3
  1350.     ORA    A
  1351.     RZ            ;ok return, only one substring
  1352. ;Have 2nd sort substring
  1353.     MOV    L,A
  1354.     MOV    A,E        ;PARM4
  1355.     SUB    L
  1356.     RC            ;negative length
  1357.     INR    A
  1358.     STA    SPL2
  1359.     MOV    L,A
  1360.     MOV    A,H
  1361.     SUB    L
  1362.     RC            ;longer than RLEN
  1363.     MOV    A,D
  1364.     DCR    A
  1365.     STA    POOF2        ;offset
  1366. ;Check for sort field overlap
  1367.     MOV    A,E        ;PARM4
  1368.     SUB    B        ;PARM1
  1369.     JC    KRS        ;ok
  1370.     MOV    A,C        ;PARM2
  1371.     SUB    D        ;PARM3
  1372.     JC    KRS        ;ok
  1373.     STC            ;overlap
  1374.     RET
  1375. KRS:
  1376.     XRA    A        ;ok, reset carry
  1377.     RET
  1378. ;
  1379. CMPSRW:
  1380. ;Compare two rows of character of equal length.
  1381. ;Registers DE and HL have addresses of the two rows of character,
  1382. ;  register A the count.  Return -1, 0, +1 in register A as HL < DE, 
  1383. ;  HL = DE, HL > DE respectively.
  1384. ;Written by C. E. Duncan 1981 January 26.
  1385.     MOV    B,A        ;count
  1386.     INR    B
  1387. CMPSRWA:
  1388.     DCR    B
  1389.     JZ    CMPSRWEQ    ;equal
  1390.     LDAX    D
  1391.     CMP    M
  1392.     JC    CMPSRWGT    ;HL > DE
  1393.     JNZ    CMPSRWLT    ;HL < DE
  1394.     INX    D        ;equal so far
  1395.     INX    H
  1396.     JMP    CMPSRWA
  1397. CMPSRWGT:
  1398.     MVI    A,1
  1399.     RET
  1400. CMPSRWEQ:
  1401.     XRA    A
  1402.     RET
  1403. CMPSRWLT:
  1404.     MVI    A,-1
  1405.     RET
  1406. ;
  1407. DIFF2:
  1408. ;Calculate difference of integers in DE and HL.  Put absolute
  1409. ;   difference in HL.  Signal DE < HL, DE = HL, DE > HL with
  1410. ;   +1, 0 -1 in A.
  1411. ;Written by C. E. Duncan 1980 February 18.
  1412. ;Revised 13:30 1981 January 29.
  1413.     MOV    A,D    
  1414.     CMP    H    
  1415.     JC    DIF1        ; DE < HL
  1416.     JNZ    DIF2        ; DE > HL
  1417.     MOV    A,E    
  1418.     CMP    L    
  1419.     JC    DIF1        ; DE < HL
  1420.     JNZ    DIF2        ; DE > HL
  1421.     LXI    H,0        ; DE = HL
  1422.     XRA    A        ; reset carry to signal equal
  1423.     RET
  1424. DIF1:
  1425.     MVI    B,1        ; signal DE < HL
  1426.     JMP    DIF3
  1427. DIF2:
  1428.     MVI    B,0FFH        ; signal DE > HL
  1429.     XCHG    
  1430. DIF3:
  1431. ; Do subtraction
  1432.     MOV    A,L
  1433.     SUB    E
  1434.     MOV    L,A
  1435.     MOV    A,H
  1436.     SBB    D
  1437.     MOV    H,A
  1438.     MOV    A,B        ; restore signal
  1439.     STC            ; set carry to signal not equal
  1440.     RET
  1441. ;
  1442. DIV12:
  1443. ;Divide 16 bit integer in HL by eight bit SHORT INT in A;
  1444. ;   return 16 bit quotient in BC, remainder in HL (L).
  1445. ;20:05 10 February 1980.
  1446.     ORA    A        ;test for zero divisor
  1447.     JZ    DIV03        ;
  1448.     PUSH    A        ;save divisor
  1449.     CMA            ;twos complement
  1450.     INR    A        ;  of divisor
  1451.     MOV    E,A        ; to DE
  1452.     MVI    D,0FFH        ;propagate negative sign
  1453.     LXI    B,0        ;clear quotient
  1454. DIV01:
  1455.     DAD    D        ;divide by subtraction
  1456.     JNC    DIV02        ;
  1457.     INX    B        ;
  1458.     JMP     DIV01        ;
  1459. DIV02:
  1460.     POP    A        ;prepare
  1461.     MOV    E,A        ;  remainder
  1462.     MVI    D,0        ;  in HL
  1463.     DAD    D        ;
  1464.     ORA    A        ;reset carry to
  1465.     RET            ;   signal ok
  1466. DIV03:
  1467.     STC            ;signal zero
  1468.     RET            ;   divisor
  1469. ;
  1470. GETNBR:
  1471. ;Extract an ASCII number (sequence of digits) from a row of character.
  1472. ;  Enter with row address in DE, count in BUFCNT.  Return with
  1473. ;  DE pointing to following characters, remaining count in BUFCNT
  1474. ;  and extracted number converted to binary in C.  Carry set if
  1475. ;  unsuccessful, else reset.
  1476. ;Written by C. E. Duncan 1981 January 27.
  1477. ;Revised 08:00 1981 January 28.
  1478.     LDA    BUFCNT        ;get count
  1479.     ORA    A
  1480.     JNZ    GETN01
  1481. GETN00:
  1482.     STC            ;signal zero length in or out
  1483.     RET
  1484. GETN01:
  1485.     LXI    B,AR-1        ;temporary store
  1486.     XCHG
  1487.     INR    A        ;count + 1
  1488.     MOV    D,A        ; to D
  1489.     MVI    E,0        ;output count
  1490.     DCX    H
  1491. GETN02:
  1492.     INX    H        ;next character
  1493.     DCR    D        ;count
  1494.     JZ    GETN04        ;finished
  1495.     MOV    A,M
  1496.     CPI    030H
  1497.     JC    GETN02        ;ignore
  1498.     CPI    03AH
  1499.     JNC    GETN02
  1500. ;Have found a digit
  1501. GETN03:
  1502.     INX    B        ;ASCII number output
  1503.     STAX    B
  1504.     INR    E        ;count output
  1505.     INX    H        ;address of next character
  1506.     DCR    D        ;count input
  1507.     JZ    GETN04        ;finished
  1508.     MOV    A,M
  1509.     CPI    030H
  1510.     JC    GETN04        ;finished
  1511.     CPI    03AH
  1512.     JC    GETN03
  1513. ;Windup
  1514. GETN04:
  1515.     MOV    A,D
  1516.     STA    BUFCNT        ;remaining input count
  1517.     PUSH    H        ;save current row address
  1518.     MOV    A,E        ;output count
  1519.     LXI    B,AR        ;recover output address
  1520.     CALL    ROW1NBR        ;convert ASCII number at BC to binary
  1521.     POP    D        ;recover address
  1522.     JC    GETN00        ;problems
  1523.     MOV    C,L
  1524.     RET            ;binary number in C
  1525. ;
  1526. INDXR:
  1527. ;Get address of array element with index given in DE. Return
  1528. ;   address of element in HL.  Array base address is stored in
  1529. ;   location ARBASE, RLEN, the record length is less than 256.
  1530. ;   Address of AR[i] is given by ARIF + I*RLEN.
  1531. ;   Index is checked against bounds.
  1532.     PUSH    D
  1533.     LHLD    ALWB        ;check LWB
  1534.     DCX    H
  1535.     CALL    DIFF2
  1536.     ORA    A
  1537.     JM    IND02        ; LWB <= I
  1538. IND01:
  1539.     LXI    D,INXMSG    ;report index out
  1540.     CALL    PUTMSG        ;  of bounds
  1541.     JMP    ABORT
  1542. IND02:
  1543.     LHLD    AUPB        ;check UPB
  1544.     POP    D
  1545.     PUSH    D
  1546.     CALL    DIFF2
  1547.     ORA    A
  1548.     JM    IND01        ;abort
  1549.     POP    D        ;index ok, I <= UPB
  1550.     LDA    RLEN
  1551.     CALL    MUL12
  1552.     JC    ABORT        ;overflow
  1553.     XCHG
  1554.     LHLD    ARIF
  1555.     DAD    D
  1556.     RET
  1557. ;
  1558. MU111:
  1559. ;Multiply 8-bit number in E by 8-bit number in A, returning
  1560. ;  8-bit number in L. Set carry for overflow, else reset.
  1561. ;Written by C. E. Duncan 1981 January 24.
  1562.     LXI    H,0        ;zero result register
  1563.     MVI    D,0        ;for double add
  1564.     MVI    B,8        ;bit count
  1565. MU111A:
  1566.     DAD    H        ;shift HL left
  1567.     RAL            ;same for multiplier
  1568.     JNC    MU111B
  1569.     DAD    D
  1570. MU111B:
  1571.     DCR    B        ;count
  1572.     JNZ    MU111A        ;get next bit
  1573.     XRA    A        ;check for overflow
  1574.     CMP    H
  1575.     RET            ;carry set if H > 0
  1576. ;
  1577. MUL12:
  1578. ;Multiply 16-bit number in DE by 8-bit number in A, placing
  1579. ;  16-bit result in HL.  Carry set for overflow, else reset.
  1580. ;Revised 22:22 1980 February 25.
  1581.     LXI    H,0        ;clear result register
  1582.     MVI    B,8        ;bit count
  1583. MUL12A:
  1584.     DAD    H        ;shift left
  1585.     RAL            ;same for multiplier
  1586.     JNC    MUL12B        ;this multiplier bit = 0
  1587.     DAD    D        ;add multiplicand
  1588.     RC            ;carry indicates overflow
  1589. MUL12B:
  1590.     DCR    B        ;count bits
  1591.     JNZ    MUL12A        ;continue
  1592.     ORA    A        ;ok, reset carry
  1593.     RET
  1594. ;
  1595. PUTMSG:
  1596. ; Write message to console via BDOS, address in DE
  1597.     PUSH    D        ;Save message address
  1598.     LXI    D,CCRLF        ;CR and LF
  1599.     MVI    C,PCONBUF    ;
  1600.     CALL    BDOS        ;
  1601.     POP    D        ;recover message
  1602.     MVI    C,PCONBUF    ;Signal write to console
  1603.     CALL    BDOS        ;
  1604.     RET            ;
  1605. ;
  1606. RDARRAY:
  1607. ;Read disk file of typed, fixed length records to array AR.
  1608. ;Written by C. E. Duncan 1980 February 3.
  1609. ;Revised 08:30 1981 February 4.
  1610. ; Initialize
  1611.     LXI    B,0FF80H    ; -128
  1612.     LXI    H,AR        ; array base
  1613.     DAD    B        ;
  1614.     PUSH    H        ;
  1615.     LXI    H,0        ; Zero sector count
  1616.     SHLD    RSCNT        ;
  1617. ; Read loop
  1618. RDAL:                ;
  1619. ; Set DMA address
  1620.     LXI    B,128        ; step pointer
  1621.     POP    H        ;
  1622.     DAD    B        ;
  1623.     PUSH    H        ;
  1624.     XCHG            ; DMA addr in DE for BDOS
  1625.     MVI    C,STDMAAD    ;
  1626.     CALL    BDOS        ;
  1627. ; Read a sector
  1628.     LXI    D,SFCB        ; Address FCB
  1629.     MVI    C,READSEQ    ; 
  1630.     CALL    BDOS        ;
  1631.     CPI    0        ; check successful completion
  1632.     JNZ    RD1        ; check further
  1633.     LHLD    RSCNT        ; ok, count 
  1634.     INX    H        ;
  1635.     SHLD    RSCNT        ;
  1636.     JMP    RDAL        ; return for next sector
  1637. RD1:    CPI    1        ;
  1638.     JZ    RD2        ; end of file
  1639.     JMP     ABORT        ; should not happen
  1640. RD2:
  1641. ; Read complete
  1642.     POP    H        ; Restore stack
  1643.     RET            ;
  1644. ;
  1645. RDPARM:
  1646. ;Read parameters from console and store in suitable form.
  1647. ;Written by C. E. Duncan 1981 January 27.
  1648. ;Revised 12:20 1981 January 28.
  1649.     LXI    D,CONSIZ    ;console buffer
  1650.     LDAX    D        ;count
  1651.     ORA    A
  1652.     JZ    RDPFIN        ;no input
  1653.     STA    BUFCNT        ;count of unprocessed characters
  1654.     INX    D        ;1st character
  1655. RDP1:
  1656.     LDAX    D        ;examine character
  1657.     ORI    020H        ;convert to lower case
  1658.     PUSH    D        ;save row address
  1659.     MVI    B,14        ;count of acceptable characters
  1660.     MOV    C,A        ;character to be tested
  1661.     LXI    D,PRMCHRS    ;list of ok characters
  1662.     CALL    CHAROW        ;is it acceptable?
  1663.     MOV    A,C        ;recover character
  1664.     POP    D
  1665.     JNC    RDP3        ;ok
  1666. RDP2:
  1667.     INX    D        ;point to next character
  1668.     LXI    H,BUFCNT    ;update count
  1669.     DCR    M
  1670.     JNZ    RDP1        ;keep trying
  1671.     JMP    RDPFIN        ;no more
  1672. RDP3:
  1673.     CPI    'a'        ;ascending?
  1674.     JZ    RDP7        ;yes, no action
  1675.     CPI    'd'        ;descending?
  1676.     JNZ    RDP9        ;must be a number
  1677.     LDA    NBRFND        ;which parameter?
  1678.     CPI    2        ;is it 3rd?
  1679.     JZ    RDP4        ;must be 5th or 6th
  1680.     CPI    4
  1681.     JZ    RDP5
  1682.     CPI    5
  1683.     JZ    RDP5
  1684.     JMP    RDP2        ;ignore
  1685. RDP4:
  1686.     LXI    H,SSEQ1
  1687.     JMP    RDP6
  1688. RDP5:
  1689.     LXI    H,SSEQ2
  1690. RDP6:
  1691.     INR    M        ;set descending
  1692. RDP7:
  1693.     LXI    H,NBRFND    ;update parameter count
  1694.     INR    M
  1695.     JMP    RDP2        ;return for more
  1696. RDP8:
  1697.     LXI    H,NBRFND    ;update number of parameters found
  1698.     INR    M
  1699.     LDA    BUFCNT        ;check for remaining characters
  1700.     ORA    A
  1701.     JZ    RDPFIN
  1702.     JMP    RDP1        ;process next character
  1703. RDP9:
  1704.     CALL    GETNBR        ;return binary in C, update buffer
  1705.     LDA    NBRFND        ;parameter count
  1706.     CPI    0
  1707.     JNZ    RDP10
  1708.     MOV    A,C
  1709.     STA    PARM1
  1710.     JMP    RDP8
  1711. RDP10:
  1712.     CPI    1
  1713.     JNZ    RDP11
  1714.     MOV    A,C
  1715.     STA    PARM2
  1716.     JMP    RDP8
  1717. RDP11:
  1718.     CPI    2
  1719.     JNZ    RDP13
  1720. RDP12:
  1721.     MOV    A,C
  1722.     STA    PARM3
  1723.     JMP    RDP8
  1724. RDP13:
  1725.     CPI    3
  1726.     JNZ    RDP14
  1727.     LDA    PARM3
  1728.     ORA    A
  1729.     JZ    RDP12
  1730. RDP14:
  1731.     MOV    A,C
  1732.     STA    PARM4
  1733.     JMP    RDP8
  1734. RDPFIN:
  1735.     LDA    NBRFND        ;all done?
  1736.     CPI    2        ;at least 2
  1737.     RET            ;carry set if not
  1738. ;
  1739. READCON:
  1740. ;Read console to console buffer CONBUF.
  1741.     LXI    D,CONBUF
  1742.     MVI    C,RCONBUF
  1743.     CALL    BDOS
  1744.     RET
  1745. ;
  1746. ROW1NBR:
  1747. ;Convert ASCII decimal row at (BC), length A, to 1-byte number
  1748. ; in L. Set carry for overflow.
  1749. ;Copyright 1980 by C. E. Duncan.
  1750. ;Revised 12:20 1981 January 24.
  1751.     CPI    4        ;check size
  1752.     JNC    RTN1A        ;
  1753.     CPI    0        ;
  1754.     JNZ    RTN1B        ;
  1755. RTN1A:
  1756.     STC            ;signal trouble
  1757.     RET            ;
  1758. RTN1B:
  1759.     MOV    D,A        ;count
  1760.     MVI    L,0        ;reset result register
  1761.     MVI    E,10        ;multiplier
  1762. RTN1C:
  1763.     MOV    A,L        ;multiply by 10
  1764.     PUSH    B        ;
  1765.     PUSH    D        ;
  1766.     CALL    MU111        ;A * E to L
  1767.     POP    D        ;
  1768.     POP    B        ;
  1769.     JC    RTN1A        ;overflow
  1770.     LDAX    B        ;next digit
  1771.     SUI    30H        ;convert to binary
  1772.     JM    RTN1A        ;not a digit
  1773.     CPI    10        ;
  1774.     JNC    RTN1A        ;not a digit
  1775.     ADD    L        ;
  1776.     MOV    L,A        ;
  1777.     INX    B        ;next
  1778.     DCR    D        ;count
  1779.     JNZ    RTN1C        ;continue
  1780.     RET            ;
  1781. ;
  1782. SCANBR:
  1783. ;Extract an ASCII number (sequence of digits) from a string.
  1784. ;  Enter with address of string in DE.  Leave with BC pointing
  1785. ;  to extracted ASCII number string, and DE pointing to remaining
  1786. ;  row of characters with count in A.
  1787. ;  String = LCCC...C.
  1788. ;Written by C. E. Duncan 1981 January 23.
  1789.     LDAX    D        ;get count
  1790.     ORA    A
  1791.     JNZ    SCNB01
  1792. SCNB00:
  1793.     STC            ;signal zero length in or out
  1794.     RET
  1795. SCNB01:
  1796.     PUSH    B        ;output string origin
  1797.     XCHG
  1798.     INR    A        ;count + 1
  1799.     MOV    D,A        ; to D
  1800.     MVI    E,0        ;output count
  1801. SCNB02:
  1802.     DCR    D        ;count
  1803.     JZ    SCNB04        ;finished
  1804.     INX    H        ;next character
  1805.     MOV    A,M
  1806.     CPI    030H
  1807.     JC    SCNB02        ;ignore
  1808.     CPI    03AH
  1809.     JNC    SCNB02
  1810. ;Have found a digit
  1811. SCNB03:
  1812.     INX    B        ;ASCII number output
  1813.     STAX    B
  1814.     INR    E        ;count output
  1815.     DCR    D        ;count input
  1816.     JZ    SCNB04        ;finished
  1817.     INX    H        ;next input character
  1818.     MOV    A,M
  1819.     CPI    030H
  1820.     JC    SCNB04        ;finished
  1821.     CPI    03AH
  1822.     JNC    SCNB04        ;finished
  1823.     JMP    SCNB03
  1824. ;Windup
  1825. SCNB04:
  1826.     POP    B        ;recover output origin
  1827.     MOV    A,E        ;output count
  1828.     ORA    A        ;test for zero length
  1829.     JZ    SCNB00
  1830.     STAX    B
  1831.     MOV    A,D        ;input count remaining
  1832.     XCHG
  1833.     RET            ;ok, carry reset
  1834. ;
  1835. SHRHL:
  1836. ;Shift HL right one bit :=: divide HL by 2.
  1837. ;Written by C.  E. Duncan 1979 June 30.
  1838.     ANA    A        ;clear carry
  1839.     MOV    A,H
  1840.     RAR
  1841.     MOV    H,A
  1842.     MOV    A,L
  1843.     RAR
  1844.     MOV    L,A
  1845.     RET
  1846. ;
  1847. SMOVE:
  1848. ;Non-overlapping move, left to right.
  1849. ;Register A has count of bytes, < 256, DE address of source and
  1850. ;  HL address of destination.
  1851. ;Written by C. E. Duncan 1980 February 18.
  1852. ;Revised 17:30 1981 January 26.
  1853.     MOV    B,A        ;count
  1854.     INR    B
  1855. SMOVE1:
  1856.     DCR    B
  1857.     RZ
  1858.     LDAX    D
  1859.     MOV    M,A
  1860.     INX    D
  1861.     INX    H
  1862.     JMP    SMOVE1
  1863. ;
  1864. SWAP:
  1865. ;Exchange two rows-of-character of equal length, addresses in
  1866. ;   DE and HL, length in A.
  1867. ;Written by C. E. Duncan 1980 February 18.
  1868. ;Revised 08:40 1981 February 4.
  1869.     ORA    A        ;check length
  1870.     RZ            ;finished
  1871.     MOV    B,A        ;count
  1872. SWAP1:
  1873.     MOV    C,M        ;save byte from HL
  1874.     LDAX    D        ;move byte from
  1875.     MOV    M,A        ;  DE to HL
  1876.     MOV    A,C        ;move byte from C
  1877.     STAX    D        ;  (from HL) to DE
  1878.     INX    D
  1879.     INX    H
  1880.     DCR    B
  1881.     JNZ    SWAP1
  1882.     RET
  1883. ;
  1884. WRTARY:
  1885. ; Write array to disk file from AR.
  1886. ; Written 1980 February 17.
  1887. ; Revised 17:45 1981 January 28.
  1888. ; Initialize
  1889.     LXI    B,0FF80H    ; -128
  1890.     LXI    H,AR        ; array base
  1891.     DAD    B        ;
  1892.     PUSH    H        ; array pointer
  1893.     LHLD    RSCNT        ; sector count
  1894.     INX    H
  1895.     PUSH    H
  1896.     LXI    B,128        ;DMA address increment
  1897. WRAL:
  1898. ; Check count of sectors remaining
  1899.     POP    D        ; get count
  1900.     DCX    D        ; count
  1901.     MOV    A,D
  1902.     ORA    E
  1903.     JNZ    WR1        ; more
  1904.     POP    H        ; restore stack
  1905.     RET            ;finished
  1906. WR1:
  1907. ; Set DMA address
  1908.     POP    H
  1909.     LXI    B,128
  1910.     DAD    B
  1911.     PUSH    H
  1912.     PUSH    D        ; count
  1913.     XCHG
  1914.     MVI    C,STDMAAD
  1915.     CALL    BDOS
  1916. ; Write sector
  1917.     LXI    D,DFCB        ; output FCB
  1918.     MVI    C,WRITSEQ    ; sequential write
  1919.     CALL    BDOS
  1920.     CPI    0
  1921.     JZ    WRAL        ; ok, continue
  1922. ; Abort because of disk problems
  1923.     LXI    H,ABRTF        ; Abort flags
  1924.     MOV    A,M
  1925.     ORI    80H
  1926.     MOV    M,A        ; write failure
  1927.     JMP    ABORT        ; quit
  1928. ;
  1929. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1930. ;
  1931. ;Patch area
  1932. PATCH    DS    48
  1933. ;
  1934. ;Equates, literals and storage for ISORT.
  1935. ;Written by C. E. Duncan 1979 June 30.
  1936. ;Revised 07:25 1981 February 4.
  1937. ;
  1938. ;Console messages
  1939. ;
  1940. CR:    EQU    13        ;Carriage return
  1941. LF:    EQU    10        ;Line feed
  1942. CCRLF:    DB    CR,LF,'$'
  1943. FNIMSG:    DB    'Unacceptable character in input file name.$'
  1944. FNOMSG:    DB    'Unacceptable character in output file name.$'
  1945. FNPMSG:    DB    'Input file not present.$'
  1946. NDSMSG:    DB    'No directory space for output file.$'
  1947. RCLMSG:    DB    'Enter record length: $'
  1948. PARMSG:    DB    'Enter sort parameters: $'
  1949. ABMSG:    DB    'Program discontinued.$'
  1950. MULMSG:    DB    'Overflow in multiply.$'
  1951. DIVMSG:    DB    'Divide by zero.$'
  1952. RLMSG:    DB    'File size not multiple of record length.$'
  1953. FSZMSG:    DB    'File larger than available memory.$'
  1954. INXMSG:    DB    'Array index out of bound.$'
  1955. ;
  1956. ; Storage
  1957. ;
  1958. PRMCHRS: DB    '0123456789ad'    ;permitted parameters
  1959. ;
  1960. ARBASE:    DW    0    ;array base address
  1961. RLEN:    DB    0    ; record length - bytes
  1962. ALWB:    DW    0    ; array lower bound
  1963. AUPB:    DW    0    ; array upper bound
  1964. CLWB:    DW    0    ; current lower bound
  1965. CUPB:    DW    0    ; current upper bound
  1966. BUFCNT:    DB    0    ;characters in buffer
  1967. NBRFND:    DB    0    ;parameter number
  1968. QMR:    DW    0    ; Q - R
  1969. SMP:    DW    0    ; S - P
  1970. PARM1:    DB    0    ; sort parameters
  1971. PARM2:    DB    0    ;
  1972. PARM3:    DB    0    ;
  1973. PARM4:    DB    0    ;
  1974. POOF1:    DB    0    ;1st sort substr offset
  1975. POOF2:    DB    0    ;2nd sort substr offset
  1976. SPL1:    DB    0    ;1st sort substr length
  1977. SPL2:    DB    0    ;2nd sort substr length
  1978. SSEQ1:    DB    0    ;1st sort direction, 0=A, 1=D
  1979. SSEQ2:    DB    0    ;2nd sort direction
  1980. AQR1:    DW    0    ; .A[R][M:N]
  1981. AQR2:    DW    0    ; .A[R][V:W]
  1982. AQS1:    DW    0    ; .A[S][M:N]
  1983. AQS2:    DW    0    ; .A[S][V:W]
  1984. NLBH:    DW    0    ; new LWB for right partition
  1985. NUBL:    DW    0    ; new UPB for left partition
  1986. ARIF:    DW    0    ;Array index calculation base
  1987. MARSIZ:    DW    0    ; maximum available memory
  1988. BYIF:    DW    0    ; Total input file size - bytes
  1989. ABRTF:    DS    1    ;Abort flags
  1990. FSCNT:    DW    0    ;Sectors in input file
  1991. RSCNT:    DW    0    ;Sectors read count
  1992. KWTP1:    DW    0    ;Temporary storage, .AR[J][M:N]
  1993. KWTP2:    DW    0    ;Temporary storage, .AR[J][V:W]
  1994. AWTP:    DW    0    ; address of temp record storage
  1995. PR:    DW    0    ; R
  1996. PS:    DW    0    ; S
  1997. ACR:    DW    0    ; .A[R]
  1998. ACS:    DW    0    ; .A[S]
  1999. MRLEN:    DB    0    ; negative of RLEN
  2000. CDSKSAV: DB    0    ;save default disk number
  2001. SLIM:    EQU    8    ;partition size lower limit
  2002. ;
  2003. DFCB:    DS    36    ; output FCB
  2004. DFDN:    EQU    DFCB+0    ; disk name
  2005. DFEX:    EQU    DFCB+12    ; current extent
  2006. DFCR:    EQU    DFCB+32    ; current/next/record number
  2007. ;
  2008. ; CONSOLE BUFFER
  2009. ;
  2010. CONBUF:    DB    CONLEN    ;
  2011. CONSIZ:    DS    1    ;number current characters
  2012. CONLIN:    DS    254    ;character buffer
  2013. SBUF:    EQU    CONSIZ    ;temporary buffer for disk directory
  2014. CONLEN: EQU    $-CONSIZ
  2015. ;
  2016. ; Stack and pointers
  2017. ;
  2018. BSTKDP:    EQU    16*4    ;
  2019. PSTKDP:    EQU    16*2    ;
  2020.     DS    BSTKDP    ;Bounds stack
  2021. BSTACK:    DW    0    ;Stack top
  2022.     DS    PSTKDP    ; program stack
  2023. PSTACK:    DW    0    ; base
  2024. STLV:    DS    1    ; current stack depth
  2025. PSAVE:    DW    0    ; program stack pointer
  2026. BSAVE:    DW    0    ; bounds stack pointer
  2027. ;
  2028. ; LOGICAL I/O FUNCTION EQUATES
  2029. ;
  2030. PCONBUF: EQU    9    ;print to console from buffer
  2031. RCONBUF: EQU    10    ; read console to buffer
  2032. SELDK:    EQU    14    ;select disk
  2033. OPEN:    EQU    15    ;open disk file
  2034. CLOSE:    EQU    16    ;close disk file
  2035. SRCHFST: EQU    17    ;search first occurrence of FCB in directory
  2036. SRCHNXT: EQU    18    ;search next occurrence of FCB
  2037. DELETE:    EQU    19    ;delete file
  2038. READSEQ: EQU    20    ;read next disk record
  2039. WRITSEQ: EQU    21    ;write next disk record
  2040. CREATE:    EQU    22    ;create file and directory entry
  2041. RTCDK    EQU    25    ;return current disk number
  2042. STDMAAD: EQU    26    ;set DMA address
  2043. ;
  2044. SFCB:    EQU    05CH    ;Input (default) FCB 
  2045. SFDN:    EQU    SFCB+0    ;disk number
  2046. SFEX:    EQU    SFCB+12    ;current extent
  2047. SFS1:    EQU    SFCB+13    ;bytes in last sector (maybe)
  2048. SFDA:    EQU    SFCB+16    ;extent allocation vector
  2049. SFCR:    EQU    SFCB+32    ;current/next/record number
  2050. ;
  2051. QUIT:    EQU    0000H    ;re-boot return to CPM
  2052. BDOS:    EQU    0005H    ;DOS entry
  2053. ; PROGRAM END
  2054.     DB        'BSORT 2-2.2 PROGRAM END'
  2055. AR:    DW    0    ;Base of sort array
  2056.     END        ;
  2057.