; .PAGE
 
;PAGE	16

;	.PAGE
;	LIST	S
;	.TITLE	'CENTRAL INPUT/OUTPUT (CIO) 2-7-79'
;		UPDATED BY AL MILLER 3-9-79
ASCZER	=	'0'			;ASCII ZERO
COLON	=	$3A		;ASCII COLON
EOL	=	$9B		;END OF RECORD
 

;PAGE	17

;	.PAGE
;
; CIO JUMP VECTOR FOR USERS
	.ORG	CIOV	
	JMP	CIO		;GO TO CIO
;
; CIO INIT JUMP VECTOR FOR POWER UP
	.ORG	CIOINV
	JMP	CIOINT		;GO TO INIT
;
;
; ERROR ROUTINE ADDRESS EQUATE
; ERRTNH =ERRTN/256	"MOVED TO LINE 788"
; ERRTNL =-ERRTNH*256+ERRTN "MOVED TO LINE 789"
;
;
	.ORG	CIOORG
;
; CIO INITIALIZATION (CALLED BY MONITOR AT POWER UP)
CIOINT: LDX	#0
CIOI1:	LDA	#IOCFRE		;SET ALL IOCB'S TO FREE
	STA	ICHID,X		;BY SETTING HANDLER ID'S=$FF
	LDA	#ERRTNL
	STA	ICPTL,X		;POINT PUT TO ERROR ROUTINE
	LDA	#ERRTNH
	STA	ICPTH,X
	TXA
	CLC
	ADC	#IOCBSZ		;BUMP INDEX BY SIZE
	TAX
	CMP	#MAXIOC		;DONE?
	BCC	CIOI1		;NO
	RTS			;YES, RETURN
;
; ERROR ROUTINE FOR ILLEGAL PUT
ERRTN	= * - 1
ERRTNH	=ERRTN/256
ERRTNL	=(-ERRTNH)*256+ERRTN
	LDY	#NOTOPN		;IOCB NOT OPEN
	RTS
	
;PAGE	18

;	.PAGE
;
; CIO LOCAL RAM (USES SPARE BYTES IN ZERO PAGE IOCB)
ENTVEC	=	ICSPRZ
;
; CIO MAIN ROUTINE
;
; CIO INTERFACES BETWEEN USER AND INPUT/OUTPUT DE
CIO:	STA	CIOCHR		;SAVE POSSIBLE OUTPUT CHARACTER
	STX	ICIDNO		;SAVE IOCB NUMBER * N
;
; CHECK FOR LEGAL IOCB
	TXA
	AND	#$F		;IS IOCB MULTIPLE OF 16?
	BNE	CIERR1		;NO, ERROR
	CPX	#MAXIOC		;IS INDEX TOO LARGE?
	BCC	IOC1		;NO
;
; INVALID IOCB NUMBER -- RETURN ERROR
CIERR1: LDY	#BADIOC		;ERROR CODE
	JMP	CIRTN1		;RETURN
;
; MOVE USER IOCB TO ZERO PAGE
IOC1:	LDY	#0
IOC1A:	LDA	IOCB,X		;USER IOCB
	STA	IOCBAS,Y	;TO ZERO PAGE
	INX
	INY
	CPY	#12		;12 BYTES
	BCC	IOC1A
;
; COMPUTE CIO INTERNAL VECTOR FOR COMMAND
	LDY	#NVALID		;ASSUME INVALID CODE
	LDA	ICCOMZ		;COMMAND CODE TO INDEX
	CMP	#OPEN		;IS COMMAND LEGAL?
	BCC	CIERR4		;NO
	TAY
;
; MOVE COMMAND TO ZERO BASE FOR INDEX
	CPY	#SPECIL		;IS COMMAND SPECIAL?
	BCC	IOC2		;NO
	LDY	#SPECIL		;YES, SET SPECIAL OFFSET INDEX
IOC2:	STY	ICCOMT		;SAVE COMMAND FOR VECTOR
	LDA	COMTAB-3,Y	;GET VECTOR OFFSET FROM TABLE
	BEQ	CIOPEN		;GO IF OPEN COMMAND
	CMP	#2		;IS IT CLOSE?
	BEQ	CICLOS		;YES
	CMP	#8		;IS IT STATUS OR SPECIAL?
	BCS	CISTSP		;YES
	CMP	#4		;IS IT READ?
	BEQ	CIREAD		;YES
	JMP	CIWRIT		;ELSE, MUST BE WRITE
 	
