;Array exchange sort in place in memory. ;Copyright 1979,1980,1981 by C. E. Duncan. Written 1979 June 30. ;Taken from an original program written by E. W. Dijkstra on ; the back of my business card at the international meeting ; on software reliability at Los Angeles in 1975. ;Permission granted to copy for any non-commercial use. ;Revised 17:55 1981 February 10. ; ;This program, BSORT, is called as a CP/M .COM routine as follows: ; ; BSORT ; ; where file names are "[d:]name.typ" as usual in CP/M. ; The user will be asked for record length and sort parameters ; through a console dialog. ; PAGE 0 ;defeats CP/M page count ORG 0100H ;program origin BSORT: ; ;Set internal stacks LXI H,BSTACK ;bounds stack SHLD BSAVE LXI H,PSTACK ;program stack SHLD PSAVE SPHL ;Initialize CALL INIT1 ;Save default disk MVI C,RTCDK ;return current disk number CALL BDOS STA CDSKSAV ;Set default disk to input file LDA SFDN ;input file disk number MOV E,A CALL ASGDSK ;Read file and check further CALL INIT2 ;Do the sort CALL PARTIT ;Assign output disk as default LDA DFDN MOV E,A CALL ASGDSK ;Write output file CALL WRTARY ;Close output LXI D,DFCB MVI C,CLOSE CALL BDOS ;Restore default disk LDA CDSKSAV MOV E,A CALL ASGDSK JMP QUIT ;return to CP/M-CCP ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * INIT1: ;Initialize variables, read parameters, check values. ;Revised 20:10 1981 January 26. ; ;Move output file name to output FCB. MVI A,12 ;character count LXI D,SFDA ;from LXI H,DFDN ;to CALL SMOVE ;Set current (next) record pointer to 0. XRA A STA SFCR ;input STA DFCR ;output STA DFEX ;file extent STA ABRTF ;abort flags STA STLV ;bounds stack level ;Check input file name LXI D,SFCB ;input FCB CALL CHKFN JNC OK01 LXI H,ABRTF ;set abort flag INR M LXI D,FNIMSG CALL PUTMSG OK01: ; Check output file name LXI D,DFCB ;output FCB CALL CHKFN JNC OK02 LXI H,ABRTF ;set abort flag INR M LXI D,FNOMSG CALL PUTMSG ; OK02: ;Abort if these names do not check LDA ABRTF ORA A JNZ ABORT ; Calculate storage available LXI H,AR ;array base address SHLD ARBASE XCHG ;to DE LHLD BDOS+1 ;BDOS base CALL DIFF2 ;subtract DCR H ;make room for temporary storage SHLD MARSIZ ;available memory ; Check size of input file MVI A,03FH ; "?" to insure match of STA SFEX ; all extents LXI H,0 ; Reset sector count SHLD FSCNT LXI D,SBUF ; Prepare a buffer for MVI C,STDMAAD ; directory information CALL BDOS MVI C,SRCHFST ; Bring in directory for first LXI D,SFCB ; extent. Returns 0,1,2 or 3 CALL BDOS ; in 2.2, 0-3F in 1.4 CPI 0FFH JNZ OK03 LXI D,FNPMSG ; not found so quit CALL PUTMSG JMP ABORT OK03: ANI 3 ; MOD 4 (needed by CP/M 1.4 only) ;Address directory entry (one of four in buffer), then ; get sector count. 32 bytes per entry. ADD A ; *2 ADD A ; *4 ADD A ; *8 ADD A ; *16 ADD A ; *32 MVI D,15 ; plus offset to count byte ADD D MOV E,A ; add buffer base address MVI D,0 LXI H,SBUF DAD D MOV A,M ; sector count MOV E,A LHLD FSCNT ; accumulate DAD D SHLD FSCNT CPI 080H ; Full track? JNZ OK04 ; no, go on MVI C,SRCHNXT ; Get information on LXI D,SFCB ; next extent CALL BDOS CPI 0FFH ; No more entries when FF hex JNZ OK03 ; Get next entry OK04: XRA A ; Reset extent byte STA SFEX ; to zero ; Deduce input file size LHLD FSCNT ; number of sectors MOV A,H ; check for empty ORA L JZ ABORT ; nothing here ;Multiply by 128 bytes per sector DAD H ; *2 JC OK05 DAD H ; *4 JC OK05 DAD H ; *8 JC OK05 DAD H ; *16 JC OK05 DAD H ; *32 JC OK05 DAD H ; *64 JC OK05 DAD H ; *128 JNC OK06 OK05: LXI D,MULMSG ; multiply error CALL PUTMSG JMP ABORT OK06: ; Last sector may have less than 128 bytes, will check later SHLD BYIF ; Check that there is enough memory XCHG LHLD MARSIZ ;memory available CALL DIFF2 ORA A JP OK07 LXI D,FSZMSG ; report file larger than memory CALL PUTMSG JMP ABORT OK07: ;Calculate address of temporary record storage area LHLD BYIF LXI D,AR DAD D SHLD AWTP ; Open input file MVI C,OPEN LXI D,SFCB CALL BDOS INR A JNZ OK08 LXI D,FNPMSG ; file not present CALL PUTMSG JMP ABORT ;Open output file OK08: MVI C,DELETE ;delete file of same name LXI D,DFCB CALL BDOS MVI C,CREATE ;make new file LXI D,DFCB CALL BDOS INR A JNZ OK09 LXI D,NDSMSG ; signal no directory space CALL PUTMSG JMP ABORT OK09: ; Ask for record length LXI D,RCLMSG CALL PUTMSG CALL READCON ;read console input LXI D,CONSIZ ;string response LXI B,AR ;temporary buffer CALL SCANBR ;extract number JC OK09 ;try again LDAX B ;count INX B ;1st character CALL ROW1NBR ;convert to binary JC OK09 ;trouble MOV A,L STA RLEN ; calculate twos complement CMA INR A STA MRLEN OK10: ; Ask for sort parameters. XRA A ; reset parameter count STA NBRFND LXI D,PARMSG CALL PUTMSG CALL READCON ;read console input LDA CONSIZ ;number of characters read ORA A JZ OK10 ;no input, try again CALL RDPARM ;read, convert and store sort parms JC OK10 ;try again CALL CKPARM ;check parameters JC OK10 ;ask again RET ;end of INIT1 ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * ; INIT2: ;Read input file to array, correct file size, final checks. ;Written by C. E. Duncan 1979 June 30. ;Revised 08:15 1981 February 4. ; Read input file to array CALL RDARRAY ; Check and possibly correct file size calculation BYIF ; HL points to byte after last sector read. LDA SFS1 ; bytes remain in last sector ORA A JNZ OK12 MVI A,01AH ; must remove eof (1A) bytes LXI B,0 ; clear counter OK11: DCX H ; char in file CMP M ; is it EOF? JNZ OK13 ; no INX B ; count JMP OK11 OK12: CMA ; subtract off eofs MOV C,A MVI B,0FFH ; minus sign JMP OK14 OK13: MOV A,B ; get twos complement CMA ; to subtract MOV B,A MOV A,C CMA ; subtract off unused bytes MOV C,A OK14: INX B ; twos complement LHLD BYIF DAD B ; subtract SHLD BYIF ; Check that file size is multiple of record length ; and calculate upper and lower bounds MOV A,H ; check that there is a record ORA L JZ ABORT ; nothing here LDA RLEN CALL DIV12 JNC OK15 LXI D,DIVMSG CALL PUTMSG JMP ABORT OK15: MOV A,L ; check remainder CMP H JZ OK16 LXI D,RLMSG ;abort msg CALL PUTMSG JMP ABORT OK16: MOV H,B ; store quotient MOV L,C ; as UPB SHLD AUPB SHLD CUPB LXI H,1 ; LWB = 1 SHLD ALWB SHLD CLWB ;Initialize array index calculation ; A[I] has address = ARRAY BASE - (LWB A)*RLEN + I*RLEN ; = ARIF + I*RLEN ; LHLD ALWB ;LWB of A XCHG ; to DE LDA RLEN CALL MUL12 JC ABORT ;overflow XCHG LHLD ARBASE ; calculate HL - DE CALL DIFF2 ORA A ; check sign JP OK17 ; positive, ok MOV A,H ; complement if negative CMA MOV H,A MOV A,L CMA MOV L,A INX H OK17: SHLD ARIF ;Addresses of sort strings in temporary area LHLD AWTP ; temporary record store LDA POOF1 ; 1st sort offset MVI B,0 MOV C,A DAD B SHLD KWTP1 ; address of awtp[m:n] LDA PARM3 ; is there a 2nd sort? ORA A RZ LDA POOF2 ; 2nd sort offset MOV C,A LHLD AWTP DAD B SHLD KWTP2 ; address of awtp[u:v] RET ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * ; PARTIT: ;Partition sort based on a program by Dijkstra. ;Written by C. E. Duncan 1979 June 30. ;Revised 17:30 1981 February 8. ; ; p = LWB a, q = UPB a ; ;Algorithm: partition and sort until q < p ; WHILE p <= q ; DO ; IF q = p ; THEN ; unstack ; ELSE ; IF q - p <= slim ; THEN ; shorts {insertion sort} ; ELSE ; parta {partition left} ; {makes two partitions: a[p] to a[s] and a[r] to a[q]} ; FI ; FI; ; IF s = p ; THEN ; p := r ; ELSE ; IF s < p ; THEN ; partb {partition right} ; {required if parta has no "small" element} ; ELSE ; IF q = r ; THEN ; q := s ; ELSE ; IF q < r ; THEN ; unstack ; ELSE ; IF q - r > s - p ; THEN ; stack right; ; q := s ; ELSE ; stack left; ; p := r ; FI ; FI ; FI ; FI ; FI ; OD ; LHLD CLWB ; P = LWB current partition XCHG LHLD CUPB ; Q = UPB current partition CALL DIFF2 ; compare ORA A RM ;sort complete when Q < P JZ UNSTACK ; only one element XCHG LXI H,SLIM ;low size limit CALL DIFF2 ;SLIM - (P - Q) ORA A PUSH PSW CP SHORTS ;use insertion sort, small partition POP PSW JP UNSTACK ;this partition completed CM PARTA ;partition leftward ; Check size of lower partition STAR01: LHLD CLWB ; P = LWB left XCHG LHLD PS ; S = UPB left CALL DIFF2 ; S - P ORA A JZ STAR02 ; only one element, finished JM STAR04 ; no small element SHLD SMP ; Upper partition. LHLD PR ; R = LWB right XCHG LHLD CUPB ; Q = UPB right CALL DIFF2 ; Q - R ORA A JZ STAR03 ; only one element, finished JM UNSTACK ; finished with this partition ; because no large element after ; having no small element. SHLD QMR ; Save bounds of larger partition. ; If Q - R > S - P then upper part is larger. LXI H,0 ; save program stack DAD SP SHLD PSAVE LHLD BSAVE ; retrieve bounds stack SPHL ; LHLD QMR ; Q - R XCHG LHLD SMP ; S - P CALL DIFF2 ; (S-P) - (Q-R) ORA A JM STHI ; stack bunds for high side STLO: LHLD CLWB ; P, new lower bound PUSH H LHLD NUBL ; S, new upper bound PUSH H LHLD NLBH ; R, set new LWB for high side SHLD CLWB JMP REST ; restore program stack STHI: LHLD NLBH ; R, new lower bound PUSH H LHLD CUPB ; Q, new upper bound PUSH H LHLD NUBL ; S, new upper bouond for low side SHLD CUPB REST: LXI H,0 ; restore program stack DAD SP SHLD BSAVE LHLD PSAVE SPHL LXI H,STLV ; increment stack level INR M JMP PARTIT ; process next partition ; ; Process upper part STAR02: LHLD PR ; R is new lower bound SHLD CLWB ; JMP PARTIT ; STAR03: ; Process lower part LHLD PS ; S is new upper bound SHLD CUPB ; JMP PARTIT ; STAR04: ; Partition again, using R <= T and S > T in place of ; R < T and S >= T respectively. CALL PARTB ; JMP STAR01 ; ; UNSTACK: ; Recover bounds of next section to be partitioned LXI H,STLV ; check level DCR M ; RM ; stack empty, sort completed LXI H,0 ; save program stack DAD SP ; SHLD PSAVE ; LHLD BSAVE ; get bounds stack SPHL ; POP H ; SHLD CUPB ; UPB POP H ; SHLD CLWB ; LWB LXI H,0 ; restore program stack DAD SP ; SHLD BSAVE ; LHLD PSAVE ; SPHL ; JMP PARTIT ; return, do next section ; PARTA: ;Re-arrange array AR into two partitions the left of which contains ; elements which precede a pivot element, and the right contains ; those which do not. ;Written by C. E. Duncan 1979 June 30. ;Revised 15:06 1981 January 31. ; ; R = LWB A, S = UPB A, T = (R+S) OVER 2. ; ;Algorithm: ; WHILE LWB A <= R < S <= UPB A ; DO ; SWAP A[R] and A[S]; ; WHILE A[R] precedes A[T] ; DO ; R +:= 1 ; OD; ; WHILE A[S] does not precede A[T] ; DO ; S -:= 1 ; OD ; OD ; ;Calculate addresses LDA POOF1 ; 1st sort parameter offset MVI B,0 MOV C,A LHLD CLWB ; current LWB SHLD PR ; R XCHG CALL INDXR ; calculate address SHLD ACR ; .A[R] DAD B SHLD AQR1 ; .A[R][M:N], 1st sort string LHLD CUPB ; current LWB SHLD PS ; S XCHG CALL INDXR SHLD ACS ; .A[S] DAD B SHLD AQS1 ; .A[S][M:N] LHLD PR ; R XCHG LHLD PS ; S DAD D ; R + S CALL SHRHL ; divide by 2 XCHG CALL INDXR ; .A[T] XCHG ; move A[T], the pivot element, to LHLD AWTP ; a safe place LDA RLEN CALL SMOVE ; Take care of possible 2nd sort substring LDA PARM3 ORA A JZ PAR01 ;not needed LDA POOF2 ;2nd ss offset MVI B,0 MOV C,A LHLD ACR ; .A[R] DAD B SHLD AQR2 ; .A[R][V:W] LHLD ACS DAD B SHLD AQS2 ; .A[S][V:W] PAR01: ;Check if finished LHLD PS ; S XCHG LHLD PR ; R CALL DIFF2 ; R - S ORA A JP PAR03 ; finished ; Update addresses of A[R] and A[S] LHLD PR ; R XCHG CALL INDXR SHLD ACR ; .A[R] LHLD PS ; S XCHG CALL INDXR SHLD ACS ; .A[S] ; Swap LDA RLEN LHLD ACR ; .A[R] XCHG LHLD ACS ; .A[S] CALL SWAP ; While A[R] precedes A[T], etc. LHLD AQR1 ; .A[R][M:N] XCHG PAR01A: LDA SPL1 ;1st sort length LHLD KWTP1 ; .A[T][M:N] XCHG CALL CMPSRW ORA A JZ PAR04 ; check 2nd sort substring PAR01B: PUSH PSW LDA SSEQ1 ; check direction ORA A JZ PAR01C ; ascending POP PSW ; descending JZ PAR02 JP PAR01D ; A[R] precedes A[T], down JMP PAR02 PAR01C: POP PSW JP PAR02 ; A[R] does not precede A[T], up PAR01D: LHLD PR ; increment R INX H SHLD PR LDA RLEN MVI B,0 MOV C,A LDA PARM3 ;2nd sort? ORA A JZ PAR01E ;no LHLD AQR2 ;update .A[R][V:W], 2nd sort string DAD B SHLD AQR2 PAR01E: LHLD AQR1 ;update .A[R][M:N] 1st sort DAD B SHLD AQR1 XCHG JMP PAR01A PAR02: ; While A[S] does not precede A[T] etc. LHLD AQS1 ; .A[S][M:N] XCHG PAR02A: LDA SPL1 ; length of 1st sort LHLD KWTP1 ; 1st sort string address XCHG CALL CMPSRW ORA A JZ PAR05 ; check 2nd sort PAR02B: PUSH PSW LDA SSEQ1 ; check direction ORA A JZ PAR02C ; ascending POP PSW JM PAR02D JZ PAR02D JMP PAR01 ; S precedes T PAR02C: POP PSW JM PAR01 PAR02D: LHLD PS ; decrement S DCX H SHLD PS ; Check array bound at lower limit, S < LWB XCHG LHLD CLWB ; P = LWB A XCHG CALL DIFF2 ; S - P ORA A JM PAR03 ; no small element ;Update addresses for next comparison LDA MRLEN ; minus RLEN MVI B,0FFH MOV C,A LDA PARM3 ;check for 2nd sort ORA A JZ PAR02E ;no LHLD AQS2 DAD B SHLD AQS2 PAR02E: LHLD AQS1 DAD B ; reduce address by RLEN SHLD AQS1 XCHG JMP PAR02A PAR03: LHLD PR SHLD NLBH ; new LWB for right partition LHLD PS SHLD NUBL ; new UPB for left partition RET PAR04: LDA PARM3 ORA A JZ PAR01B ;no 2nd sort LHLD KWTP2 XCHG LHLD AQR2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JZ PAR04A ; ascending POP PSW JZ PAR02 JP PAR01D JMP PAR02 PAR04A: POP PSW JM PAR01D JMP PAR02 ; this one is out of order ; PAR05: LDA PARM3 ; is there a 2nd sort? ORA A JZ PAR02B ; no LHLD KWTP2 XCHG LHLD AQS2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JZ PAR05A POP PSW JZ PAR02D JM PAR02D JMP PAR01 PAR05A: POP PSW JP PAR02D JMP PAR01 ; PARTB: ;Re-arrange array A into two partitions, the right of which contains ; elements which follow a pivot element, and the left contains those ; which do not. ;Written by C. E. Duncan 1979 June 30. ;Revised 18:50 1981 February 8. ; ; R = LWB A, S = UPB A, T = (R+S) OVER 2 ; ;Algorithm: ; WHILE LWB A <= R < S <= UPB A ; DO ; SWAP A[R] and A[S]; ; WHILE A[R] does not follow A[T] ; DO ; R +:= 1 ; OD; ; WHILE A[S] follows A[T] ; DO ; S -:= 1 ; OD ; OD ; ; Calculate addresses LDA POOF1 ; 1st sort offset MVI B,0 MOV C,A LHLD CLWB ; current LWB A SHLD PR ; R XCHG CALL INDXR SHLD ACR ; .A[R] DAD B ; .A[R][M:N] SHLD AQR1 LHLD CUPB SHLD PS ; S XCHG CALL INDXR SHLD ACS ; .A[S] DAD B SHLD AQS1 ; .A[S][M:N] LHLD PR ; R XCHG LHLD PS ; S DAD D ; R+S CALL SHRHL ; shift right, OVER 2 XCHG CALL INDXR ; address of A[T] XCHG ; move A[T] to a safe place LHLD AWTP LDA RLEN CALL SMOVE ; Take care of 2nd sort substring LDA PARM3 ; is there one? ORA A JZ PAB01 ; no LDA POOF2 ; offset MVI B,0 MOV C,A LHLD ACR DAD B SHLD AQR2 ; .A[R][V:W] LHLD ACS DAD B SHLD AQS2 ; .A[S][V:W] PAB01: ; Check completion LHLD PS ; S XCHG LHLD PR ; R CALL DIFF2 ; R-S ORA A JP PAB03 ; finished ; Update addresses of A[R] and A[S] LHLD PR ; R XCHG CALL INDXR SHLD ACR ; .A[R] LHLD PS ; S XCHG CALL INDXR SHLD ACS ; .A[S] ; Swap Elements with indices R and S LDA RLEN LHLD ACR ; .A[R] XCHG LHLD ACS ; .A[S] CALL SWAP ; While A[R] does not follow A[T] increment R. LHLD AQR1 ; .A[R][M:M] XCHG PAB01A: LDA SPL1 ; length sort 1 LHLD KWTP1 ; .A[T][M:N] CALL CMPSRW ORA A JZ PAB04 ; check 2nd sort PAB01B: PUSH PSW LDA SSEQ1 ; direction ORA A JZ PAB01C POP PSW ; descending JM PAB01D JZ PAB01D JMP PAB02 PAB01C: POP PSW JM PAB02 PAB01D: LHLD PR ; R INX H SHLD PR ; Check upper bound in case no large element XCHG LHLD CUPB ; Q = UPB A CALL DIFF2 ORA A JM PAB03 ; upper limit, no large element ; Update addresses, etc. LDA RLEN MVI B,0 MOV C,A LDA PARM3 ORA A ; 2nd sort JZ PAB01E ; no LHLD AQR2 ; .A[R][V:W] DAD B SHLD AQR2 PAB01E: LHLD AQR1 DAD B SHLD AQR1 XCHG JMP PAB01A PAB02: ;While A[S] follows A[T] decrease S, etc. LHLD AQS1 ; .A[S][M:N] XCHG PAB02A: LDA SPL1 LHLD KWTP1 CALL CMPSRW ORA A JZ PAB05 ; check for 2nd sort PAB02B: PUSH PSW LDA SSEQ1 ORA A JZ PAB02C POP PSW JZ PAB01 JP PAB02D JMP PAB01 ; A[S] <= A[T] PAB02C: POP PSW JP PAB01 PAB02D: LHLD PS ; decrement S DCX H SHLD PS LDA MRLEN MVI B,0FFH MOV C,A LDA PARM3 ; 2nd sort? ORA A JZ PAB02E ; no LHLD AQS2 DAD B SHLD AQS2 PAB02E: LHLD AQS1 DAD B SHLD AQS1 XCHG JMP PAB02A ; PAB03: LHLD PR SHLD NLBH ; new LWB for right partition LHLD PS SHLD NUBL ; new UPB for left partition RET PAB04: LDA PARM3 ; 2nd sort? ORA A JZ PAB01B ; no LHLD AQR2 XCHG LHLD KWTP2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JZ PAB04A POP PSW JM PAB01D JZ PAB01D JMP PAB02 PAB04A: POP PSW JP PAB01D JMP PAB02 ; PAB05: LDA PARM3 ORA A JZ PAB02B LHLD AQS2 XCHG LHLD KWTP2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JZ PAB05A POP PSW JZ PAB01 JP PAB02D JMP PAB01 PAB05A: POP PSW JM PAB02D JMP PAB01 ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * ; SHORTS: ;Insertion sort for small partitions. ;Written by C. E. Duncan 1980 February 16, from Knuth volume 3 ; (Searching and Sorting) page 81. ;Revised 12:30 1981 February 8. ; ;Algorithm: ; ; FOR j FROM 2 TO UPB(a) ; DO ; IF a[j] < a[j - 1] ; THEN ; awtp := a[j]; ; FOR i FROM j - 1 BY -1 TO LWB(a) ; WHILE at < a[i] ; DO ; a[i + 1] := a[i]; ; k := i ; OD; ; a[k] := awtp ; FI ; OD ; ;Initialize indices and addresses. LHLD CLWB ;LWB of current partition SHLD PS ; j XCHG CALL INDXR SHLD ACS ; address of a[j] = a[LWB] LDA POOF1 ;1st sort offset MVI B,0 MOV C,A DAD B SHLD AQS1 ;address of a[LWB][m:n] LDA PARM3 ORA A JZ SH01 LDA POOF2 ;2nd sort offset MOV C,A LHLD ACS DAD B SHLD AQS2 ;address of a[LWB][u:v] SH01: ;Increment j, compare to UPB, set i := j - 1 LHLD PS ; j - 1 SHLD PR ; i := j - 1 INX H ; j +:= 1 SHLD PS ; j ;Check that j <= UPB XCHG LHLD CUPB ; UPB of parttion CALL DIFF2 ;UPB - j ORA A RM ;finished when J > UPB ;Update addresses LHLD ACS ;old .a[j] SHLD ACR ;new .a[i] LDA RLEN MVI B,0 MOV C,A DAD B SHLD ACS ; new a[j] ;Update sort string addresses LHLD AQS1 SHLD AQR1 ; a[i][m:n] DAD B SHLD AQS1 ; a[j][m:n] LDA PARM3 ORA A JZ SH02 LHLD AQS2 SHLD AQR2 ; a[i][u:v] DAD B SHLD AQS2 ; a[j][u:v] SH02: ;Compare a[j] with a[j - 1] = a[i] LHLD AQR1 XCHG LHLD AQS1 LDA SPL1 CALL CMPSRW ORA A JZ SH05 ; check 2nd sort SH03: PUSH PSW LDA SSEQ1 ; check direction ORA A JNZ SH04 POP PSW JM SH07 ; have to do some moves JMP SH01 ; ok where it is, go to next j SH04: POP PSW JM SH01 ; ok as is JZ SH01 ; ditto JMP SH07 SH05: ;Second compare for a[j] and a[j - 1] LDA PARM3 ORA A JZ SH03 ; no 2nd compare LHLD AQR2 XCHG LHLD AQS2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JNZ SH06 POP PSW JM SH07 JMP SH01 SH06: POP PSW JM SH01 JZ SH01 JMP SH07 ; SH07: ;Move a[j] to a safe place: awtp := a[j] LHLD ACS XCHG ; from LHLD AWTP ; to LDA RLEN CALL SMOVE SH08: ;Move a[i] up one place to position i + 1 LDA RLEN MVI B,0 MOV C,A LHLD ACR ; a[i] MOV D,H ; to DE, from MOV E,L DAD B ; a[i + 1] CALL SMOVE ;Decrement i, check against LWB LHLD CLWB ; LWB XCHG LHLD PR ; i DCX H ; i -:= 1 SHLD PR CALL DIFF2 ; i - LWB ORA A JM SH14 ; at LWB, hence a[LWB] := awtp ;Decrement addresses and compare again LDA MRLEN ; negative record length MVI B,0FFH MOV C,A LHLD ACR DAD B SHLD ACR ; new address of a[i] LHLD AQR1 DAD B SHLD AQR1 ; new 1st sort address LDA PARM3 ORA A JZ SH09 LHLD AQR2 DAD B SHLD AQR2 ; new 2nd sort address SH09: ;Compare awtp = a[j] with a[i] LHLD AQR1 XCHG LHLD KWTP1 LDA SPL1 CALL CMPSRW ORA A JZ SH12 SH10: PUSH PSW LDA SSEQ1 ORA A JNZ SH11 POP PSW JM SH08 ; keep trying and comparing JMP SH15 ; found place for at in a[i + 1] SH11: POP PSW JM SH15 JZ SH15 JMP SH08 ; SH12: ;Second compare for awtp = a[j] and a[i] LDA PARM3 ORA A JZ SH10 LHLD AQR2 XCHG LHLD KWTP2 LDA SPL2 CALL CMPSRW ORA A PUSH PSW LDA SSEQ2 ORA A JNZ SH13 POP PSW JM SH08 JMP SH15 SH13: POP PSW JM SH15 JZ SH15 JMP SH08 ; SH14: ;Move awtp = a[j] into slot at a[LWB] LHLD AWTP XCHG LDA RLEN LHLD ACR CALL SMOVE JMP SH01 SH15: ;Move awtp = a[j] into slot at a[i + 1] LHLD AWTP XCHG LDA RLEN MVI B,0 MOV C,A LHLD ACR DAD B CALL SMOVE JMP SH01 ; ;* * * * * * * * * * * * * * * * * * * * * * ; ABORT: ;Return to CP/M LXI D,ABMSG CALL PUTMSG JMP QUIT ; ASGDSK: ;Assign default disk for faster input and output. ; Must have desired disk number in E, and default disk number ; in location CDSKSAV. ;Written by C. E. Duncan 1981 January 28. XRA A ;get zero CMP E JNZ ASGD1 LDA CDSKSAV ;need default disk MOV E,A JMP ASGD2 ASGD1: DCR E ;A-P = 1-16 become 0-15 ASGD2: MVI C,SELDK ;select disk CALL BDOS RET ; CHAROW: ;Reset carry if character in C is present in row of character ; whose address is in DE, length in B, else set carry. ; Return position number in B. ;Written by C. E. Duncan 1981 January 23. ;Revised 09:00 1981 January 28. MOV A,B ;row length CPI 0 ;check zero length JZ CHAR2 XCHG ;row address in HL MOV A,C ;character sought MVI D,0 ;position count CHAR1: INR D ;count CMP M ;is this it? JZ CHAR3 ;yes DCR B ;count off row INX H ;next permitted JNZ CHAR1 ;more CHAR2: STC ;signal not found RET CHAR3: MOV B,D ;position number ORA A ;found, reset carry RET ; CHKFN: ; Check file name for legal characters, FCB address in DE. ; Written by C. E. Duncan 1980 February 7. ; Revised 05:30 1981 February 4. LDAX D ;drive CPI 5 ;no more than 4 drives JNC CHKFN2 ;out of limits MVI B,11 ; Number of characters to check INX D ;first character LDAX D ; must be non-blank CPI 021H ; JC CHKFN2 ; not acceptable JMP CHKFN3 ; CHKFN1: ; INX D ; next character LDAX D ; CPI 020H ; blank JC CHKFN2 ; control character CHKFN3: CPI 05BH ; [ JNC CHKFN2 ; also unacceptable DCR B ; count JNZ CHKFN1 ; return for next XRA A ; signal ok RET ; CHKFN2: ; STC ; signal presence of RET ; unacceptable character ; CKPARM: ;Check sort parameters. Each parameter one byte from PARM1. ;Written by C. E. Duncan 1981 January 21. ;Revised 13:37 1981 January 28. ; ;get parameters in registers LXI H,PARM1 ;address parameters MOV B,M INX H MOV C,M INX H MOV D,M INX H MOV E,M LDA RLEN MOV H,A ;check parameters <= RLEN MOV L,B CALL KPR ;check range of parm1 RC ;out of limits MOV L,C CALL KPR ;check parm2 RC MOV A,D ;is there a 2nd sort range? CPI 0 JZ KRR ;no MOV L,D CALL KPR ;check parm3 RC MOV L,E CALL KPR RC JMP KRR KPR: MOV A,L CPI 1 RC ;< 1 MOV A,H SUB L RET ;carry set if > RLEN KRR: ;Calculate sort string lengths and check them MOV A,C ;1st SUB B RC ;negative length INR A STA SPL1 ;length of 1st sort substring MOV L,A MOV A,H ;RLEN SUB L RC ;substring longer than record MOV A,B ;PARM1 DCR A STA POOF1 ;offset of sort substring in record MOV A,D ;PARM3 ORA A RZ ;ok return, only one substring ;Have 2nd sort substring MOV L,A MOV A,E ;PARM4 SUB L RC ;negative length INR A STA SPL2 MOV L,A MOV A,H SUB L RC ;longer than RLEN MOV A,D DCR A STA POOF2 ;offset ;Check for sort field overlap MOV A,E ;PARM4 SUB B ;PARM1 JC KRS ;ok MOV A,C ;PARM2 SUB D ;PARM3 JC KRS ;ok STC ;overlap RET KRS: XRA A ;ok, reset carry RET ; CMPSRW: ;Compare two rows of character of equal length. ;Registers DE and HL have addresses of the two rows of character, ; register A the count. Return -1, 0, +1 in register A as HL < DE, ; HL = DE, HL > DE respectively. ;Written by C. E. Duncan 1981 January 26. MOV B,A ;count INR B CMPSRWA: DCR B JZ CMPSRWEQ ;equal LDAX D CMP M JC CMPSRWGT ;HL > DE JNZ CMPSRWLT ;HL < DE INX D ;equal so far INX H JMP CMPSRWA CMPSRWGT: MVI A,1 RET CMPSRWEQ: XRA A RET CMPSRWLT: MVI A,-1 RET ; DIFF2: ;Calculate difference of integers in DE and HL. Put absolute ; difference in HL. Signal DE < HL, DE = HL, DE > HL with ; +1, 0 -1 in A. ;Written by C. E. Duncan 1980 February 18. ;Revised 13:30 1981 January 29. MOV A,D CMP H JC DIF1 ; DE < HL JNZ DIF2 ; DE > HL MOV A,E CMP L JC DIF1 ; DE < HL JNZ DIF2 ; DE > HL LXI H,0 ; DE = HL XRA A ; reset carry to signal equal RET DIF1: MVI B,1 ; signal DE < HL JMP DIF3 DIF2: MVI B,0FFH ; signal DE > HL XCHG DIF3: ; Do subtraction MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A MOV A,B ; restore signal STC ; set carry to signal not equal RET ; DIV12: ;Divide 16 bit integer in HL by eight bit SHORT INT in A; ; return 16 bit quotient in BC, remainder in HL (L). ;20:05 10 February 1980. ORA A ;test for zero divisor JZ DIV03 ; PUSH A ;save divisor CMA ;twos complement INR A ; of divisor MOV E,A ; to DE MVI D,0FFH ;propagate negative sign LXI B,0 ;clear quotient DIV01: DAD D ;divide by subtraction JNC DIV02 ; INX B ; JMP DIV01 ; DIV02: POP A ;prepare MOV E,A ; remainder MVI D,0 ; in HL DAD D ; ORA A ;reset carry to RET ; signal ok DIV03: STC ;signal zero RET ; divisor ; GETNBR: ;Extract an ASCII number (sequence of digits) from a row of character. ; Enter with row address in DE, count in BUFCNT. Return with ; DE pointing to following characters, remaining count in BUFCNT ; and extracted number converted to binary in C. Carry set if ; unsuccessful, else reset. ;Written by C. E. Duncan 1981 January 27. ;Revised 08:00 1981 January 28. LDA BUFCNT ;get count ORA A JNZ GETN01 GETN00: STC ;signal zero length in or out RET GETN01: LXI B,AR-1 ;temporary store XCHG INR A ;count + 1 MOV D,A ; to D MVI E,0 ;output count DCX H GETN02: INX H ;next character DCR D ;count JZ GETN04 ;finished MOV A,M CPI 030H JC GETN02 ;ignore CPI 03AH JNC GETN02 ;Have found a digit GETN03: INX B ;ASCII number output STAX B INR E ;count output INX H ;address of next character DCR D ;count input JZ GETN04 ;finished MOV A,M CPI 030H JC GETN04 ;finished CPI 03AH JC GETN03 ;Windup GETN04: MOV A,D STA BUFCNT ;remaining input count PUSH H ;save current row address MOV A,E ;output count LXI B,AR ;recover output address CALL ROW1NBR ;convert ASCII number at BC to binary POP D ;recover address JC GETN00 ;problems MOV C,L RET ;binary number in C ; INDXR: ;Get address of array element with index given in DE. Return ; address of element in HL. Array base address is stored in ; location ARBASE, RLEN, the record length is less than 256. ; Address of AR[i] is given by ARIF + I*RLEN. ; Index is checked against bounds. PUSH D LHLD ALWB ;check LWB DCX H CALL DIFF2 ORA A JM IND02 ; LWB <= I IND01: LXI D,INXMSG ;report index out CALL PUTMSG ; of bounds JMP ABORT IND02: LHLD AUPB ;check UPB POP D PUSH D CALL DIFF2 ORA A JM IND01 ;abort POP D ;index ok, I <= UPB LDA RLEN CALL MUL12 JC ABORT ;overflow XCHG LHLD ARIF DAD D RET ; MU111: ;Multiply 8-bit number in E by 8-bit number in A, returning ; 8-bit number in L. Set carry for overflow, else reset. ;Written by C. E. Duncan 1981 January 24. LXI H,0 ;zero result register MVI D,0 ;for double add MVI B,8 ;bit count MU111A: DAD H ;shift HL left RAL ;same for multiplier JNC MU111B DAD D MU111B: DCR B ;count JNZ MU111A ;get next bit XRA A ;check for overflow CMP H RET ;carry set if H > 0 ; MUL12: ;Multiply 16-bit number in DE by 8-bit number in A, placing ; 16-bit result in HL. Carry set for overflow, else reset. ;Revised 22:22 1980 February 25. LXI H,0 ;clear result register MVI B,8 ;bit count MUL12A: DAD H ;shift left RAL ;same for multiplier JNC MUL12B ;this multiplier bit = 0 DAD D ;add multiplicand RC ;carry indicates overflow MUL12B: DCR B ;count bits JNZ MUL12A ;continue ORA A ;ok, reset carry RET ; PUTMSG: ; Write message to console via BDOS, address in DE PUSH D ;Save message address LXI D,CCRLF ;CR and LF MVI C,PCONBUF ; CALL BDOS ; POP D ;recover message MVI C,PCONBUF ;Signal write to console CALL BDOS ; RET ; ; RDARRAY: ;Read disk file of typed, fixed length records to array AR. ;Written by C. E. Duncan 1980 February 3. ;Revised 08:30 1981 February 4. ; Initialize LXI B,0FF80H ; -128 LXI H,AR ; array base DAD B ; PUSH H ; LXI H,0 ; Zero sector count SHLD RSCNT ; ; Read loop RDAL: ; ; Set DMA address LXI B,128 ; step pointer POP H ; DAD B ; PUSH H ; XCHG ; DMA addr in DE for BDOS MVI C,STDMAAD ; CALL BDOS ; ; Read a sector LXI D,SFCB ; Address FCB MVI C,READSEQ ; CALL BDOS ; CPI 0 ; check successful completion JNZ RD1 ; check further LHLD RSCNT ; ok, count INX H ; SHLD RSCNT ; JMP RDAL ; return for next sector RD1: CPI 1 ; JZ RD2 ; end of file JMP ABORT ; should not happen RD2: ; Read complete POP H ; Restore stack RET ; ; RDPARM: ;Read parameters from console and store in suitable form. ;Written by C. E. Duncan 1981 January 27. ;Revised 12:20 1981 January 28. LXI D,CONSIZ ;console buffer LDAX D ;count ORA A JZ RDPFIN ;no input STA BUFCNT ;count of unprocessed characters INX D ;1st character RDP1: LDAX D ;examine character ORI 020H ;convert to lower case PUSH D ;save row address MVI B,14 ;count of acceptable characters MOV C,A ;character to be tested LXI D,PRMCHRS ;list of ok characters CALL CHAROW ;is it acceptable? MOV A,C ;recover character POP D JNC RDP3 ;ok RDP2: INX D ;point to next character LXI H,BUFCNT ;update count DCR M JNZ RDP1 ;keep trying JMP RDPFIN ;no more RDP3: CPI 'a' ;ascending? JZ RDP7 ;yes, no action CPI 'd' ;descending? JNZ RDP9 ;must be a number LDA NBRFND ;which parameter? CPI 2 ;is it 3rd? JZ RDP4 ;must be 5th or 6th CPI 4 JZ RDP5 CPI 5 JZ RDP5 JMP RDP2 ;ignore RDP4: LXI H,SSEQ1 JMP RDP6 RDP5: LXI H,SSEQ2 RDP6: INR M ;set descending RDP7: LXI H,NBRFND ;update parameter count INR M JMP RDP2 ;return for more RDP8: LXI H,NBRFND ;update number of parameters found INR M LDA BUFCNT ;check for remaining characters ORA A JZ RDPFIN JMP RDP1 ;process next character RDP9: CALL GETNBR ;return binary in C, update buffer LDA NBRFND ;parameter count CPI 0 JNZ RDP10 MOV A,C STA PARM1 JMP RDP8 RDP10: CPI 1 JNZ RDP11 MOV A,C STA PARM2 JMP RDP8 RDP11: CPI 2 JNZ RDP13 RDP12: MOV A,C STA PARM3 JMP RDP8 RDP13: CPI 3 JNZ RDP14 LDA PARM3 ORA A JZ RDP12 RDP14: MOV A,C STA PARM4 JMP RDP8 RDPFIN: LDA NBRFND ;all done? CPI 2 ;at least 2 RET ;carry set if not ; READCON: ;Read console to console buffer CONBUF. LXI D,CONBUF MVI C,RCONBUF CALL BDOS RET ; ROW1NBR: ;Convert ASCII decimal row at (BC), length A, to 1-byte number ; in L. Set carry for overflow. ;Copyright 1980 by C. E. Duncan. ;Revised 12:20 1981 January 24. CPI 4 ;check size JNC RTN1A ; CPI 0 ; JNZ RTN1B ; RTN1A: STC ;signal trouble RET ; RTN1B: MOV D,A ;count MVI L,0 ;reset result register MVI E,10 ;multiplier RTN1C: MOV A,L ;multiply by 10 PUSH B ; PUSH D ; CALL MU111 ;A * E to L POP D ; POP B ; JC RTN1A ;overflow LDAX B ;next digit SUI 30H ;convert to binary JM RTN1A ;not a digit CPI 10 ; JNC RTN1A ;not a digit ADD L ; MOV L,A ; INX B ;next DCR D ;count JNZ RTN1C ;continue RET ; ; SCANBR: ;Extract an ASCII number (sequence of digits) from a string. ; Enter with address of string in DE. Leave with BC pointing ; to extracted ASCII number string, and DE pointing to remaining ; row of characters with count in A. ; String = LCCC...C. ;Written by C. E. Duncan 1981 January 23. LDAX D ;get count ORA A JNZ SCNB01 SCNB00: STC ;signal zero length in or out RET SCNB01: PUSH B ;output string origin XCHG INR A ;count + 1 MOV D,A ; to D MVI E,0 ;output count SCNB02: DCR D ;count JZ SCNB04 ;finished INX H ;next character MOV A,M CPI 030H JC SCNB02 ;ignore CPI 03AH JNC SCNB02 ;Have found a digit SCNB03: INX B ;ASCII number output STAX B INR E ;count output DCR D ;count input JZ SCNB04 ;finished INX H ;next input character MOV A,M CPI 030H JC SCNB04 ;finished CPI 03AH JNC SCNB04 ;finished JMP SCNB03 ;Windup SCNB04: POP B ;recover output origin MOV A,E ;output count ORA A ;test for zero length JZ SCNB00 STAX B MOV A,D ;input count remaining XCHG RET ;ok, carry reset ; SHRHL: ;Shift HL right one bit :=: divide HL by 2. ;Written by C. E. Duncan 1979 June 30. ANA A ;clear carry MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A RET ; SMOVE: ;Non-overlapping move, left to right. ;Register A has count of bytes, < 256, DE address of source and ; HL address of destination. ;Written by C. E. Duncan 1980 February 18. ;Revised 17:30 1981 January 26. MOV B,A ;count INR B SMOVE1: DCR B RZ LDAX D MOV M,A INX D INX H JMP SMOVE1 ; SWAP: ;Exchange two rows-of-character of equal length, addresses in ; DE and HL, length in A. ;Written by C. E. Duncan 1980 February 18. ;Revised 08:40 1981 February 4. ORA A ;check length RZ ;finished MOV B,A ;count SWAP1: MOV C,M ;save byte from HL LDAX D ;move byte from MOV M,A ; DE to HL MOV A,C ;move byte from C STAX D ; (from HL) to DE INX D INX H DCR B JNZ SWAP1 RET ; WRTARY: ; Write array to disk file from AR. ; Written 1980 February 17. ; Revised 17:45 1981 January 28. ; Initialize LXI B,0FF80H ; -128 LXI H,AR ; array base DAD B ; PUSH H ; array pointer LHLD RSCNT ; sector count INX H PUSH H LXI B,128 ;DMA address increment WRAL: ; Check count of sectors remaining POP D ; get count DCX D ; count MOV A,D ORA E JNZ WR1 ; more POP H ; restore stack RET ;finished WR1: ; Set DMA address POP H LXI B,128 DAD B PUSH H PUSH D ; count XCHG MVI C,STDMAAD CALL BDOS ; Write sector LXI D,DFCB ; output FCB MVI C,WRITSEQ ; sequential write CALL BDOS CPI 0 JZ WRAL ; ok, continue ; Abort because of disk problems LXI H,ABRTF ; Abort flags MOV A,M ORI 80H MOV M,A ; write failure JMP ABORT ; quit ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ;Patch area PATCH DS 48 ; ;Equates, literals and storage for ISORT. ;Written by C. E. Duncan 1979 June 30. ;Revised 07:25 1981 February 4. ; ;Console messages ; CR: EQU 13 ;Carriage return LF: EQU 10 ;Line feed CCRLF: DB CR,LF,'$' FNIMSG: DB 'Unacceptable character in input file name.$' FNOMSG: DB 'Unacceptable character in output file name.$' FNPMSG: DB 'Input file not present.$' NDSMSG: DB 'No directory space for output file.$' RCLMSG: DB 'Enter record length: $' PARMSG: DB 'Enter sort parameters: $' ABMSG: DB 'Program discontinued.$' MULMSG: DB 'Overflow in multiply.$' DIVMSG: DB 'Divide by zero.$' RLMSG: DB 'File size not multiple of record length.$' FSZMSG: DB 'File larger than available memory.$' INXMSG: DB 'Array index out of bound.$' ; ; Storage ; PRMCHRS: DB '0123456789ad' ;permitted parameters ; ARBASE: DW 0 ;array base address RLEN: DB 0 ; record length - bytes ALWB: DW 0 ; array lower bound AUPB: DW 0 ; array upper bound CLWB: DW 0 ; current lower bound CUPB: DW 0 ; current upper bound BUFCNT: DB 0 ;characters in buffer NBRFND: DB 0 ;parameter number QMR: DW 0 ; Q - R SMP: DW 0 ; S - P PARM1: DB 0 ; sort parameters PARM2: DB 0 ; PARM3: DB 0 ; PARM4: DB 0 ; POOF1: DB 0 ;1st sort substr offset POOF2: DB 0 ;2nd sort substr offset SPL1: DB 0 ;1st sort substr length SPL2: DB 0 ;2nd sort substr length SSEQ1: DB 0 ;1st sort direction, 0=A, 1=D SSEQ2: DB 0 ;2nd sort direction AQR1: DW 0 ; .A[R][M:N] AQR2: DW 0 ; .A[R][V:W] AQS1: DW 0 ; .A[S][M:N] AQS2: DW 0 ; .A[S][V:W] NLBH: DW 0 ; new LWB for right partition NUBL: DW 0 ; new UPB for left partition ARIF: DW 0 ;Array index calculation base MARSIZ: DW 0 ; maximum available memory BYIF: DW 0 ; Total input file size - bytes ABRTF: DS 1 ;Abort flags FSCNT: DW 0 ;Sectors in input file RSCNT: DW 0 ;Sectors read count KWTP1: DW 0 ;Temporary storage, .AR[J][M:N] KWTP2: DW 0 ;Temporary storage, .AR[J][V:W] AWTP: DW 0 ; address of temp record storage PR: DW 0 ; R PS: DW 0 ; S ACR: DW 0 ; .A[R] ACS: DW 0 ; .A[S] MRLEN: DB 0 ; negative of RLEN CDSKSAV: DB 0 ;save default disk number SLIM: EQU 8 ;partition size lower limit ; DFCB: DS 36 ; output FCB DFDN: EQU DFCB+0 ; disk name DFEX: EQU DFCB+12 ; current extent DFCR: EQU DFCB+32 ; current/next/record number ; ; CONSOLE BUFFER ; CONBUF: DB CONLEN ; CONSIZ: DS 1 ;number current characters CONLIN: DS 254 ;character buffer SBUF: EQU CONSIZ ;temporary buffer for disk directory CONLEN: EQU $-CONSIZ ; ; Stack and pointers ; BSTKDP: EQU 16*4 ; PSTKDP: EQU 16*2 ; DS BSTKDP ;Bounds stack BSTACK: DW 0 ;Stack top DS PSTKDP ; program stack PSTACK: DW 0 ; base STLV: DS 1 ; current stack depth PSAVE: DW 0 ; program stack pointer BSAVE: DW 0 ; bounds stack pointer ; ; LOGICAL I/O FUNCTION EQUATES ; PCONBUF: EQU 9 ;print to console from buffer RCONBUF: EQU 10 ; read console to buffer SELDK: EQU 14 ;select disk OPEN: EQU 15 ;open disk file CLOSE: EQU 16 ;close disk file SRCHFST: EQU 17 ;search first occurrence of FCB in directory SRCHNXT: EQU 18 ;search next occurrence of FCB DELETE: EQU 19 ;delete file READSEQ: EQU 20 ;read next disk record WRITSEQ: EQU 21 ;write next disk record CREATE: EQU 22 ;create file and directory entry RTCDK EQU 25 ;return current disk number STDMAAD: EQU 26 ;set DMA address ; SFCB: EQU 05CH ;Input (default) FCB SFDN: EQU SFCB+0 ;disk number SFEX: EQU SFCB+12 ;current extent SFS1: EQU SFCB+13 ;bytes in last sector (maybe) SFDA: EQU SFCB+16 ;extent allocation vector SFCR: EQU SFCB+32 ;current/next/record number ; QUIT: EQU 0000H ;re-boot return to CPM BDOS: EQU 0005H ;DOS entry ; PROGRAM END DB 'BSORT 2-2.2 PROGRAM END' AR: DW 0 ;Base of sort array END ;