;		 SORTV.ASM ver 1.2
;		by Ward Christensen
;		 (revised 1/3/81)
;
;SIMPLE SORT PROGRAM FOR SORTING LISTS OF NAMES,
;OR ANY OTHER VARIABLE LENGTH FILE, WITH CR/LF
;DELIMITED RECORDS.
;
;THIS IS A "SIMPLE" PROGRAM: FILE MUST FIT IN MEMORY.
;
;--->NEEDS MAC TO ASSEMBLE<---
;
;01/03/81 Change stack init.  By Keith Petersen, W8SDZ
;
;11/15/80 Add @ command (WLC)
;
;10/24/80 Originally written by Ward Christensen
;
;FORMAT:
;	SORTV INPUT-NAME OUTPUT-NAME
; or	SORTV NAME
;
;if the second format is used, the file is read into
;memory, sorted, erased, created, and written back.
;
;	THE SORT WILL BE BASED ON THE FIRST CHARACTERS
;	IN THE FILE, UNLESS THE COMMAND IS FOLLOWED BY
;	AN "@" SIGN, THEN A STRING (1 OR MORE CHARACTERS)
;	TO SKIP.
;
;EX:	SORTV NAMES.SUB @.
;
;WILL SORT NAMES.SUB BY FILETYPE, SINCE IT SKIPS PAST
;THE "." BEFORE DOING THE COMPARE.
;
EOF	EQU	1AH
CR	EQU	0DH
LF	EQU	0AH
;
;(FROM EQU10.LIB...)
;
MF	SET	0	;SHOW MOVE NOT REQUESTED
CF	SET	0	;SHOW COMP NOT REQUESTED
;
;DEFINE SOME MACROS TO MAKE THINGS EASIER
;
;DEFINE DATA MOVE MACRO: MOVE from,to,length
;	from may be addr, or quoted string
;
MOVE	MACRO	?F,?T,?L
	MCSUB	?F,?T,?L ;;HANDLE ARGS
	CALL	MOVER
MF	SET	-1	;;SHOW EXPANSION
	ENDM
;
;COMPARE MACRO
COMP	MACRO	?F,?T,?L
	MCSUB	?F,?T,?L ;;HANDLE ARGS
	CALL	COMPR
CF	SET	-1	;;SHOW EXPANSION
	ENDM
;
;MCSUB - HANDLES MOVE, COMPARE ARGUMENTS
MCSUB	MACRO	?F,?T,?L
	IF	NOT NUL ?F
	IRPC	?C,?F