;PAGE	19

;	.PAGE
;
; OPEN COMMAND
;
; FIND DEVICE HANDLER IN HANDLER ADDRESS TABLE
CIOPEN: LDA	ICHIDZ		;GET HANDLER ID
	CMP	#IOCFRE		;IS THIS IOCB CLOSED?
	BEQ	IOC6		;YES
;
; ERROR -- IOCB ALREADY OPEN
CIERR3: LDY	#PRVOPN		;ERROR CODE
CIERR4: JMP	CIRTN1		;RETURN
;
; GO FIND DEVICE
IOC6:	JSR	DEVSRC		;CALL DEVICE SEARCH
	BCS	CIERR4		;GO IF DEVICE NOT FOUND
;
; DEVICE FOUND,	INITIALIZE IOCB FOR OPEN
;
; COMPUTE HANDLER ENTRY POINT
IOC7:	JSR	COMENT
	BCS	CIERR4		;GO IF ERROR IN COMPUTE
;
; GO TO HANDLER FOR INITIALIZATION
	JSR	GOHAND		;USE INDIRECT JUMP
;
; STORE PUT BYTE ADDRESS-1 INTO IOCB
	LDA	#PUTCHR		;SIMULATE PUT CHARACTER
	STA	ICCOMT
	JSR	COMENT		;COMPUTE ENTRY POINT
	LDA	ICSPRZ		;MOVE COMPUTED VALUE
	STA	ICPTLZ		;TO PUT BYTE ADDRESS
	LDA	ICSPRZ+1
	STA	ICPTHZ
	JMP	CIRTN2		;RETURN TO USER
 	
;PAGE	20

;	.PAGE
;
;
; CLOSE COMMAND
CICLOS: LDY	#SUCCES		;ASSUME GOOD CLOSE
	STY	ICSTAZ
	JSR	COMENT		;COMPUTE HANDLER ENTRY POINT
	BCS	CICLO2		;GO IF ERROR IN COMPUTE
	JSR	GOHAND		;GO TO HANDLER TO CLOSE DEVICE
CICLO2: LDA	#IOCFRE		;GET IOCB "FREE" VALUE
	STA	ICHIDZ		;SET HANDLER ID
	LDA	#ERRTNH
	STA	ICPTHZ		;SET PUT BYTE TO POINT TO ERROR
	LDA	#ERRTNL
	STA	ICPTLZ
	JMP	CIRTN2		;RETURN
;
;
; STATUS AND SPECIAL REQUESTS
; DO IMPLIED OPEN IF NECESSARY AND GO TO DEVICE
CISTSP: LDA	ICHIDZ		;IS THERE A HANDLER ID?
	CMP	#IOCFRE
	BNE	CIST1		;YES
;
; IOCB IS FREE, DO IMPLIED OPEN
	JSR	DEVSRC		;FIND DEVICE IN TABLE
	BCS	CIERR4		;GO IF ERROR IN COMPUTE
;
; COMPUTE AND GO TO ENTRY POINT IN HANDLER
CIST1:	JSR	COMENT		;COMPUTER HANDLER ENTRY VECTOR
	JSR	GOHAND		;GO TO HANDLER
;
; RESTORE HANDLER INDEX (DO IMPLIED CLOSE)
	LDX	ICIDNO		;IOCB INDEX
	LDA	ICHID,X		;GET ORIGINAL HANDLER ID
	STA	ICHIDZ		;RESTORE ZERO PAGE
	JMP	CIRTN2		;RETURN
	
;PAGE	21
;	.PAGE
;
; READ -- DO GET COMMANDS
CIREAD: LDA	ICCOMZ		;GET COMMAND BYTE
	AND	ICAX1Z		;IS THIS READ LEGAL?
	BNE	RCI1A		;YES
;
; ILLEGAL READ -- IOCB OPENED FOR WRITE ONLY
	LDY	#WRONLY		;ERROR CODE
RCI1B:	JMP	CIRTN1		;RETURN
;
; COMPUTE AND CHECK ENTRY POINT
RCI1A:	JSR	COMENT		;COMPUTE ENTRY POINT
	BCS	RCI1B		;GO IF ERROR IN COMPUTE
;
; GET RECORD OR CHARACTERS
	LDA	ICBLLZ
	ORA	ICBLLZ+1	;IS BUFFER LENGTH ZERO?
	BNE	RCI3		;NO
	JSR	GOHAND
	STA	CIOCHR
	JMP	CIRTN2
;
; LOOP TO FILL BUFFER OR END RECORD
RCI3:	JSR	GOHAND		;GO TO HANDLER TO GET BYTE
	STA	CIOCHR		;SAVE BYTE
	BMI	RCI4		;END TRANSFER IF ERROR
	LDY	#0
	STA	(ICBALZ),Y	;PUT BYTE IN USER BUFFER
	JSR	INCBFP		;INCREMENT BUFFER POINTER
	LDA	ICCOMZ		;GET COMMAND CODE
	AND	#2		;IS IT GET RECORD?
	BNE	RCI1		;NO
;
; CHECK FOR EOL ON TEXT RECORDS
	LDA	CIOCHR		;GET BYTE
	CMP	#EOL		;IS IT AN EOL?
	BNE	RCI1		;NO
	JSR	DECBFL		;YES, DECREMENT BUFFER LENGTH
	JMP	RCI4		;END TRANSFER
;
; CHECK BUFFER FULL
RCI1:	JSR	DECBFL		;DECREMENT BUFFER LENGTH
	BNE	RCI3		;CONTINUE IF NON ZERO
 
;PAGE	22

;	.PAGE
;
; BUFFER FULL, RECORD NOT ENDED
; DISCARD BYTES UNTIL END OF RECORD
RCI2:	LDA	ICCOMZ		;GET COMMAND BYTE
	AND	#2		;IS IT GET CHARACTER?
	BNE	RCI4		;YES, END TRANSFER
;
; LOOP TO WAIT FOR EOL
RCI6:	JSR	GOHAND		;GET BYTE FROM HANDLER
	STA	CIOCHR		;SAVE CHARACTER
	BMI	RCI4		;GO IF ERROR
;
; TEXT RECORD, WAIT FOR EOL
	LDA	CIOCHR		;GET GOT BYTE
	CMP	#EOL		;IS IT EOL?
	BNE	RCI6		;NO, CONTINUE
;
; END OF RECORD, BUFFER FULL -- SEND TRUNCATED RECORD MESSAGE
RCI11:	LDA	#TRNRCD		;ERROR CODE
	STA	ICSTAZ		;STORE IN IOCB
;
; TRANSFER DONE
RCI4:	JSR	SUBBFL		;SET FINAL BUFFER LENGTH
	JMP	CIRTN2		;RETURN
 	
;PAGE	23

;	.PAGE
;
; WRITE -- DO PUT COMMANDS
CIWRIT: LDA	ICCOMZ		;GET COMMAND BYTE
	AND	ICAX1Z		;IS THIS WRITE LEGAL?
	BNE	WCI1A		;YES
;
; ILLEGAL WRITE -- DEVICE OPENED FOR READ ONLY
	LDY	#RDONLY		;ERROR CODE
WCI1B:	JMP	CIRTN1		;RETURN
;
; COMPUTE AND CHECK ENTRY POINT
WCI1A:	JSR	COMENT		;COMPUTE HANDLER ENTRY POINT
	BCS	WCI1B		;GO IF ERROR IN COMPUTE
;
; PUT RECORD OR CHARACTERS
	LDA	ICBLLZ
	ORA	ICBLLZ+1	;IS BUFFER LENGTH ZERO?
	BNE	WCI3		;NO
	LDA	CIOCHR		;GET CHARACTER
	INC	ICBLLZ		;SET BUFFER LENGTH=1
	BNE	WCI4		;THEN JUST TRANSFER ONE BYTE
;
; LOOP TO TRANSFER BYTES FROM BUFFER TO HANDLER
WCI3:	LDY	#0
	LDA	(ICBALZ),Y	;GET BYTE FROM BUFFER
	STA	CIOCHR		;SAVE
WCI4:	JSR	GOHAND		;GO PUT BYTE
	BMI	WCI5		;END IF ERROR
	JSR	INCBFP		;INCREMENT BUFFER POINTER
;
; CHECK FOR TEXT RECORD
	LDA	ICCOMZ		;GET COMMAND BYTE
	AND	#2		;IS IT PUT RECORD?
	BNE	WCI1		;NO