?Q	SET	'&?C&?C' ;;TEST FOR QUOTE
	EXITM
	ENDM
	IF	?Q EQ ''''
	LOCAL	?B,?Z
	CALL	?Z
?B	DB	?F
?Z	POP	H	;GET FROM
	LXI	B,?Z-?B	;GET LEN
	ELSE
	LXI	H,?F
	ENDIF
	ENDIF
	IF	NOT NUL ?T
	LXI	D,?T
	ENDIF
	IF	NOT NUL ?L
	LXI	B,?L
	ENDIF
	ENDM
;
;DEFINE CP/M MACRO - CPM FNC,PARM [,NOSAVE]
;
CPM	MACRO	?F,?P,?N
	IF	NUL ?N
	PUSH	B
	PUSH	D
	PUSH	H
	ENDIF
	IF	NOT NUL ?F
	MVI	C,?F
	ENDIF
	IF	NOT NUL ?P
	LXI	D,?P
	ENDIF
	CALL	BDOS
	IF	NUL ?N
	POP	H
	POP	D
	POP	B
	ENDIF
	ENDM
;
RDB	MACRO	?F
	PUSH	D
	PUSH	H
	LXI	H,?F
	CALL	RDBYTE
	POP	H
	POP	D
	ENDM
;
WRB	MACRO	?F
	PUSH	H
	LXI	H,?F
	CALL	WRBYTE
	POP	H
	ENDM
;
EFCB	MACRO	?B,?P,?F
	DW	?B
	DW	0
	DB	?P
	DW	?F
	ENDM
;
	ORG	100H
;
;INIT LOCAL STACK
;
	LXI	SP,STACK
;
	CALL	START
	DB	'SORTV rev 1.2'
	DB	CR,LF,'$'
;
START	POP	D	;GET ID
	MVI	C,PRINT
	CALL	BDOS	;PRINT ID
;
;START OF PROGRAM EXECUTION
;
	CALL	SVSKIP	;SAVE SKIP INFO
	CALL	CKNAMES	;SEE THAT 2 NAMES ARE THERE
	CALL	OPENIN	;OPEN INPUT FILE
	CALL	READN	;READ THE NAMES
	CALL	SORTN	;SORT THE NAMES
	CALL	WRITEN	;WRITE THE NAMES
	CALL	ERXIT
	DB	'++DONE++$'
;
;====>	SUBROUTINES
;	----------------
;
;====>	SAVE "SKIP TO" INFORMATION
;
SVSKIP	LXI	H,81H
;
SVSKL	MOV	A,M
	ORA	A
	RZ		;NO 'SKIP TO'
	CPI	'@'	;SKIP DELIMITER?
	INX	H
	JNZ	SVSKL
	LXI	D,SKIPC	;CHARS TO SKIP
;
SVSKL2	MOV	A,M
	STAX	D
	INX	H
	INX	D
	ORA	A
	JNZ	SVSKL2
	RET
;
;====>	CHECK THAT 2 NAMES WERE SUPPLIED
;
CKNAMES	LDA	FCB+1
	CPI	' '
	JZ	NONAME
	LDA	FCB2+1
	CPI	' '
	JZ	SAMENAM
	CPI	'@'	;SKIP PARM?
	JZ	SAMENAM
	MOVE	FCB2,OUTNAME,12
	RET
;
;OUTPUT NAME = INPUT NAME
;
SAMENAM	MOVE	FCB,OUTNAME,12
	RET
;
NONAME	CALL	ERXIT
	DB	'++Error - ',CR,LF
	DB	'Command format requires an '
	DB	'input name, and an output name.$'
;
;====>	OPEN THE INPUT FILE
;
OPENIN	CPM	OPEN,FCB
	INR	A
	RNZ		;SUCCESSFUL?  RETURN
	CALL	ERXIT
	DB	'++Input file not found$'
;
;====>	READ IN THE NAMES
;
READN	LXI	H,BUFF	;TO FIRST NAME
;
READNL	CALL	READL	;READ ONE LINE
	RC		;GOT EOF, RETURN
	CALL	CHAIN	;CHAIN THINGS TOGETHER
	JMP	READNL
;
;====>	READ ONE LINE
;
READL	SHLD	CURR	;SAVE CURR LINE PTR
	XRA	A	;GET 0
	MOV	M,A	;INIT FORWARD
	INX	H	;	POINTER
	MOV	M,A	;	TO
	INX	H	;	0
	LXI	D,SKIPC	;TO CK SKIP CHARS PRESENT
;
READLLP	LDA	BDOS+2	;ARE WE
	DCR	A	;	OVER-
	CMP	H	;	FLOW-
	JZ	OFLO	;	ING?
	RDB	EXTFCB	;READ A BYTE
	CPI	EOF	;SET CARRY
	STC		;	AND RETURN
	RZ		;	IF EOF
	MOV	M,A	;STORE CHAR
;TEST FOR SKIP CHAR FOUND
	MOV	B,A	;SAVE FOR COMPARE
	LDAX	D
	ORA	A	;NO MORE SKIP CHARS?
	JZ	READLNS	;NO MORE
	CMP	B	;A SKIP CHAR?
	JNZ	READLNS	;NO, KEEP TRYIN.
	INX	D	;TO NEXT SKIP CHAR
;
READLNS	INX	H	;POINT TO NEXT
	MOV	A,B	;GET CHAR
	CPI	CR	;END OF LINE?
	JNZ	READLLP	;	NO, LOOP.
	RDB	EXTFCB	;GOBBLE UP LF
	LDAX	D	;GET SKIP CHAR END
	ORA	A	;TEST IT AND SET "NO EOF"
	RZ
;ERROR - NO SKIP CHAR
	LHLD	CURR
	INX	H	;SKIP
	INX	H	;	POINTER
;
ERPLP	MOV	E,M
	CPM	WRCON
	MOV	A,M
	INX	H
	CPI	CR
	JNZ	ERPLP
	CALL	ERXIT
	DB	LF,'++NO SKIP CHAR FOUND++$'
;
OFLO	CALL	ERXIT
	DB	'++File won''t fit in memory$'
;
;====>	CHAIN RECORDS TOGETHER
;
CHAIN	PUSH	H	;SAVE POINTER
	LHLD	CURR	;GET CURRENT
	XCHG		;	TO DE
	LHLD	PREV	;PREV TO HL
	MOV	M,E	;MOVE CURR
	INX	H	;	TO
	MOV	M,D	;	PREV
	XCHG		;THEN MOVE
	SHLD	PREV	;	PREV TO CURR
	POP	H
	RET
;
;====>	SORT THE NAMES
;
SORTN	XRA	A	;SHOW NO
	STA	SWAPS	;	SWAPS
	LXI	H,PTR	;POINT PREV
	SHLD	PREV	;	TO PTR
	LHLD	PTR	;POINT TO FIRST
;
;HANDLE WIERD CASE OF ONLY ONE NAME
;
	MOV	A,M	;GET POINTER
	INX	H	;POINT TO NEXT
	ORA	M	;OR TOGETHER
	DCX	H	;BACK UP
	RZ		;RETURN IF ONLY ONE
;
SORTL	CALL	CMPR	;COMPARE ENTRIES
	CC	SWAP	;SWAP IF WRONG ORDER
	CALL	NEXT	;POINT TO NEXT
	JNC	SORTL	;LOOP IF MORE
	LDA	SWAPS	;ANY
	ORA	A	;	SWAPS?
	JNZ	SORTN	;YES, LOOP
	RET		;NO, RETURN
;
;---->	COMPARE TWO NAMES
;
CMPR	PUSH	H	;SAVE POINTER
	MOV	E,M	;GET NEXT
	INX	H	;	POINTER
	MOV	D,M	;	TO DE
	INX	D	;ALIGN POINTERS
;
;SKIP IF NECESS
;
	LXI	B,SKIPC
;
TSTSKIP	LDAX	B
	ORA	A
	JZ	COMPL	;NO SKIP
	INX	B
;
SKIP1	INX	H
	CMP	M
	JNZ	SKIP1
	XCHG		;SWAP
;
SKIP2	INX	H
	CMP	M
	JNZ	SKIP2
	XCHG		;PUT THINGS BACK
	JMP	TSTSKIP
;
COMPL	INX	D	;TO NEXT
	INX	H	;TO NEXT
	LDAX	D	;GET ONE
	CMP	M	;COMPARE
	JNZ	COMPNE	;NO COMPARE
	CPI	CR	;END?
	JNZ	COMPL	;	NO, LOOP
;
COMPH	POP	H	;RESTORE POINTER
	RET		;THEY ARE EQUAL
;
;COMPARE NOT EQUAL - SEE IF END OF ELEMENT,
;AND IF SO, CALL THEM EQUAL
;
COMPNE	MOV	A,M
	CPI	CR
	JZ	COMPH
	LDAX	D
	CMP	M
	JMP	COMPH	;CARRY SET AS APPROP
;
;---->	SWAP ENTRIES
;
;LOGIC:	PTR points to some entry, which points
;to another entry.  They are not in order.  Thus:
;point PTR to the second, point the second to
;the first, and point the first to what the
;second USED to point to.
;
SWAP	MVI	A,1
	STA	SWAPS	;SHOW WE SWAPPED
;BC=NEXT
	MOV	C,M
	INX	H
	MOV	B,M
	DCX	H
;CHAIN CURRENT TO NEXT ONES CHAIN
	LDAX	B
	MOV	M,A
	INX	B
	INX	H
	LDAX	B
	MOV	M,A
	DCX	B
	DCX	H
;SAVE CURRENT POINTER IN DE
	XCHG
;GET POINTER TO PREV
	LHLD	PREV
;POINT PREV TO NEXT
	MOV	M,C
	INX	H
	MOV	M,B
;STORE CURR IN NEXT
	MOV	A,E
	STAX	B
	INX	B
	MOV	A,D
	STAX	B
	DCX	B
;RESTORE CURRENT POINTER
	XCHG
	RET		;CURRENT POINTER IN DE
;
;---->	GET NEXT ENTRY, CARRY IF NOT 2 MORE
;
NEXT	SHLD	PREV	;SAVE POINTER
	MOV	E,M
	INX	H
	MOV	D,M
	XCHG		;HL= NEXT
	MOV	A,H	;CARRY ON
	ORA	L	;	IF HL
	STC		;	=
	RZ		;	0
	MOV	A,M	;GET
	INX	H	;SEE IF THERE
	ORA	M	;	IS
	DCX	H	;	ANOTHER
	RNZ		;THERE IS ANOTHER
	STC		;SHOW NOT 2 TO SWAP
	RET
;
;====>	WRITE THE NAMES
;
WRITEN	LXI	H,0	;INIT
	SHLD	EXTFCB+2 ;	EFCB
	XRA	A	;INIT
	STA	FCBEXT	;	THE
	STA	FCBRNO	;	FCB
	MOVE	OUTNAME,FCB,12 ;RESTORE NAME
	CPM	ERASE,FCB
	CPM	MAKE,FCB
	INR	A	;MAKE OK?
	JZ	BADOUT	;	NO, ERROR
	LHLD	PTR	;GET FIRST
;
WNLP	CALL	WRITEL		;WRITE ONE LINE
	JNC	WNLP		;LOOP IF MORE
	MVI	A,EOF		;WRITE EOF
	WRB	EXTFCB		;	CHAR
	LXI	H,EXTFCB	;FLUSH
	CALL	FLUSH		;	BUFFERS
	CPM	STDMA,80H	;RESET DMA
	CPM	CLOSE,FCB	;CLOSE,
	CALL	ERXIT	 	;	AND EXIT
	DB	'++DONE++$'
;
WRITEL	PUSH	H	;SAVE POINTER
	INX	H
;
WRLP	INX	H	;TO NEXT CHAR
	MOV	A,M	;GET CHAR
	WRB	EXTFCB	;WRITE IT
	MOV	A,M	;SEE IF END
	CPI	CR	;	OF LINE
	JNZ	WRLP	;NO, LOOP
	MVI	A,LF	;OTHERWISE
	WRB	EXTFCB	;	WRITE LF
	POP	H	;GET POINTER
	MOV	E,M	;GET 
	INX	H	;	FORWARD
	MOV	D,M	;	POINTER
	XCHG		;PUT IT IN HL
	MOV	A,H	;IS POINTER
	ORA	L	;	ZERO?
	RNZ		;NO, RETURN
	STC		;CARRY SHOWS END
	RET
;
BADOUT	CALL	ERXIT
	DB	'++Can''t make output file$'
;
;FOLLOWING FROM 'EQU10.LIB'---->
;
;MOVE, COMPARE SUBROUTINES
;
	IF	MF	;MACRO EXPANSION FLAG SET?
MOVER	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	MOVER
	RET
	ENDIF
;
	IF	CF	;MACRO EXPANSION FLAG SET?
COMPR	LDAX	D
	CMP	M
	RNZ
	INX	D
	INX	H
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	COMPR
	RET
	ENDIF
;
;	FROM EQU10.LIB: AS OF 07/19/80
;
;RDBYTE, HL POINTS TO EXTENDED FCB:
;
;	2 BYTE BUFFER ADDR
;	2 BYTE "BYTES LEFT" (INIT TO 0)
;	1 BYTE BUFFER SIZE (IN PAGES)
;	2 BYTE FCB ADDRESS
;
RDBYTE	MOV	E,M
	INX	H
	MOV	D,M	;GET BUFFER ADDR
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M	;BC = BYTES LEFT
	MOV	A,B	;GET COUNT
	ORA	C
	JNZ	RDBNORD	;NO READ
;
	INX	H	;TO BUFFER SIZE
	MOV	A,M	;GET COUNT
	ADD	A	;MULTIPLY BY 2
	MOV	B,A	;SECTOR COUNT IN B
	INX	H	;TO FCB
	PUSH	H	;SAVE FCB POINTER
	MOV	A,M	;GET..
	INX	H	;..FCB..
	MOV	H,M	;..ADDR..
	MOV	L,A	;..TO HL
;
RDBLP	MVI	A,1AH	;GET EOF CHAR
	STAX	D	;SAVE IN CASE EOF
	PUSH	D	;SAVE DMA ADDR
	PUSH	H	;SAVE FCB ADDR
	CPM	STDMA	;SET DMA ADDR
	POP	D	;GET FCB
	CPM	READ
	ORA	A
	POP	H	;HL=DMA, DE=FCB
	JNZ	RDBRET	;GOT EOF
	MOV	A,L
	ADI	80H	;TO NEXT BUFF
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	XCHG		;DMA TO DE, FCB TO HL
	DCR	B	;MORE SECTORS?
	JNZ	RDBLP	;YES, MORE
;
RDBRET	POP	H	;GET FCB POINTER
	DCX	H	;TO LENGTH
	MOV	A,M	;GET LENGTH
	DCX	H	;TO COUNT
	MOV	M,A	;SET PAGE COUNT
	DCX	H	;TO LO COUNT
	DCX	H	;TO HI FCB
	DCX	H	;TO EFCB START
	JMP	RDBYTE	;LOOP THRU AGAIN
;
RDBNORD	INX	H	;TO LENGTH
	MOV	A,M	;GET LENGTH (PAGES)
	XCHG		;BUFF TO HL
	ADD	H
	MOV	H,A	;HL = END OF BUFF
	MOV	A,L
	SUB	C
	MOV	L,A
	MOV	A,H
	SBB	B
	MOV	H,A	;HL = DATA POINTER
	MOV	A,M	;GET BYTE
	XCHG		;EFCB BACK TO HL
	CPI	1AH	;EOF?
	RZ		;YES, LEAVE POINTERS
	DCX	B	;DECR COUNT
	DCX	H	;BACK TO "BYTES LEFT"
	MOV	M,B
	DCX	H
	MOV	M,C	;STORE BACK COUNT
	RET
;
;SAMPLE EFCB:
;
;EFCB	DW	BUFF	;BUFFER ADDR
;	DW	0	;BYTES LEFT (OR TO WRITE)
;	DB	20	;BUFFER SIZE (IN PAGES)
;	DW	FCB	;FCB ADDRESS
;
;
;WRBYTE, HL POINTS TO EXTENDED FCB:
;
;	2 BYTE BUFFER ADDR
;	2 BYTE "BYTES LEFT" (INIT TO 0)
;	1 BYTE BUFFER SIZE (IN PAGES)
;	2 BYTE FCB ADDRESS
;
WRBYTE	MOV	E,M
	INX	H
	MOV	D,M	;DE=BUF ADDR
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M	;BC=BYTES IN BUFF
	PUSH	D	;SAVE FCB
	XCHG
	DAD	B	;TO NEXT BYTE
	MOV	M,A	;STORE IT
	INX	B	;ONE MORE
	XCHG
	POP	D
;
;SEE IF BUFFER IS FULL
;
	INX	H	;GET
	MOV	A,M	;	SIZE
	CMP	B	;FULL?
	JNZ	WRBNOWR	;NO WRITE
;
	ADD	A	;MULTIPLY BY 2
	MOV	B,A	;SECTOR COUNT IN B
	INX	H	;TO FCB
	PUSH	H	;SAVE FCB POINTER
	MOV	A,M	;GET..
	INX	H	;..FCB..
	MOV	H,M	;..ADDR..
	MOV	L,A	;..TO HL
;
WRBLP	PUSH	D	;SAVE DMA ADDR
	PUSH	H	;SAVE FCB ADDR
	CPM	STDMA	;SET DMA ADDR
	POP	D	;GET FCB
	CPM	WRITE
	ORA	A
	POP	H	;HL=DMA, DE=FCB
	JNZ	WRBERR	;GOT ERR
	MOV	A,L
	ADI	80H	;TO NEXT BUFF
	MOV	L,A
	MOV	A,H
	ACI	0
	MOV	H,A
	XCHG		;DMA TO DE, FCB TO HL
	DCR	B	;MORE SECTORS?
	JNZ	WRBLP	;YES, MORE
;
WRBRET	POP	H	;GET FCB POINTER
	DCX	H	;TO LENGTH
	DCX	H	;TO COUNT
	MVI	M,0	;SET 0 TO WRITE
	DCX	H	;TO LO COUNT
	MVI	M,0
	CPM	STDMA,80H
	RET
;
WRBNOWR	DCX	H	;TO LENGTH
	MOV	M,B	;SET NEW LENGTH
	DCX	H
	MOV	M,C
	RET
;
;FLUSH THE EFCB BUFFERS
;
FLUSH	MOV	E,M
	INX	H
	MOV	D,M	;DE=BUF ADDR
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M	;BC=BYTES IN BUFF
	INX	H	;TO COUNT
	MOV	A,B
	ORA	C
	RZ		;NOTHING TO WRITE
	MOV	A,C	;GET LOW COUNT
	ADD	A	;SHIFT HIGH TO CARRY
	MOV	A,B	;GET LOW COUNTAL
	RAL		;MULT BY 2, + CARRY
	INR	A	;FUDGE FOR PARTIAL SECT
	MOV	B,A	;SAVE SECTOR COUNT
	INX	H	;TO FCB
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A	;HL=FCB
;
FLUSHL	CPM	STDMA
	XCHG
	CPM	WRITE
	XCHG
	ORA	A
	JNZ	WRBERR
	PUSH	H
	LXI	H,80H
	DAD	D
	XCHG
	POP	H
	DCR	B
	JNZ	FLUSHL
	XCHG
	CPM	CLOSE
	INR	A
	RNZ
	CALL	ERXIT
	DB	'++OUTPUT FILE CLOSE ERROR ++$'
;
WRBERR	CALL	ERXIT
	DB	'++OUTPUT FILE WRITE ERROR++$'
;
;EXIT WITH ERROR MESSAGE
;
MSGEXIT	EQU	$	;EXIT W/"INFORMATIONAL" MSG
;
ERXIT	POP	D	;GET MSG
	MVI	C,PRINT
	CALL	BDOS
;
;EXIT, RESTORING STACK AND RETURN
;
EXIT	JMP	0
;
;====>	START OF WORK AREA
;
EXTFCB	EFCB	DKBUF,4,FCB
PREV	DW	PTR	;POINTER TO PREV POINTER
SKIPC	DB	0	;SKIP CHARS END
	DS	8	;VARIABLE SKIP CHARS
;
	DS	100	;STACK AREA
STACK	EQU	$
;
OUTNAME	DS	12	;OUTPUT FILENAME
SWAPS	DS	1
CURR	DS	2
PTR	DS	2	;POINTER TO FIRST NAME
	ORG	($+255) AND 0FF00H	;TO PAGE
DKBUF	DS	256*4	;4 PAGES OF BUFFER
BUFF	EQU	$	;NAMES READ IN HERE
;
;BDOS/CBIOS EQUATES (VERSION 10)
;
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
RDCONBF	EQU	10
CONST	EQU	11
OPEN	EQU	15
CLOSE	EQU	16
SRCHF	EQU	17
SRCHN	EQU	18
ERASE	EQU	19
READ	EQU	20
WRITE	EQU	21
MAKE	EQU	22
REN	EQU	23
STDMA	EQU	26
BDOS	EQU	5
FCB	EQU	5CH 
FCB2	EQU	6CH
FCBEXT	EQU	FCB+12
FCBRNO	EQU	FCB+32
;
	END