;
; TEXT RECORD -- CHECK FOR EOL TRANSFER
	LDA	CIOCHR		;GET LAST CHARACTER
	CMP	#EOL		;IS IT AN EOL?
	BNE	WCI1		;NO
	JSR	DECBFL		;DECREMENT BUFFER LENGTH
	JMP	WCI5		;END TRANSFER
;
; CHECK FOR BUFFER EMPTY
WCI1:	JSR	DECBFL		;DECREMENT BUFFER LENGTH
	BNE	WCI3		;CONTINUE IF NON ZERO
	
;PAGE	24

;	.PAGE
;
; BUFFER EMPTY, RECORD NOT FILLED
; CHECK TYPE OF TRANSFER
WCI2:	LDA	ICCOMZ		;GET COMMAND CODE
	AND	#2		;IS IT PUT CHARACTER?
	BNE	WCI5		;YES, END TRANSFER
;
; PUT RECORD (TEXT), BUFFER EMPTY, SEND EOL
	LDA	#EOL
	JSR	GOHAND		;GO TO HANDLER
;
; END PUT TRANSFER
WCI5:	JSR	SUBBFL		;SET ACTUAL PUT BUFFER LENGTH
	JMP	CIRTN2		;RETURN
 
;PAGE	25

;	.PAGE
;
; CIO RETURNS
; RETURNS WITH Y=STATUS
CIRTN1: STY	ICSTAZ		;SAVE STATUS
;
; RETURNS WITH STATUS STORED IN ICSTAZ
; MOVE IOCB IN ZERO PAGE BACK TO USER AREA
CIRTN2: LDY	ICIDNO		;GET IOCB INDEX
	LDA	ICBAL,Y
	STA	ICBALZ		;RESTORE USER BUFFER POINTER
	LDA	ICBAH,Y
	STA	ICBAHZ
	LDX	#0		;LOOP COUNT AND INDEX
CIRT3:	LDA	IOCBAS,X	;ZERO PAGE
	STA	IOCB,Y		;TO USER AREA
	INX
	INY
	CPX	#12		;12 BYTES
	BCC	CIRT3
;
; RESTORE A,X, & Y
	LDA	CIOCHR		;GET LAST CHARACTER
	LDX	ICIDNO		;IOCB INDEX
	LDY	ICSTAZ		;GET STATUS AND SET FLAGS
	RTS			;RETURN TO USER
 
;PAGE	26

;	.PAGE
;
;
; CIO SUBROUTINES
;
; COMENT -- CHECK AND COMPUTE HANDLER ENTRY POINT
COMENT: LDY	ICHIDZ		;GET HANDLER INDEX
	CPY	#MAXDEV+1	;IS IT A LEGAL INDEX?
	BCC	COM1		;YES
;
; ILLEGAL HANDLER INDEX MEANS DEVICE NOT OPEN FOR OPERATION
	LDY	#NOTOPN		;ERROR CODE
	BCS	COM2		;RETURN
;
; USE HANDLER ADDRESS TABLE AND COMMAND TABLE TO GET VECTOR
COM1:	LDA	HATABS+1,Y	;GET LOW BYTE OF ADDRESS
	STA	ICSPRZ		;AND SAVE IN POINTER
	LDA	HATABS+2,Y	;GET HI BYTE OF ADDRESS
	STA	ICSPRZ+1
	LDY	ICCOMT		;GET COMMAND CODE
	LDA	COMTAB-3,Y	;GET COMMAND OFFSET
	TAY
	LDA	(ICSPRZ),Y	;GET LOW BYTE OF VECTOR FROM
	TAX			;HANDLER ITSELF AND SAVE
	INY
	LDA	(ICSPRZ),Y	;GET HI BYTE OF VECTOR
	STA	ICSPRZ+1
	STX	ICSPRZ		;SET LO BYTE
	CLC			;SHOW NO ERROR
COM2:	RTS
;
;
; DECBFL -- DECREMENT BUFFER LENGTH DOUBLE BYTE
; Z FLAG = 0 ON RETURN IF LENGTH = 0 AFTER DECREMENT
DECBFL: DEC	ICBLLZ		;DECREMENT LOW BYTE
	LDA	ICBLLZ		;CHECK IT
	CMP	#$FF		;DID IT GO BELOW?
	BNE	DECBF1		;NO
	DEC	ICBLLZ+1	;DECREMENT HI BYTE
DECBF1: ORA	ICBLLZ+1	;SET Z IF BOTH ARE ZERO
	RTS
;
;
; INCBFP -- INCREMENT WORKING BUFFER POINTER
INCBFP: INC	ICBALZ		;BUMP LOW BYTE
	BNE	INCBF1		;GO IF NOT ZERO
	INC	ICBALZ+1	;ELSE, BUMP HI BYTE
INCBF1: RTS
;
;
; SUBBFL -- SET BUFFER LENGTH = BUFFER LENGTH - WORKING BYTE COUNT
SUBBFL: LDX	ICIDNO		;GET IOCB INDEX
	SEC
	LDA	ICBLL,X		;GET LOW BYTE OF INITIAL LENGTH
 
;PAGE	27

	SBC	ICBLLZ		;SUBTRACT FINAL LOW BYTE
	STA	ICBLLZ		;AND SAVE BACK
	LDA	ICBLH,X		;GET HI BYTE
	SBC	ICBLLZ+1
	STA	ICBLHZ
	RTS
;
;
; GOHAND -- GO INDIRECT TO A DEVICE HANDLER
; Y= STATUS ON RETURN, N FLAG=1 IF ERROR ON RETURN
GOHAND: LDY	#FNCNOT		;PREPARE NO FUNCTION STATUS FOR HANDLER RTS
	JSR	CIJUMP		;USE THE INDIRECT JUMP
	STY	ICSTAZ		;SAVE STATUS
	CPY	#0		;AND SET N FLAG
	RTS
;
; INDIRECT JUMP TO HANDLER BY PAUL'S METHOD
CIJUMP: TAX			;SAVE A
	LDA	ICSPRZ+1	;GET JUMP ADDRESS HI BYTE
	PHA			;PUT ON STACK
	LDA	ICSPRZ		;GET JUMP ADDRESS LO BYTE
	PHA			;PUT ON STACK
	TXA			;RESTORE A
	LDX	ICIDNO		;GET IOCB INDEX
	RTS			;GO TO HANDLER INDIRECTLY
	
;PAGE	28

;	.PAGE
;
; DEVSRC -- DEVICE SEARCH, FIND DEVICE IN HANDLER ADDRESS TABLE
;
; LOOP TO FIND DEVICE
DEVSRC: LDY	#0
	LDA	(ICBALZ),Y	;GET DEVICE NAME FROM USER
	BEQ	CIERR2
	LDY	#MAXDEV		;INITIAL COMPARE INDEX
DEVS1:	CMP	HATABS,Y	;IS THIS THE DEVICE?
	BEQ	DEVS2		;YES
	DEY
	DEY			;ELSE, POINT TO NEXT DEVICE NAME
	DEY
	BPL	DEVS1		;CONTINUE FOR ALL DEVICES
;
; NO DEVICE FOUND, DECLARE NON-EXISTENT DEVICE ERROR
CIERR2: LDY	#NONDEV		;ERROR CODE
	SEC			;SHOW ERROR
	BCS	DEVS4		;AND RETURN
;
; FOUND DEVICE, SET ICHID,ICDNO, AND INIT DEVICE
DEVS2:	TYA
	STA	ICHIDZ		;SAVE HANDLER INDEX
	SEC
	LDY	#1
	LDA	(ICBALZ),Y	;GET DEVICE NUMBER (DRIVE NUMBER)
	SBC	#ASCZER		;SUBTRACT ASCII ZERO
	CMP	#$A		;IS NUMBER IN RANGE?
	BCC	DEVS3		;YES
	LDA	#1		;NO, DEFAULT TO ONE
DEVS3:	STA	ICDNOZ		;SAVE DEVICE NUMBER
	CLC			;SHOW NO ERROR
;
; RETURN
DEVS4:	RTS
	
;PAGE	29

;	.PAGE
;
;
; CIO ROM TABLES
;
; COMMAND TABLE
; MAPS EACH COMMAND TO OFFSET FOR APPROPRIATE VECTOR IN HANDLER
COMTAB: .BYTE	0,4,4,4,4,6,6,6,6,2,8,10
LENGTH	=(*-CIOINT)
CRNTP1	=*
	.ORG	$14
CIOSPR: .BYTE	(INTORG-CRNTP1) ;^GCIOL IS TOO LONG
 
