
		; ************************
		; **                    **
		; **  INIT CENTRAL I/O  **
		; **                    **
		; ************************

		; CIO initialization (called by monitor at power up)
	
ICIO:		ldx	#0
@1:		lda	#$FF		; set all IOCB's to free
		sta	ICHID,X		; by setting handler ID's=$FF
		lda	#<(IIN-1)
		sta	ICPTL,X		; point PUT to error routine
		lda	#>(IIN-1)
		sta	ICPTH,X
		txa
		clc
		adc	#$10		; bump index by size
		tax
		cmp	#$80		; done?
		bcc	@1		; no
		rts			; yes, return

		; error routine for illegal PUT
	
IIN:		ldy	#133		; IOCB not open
		rts

		; *******************************
		; **                           **
		; **  CENTRAL I/O (602 bytes)  **
		; **                           **
		; *******************************

		; CIO interfaces between user and input/output device
	
CIO:		sta	ICAX6Z		; save possible output character
		stx	ICAX5Z		; save IOCB number * N

		; check for legal IOCB
	
		txa
		and	#%00001111	; is IOCB multiple of 16? 
		bne	@1		; no, error
		cpx	#$80		; is index too large?
		bcc	@2		; no

		; invalid IOCB number -- return error
	
@1:		ldy	#134		; error code
		jmp	E670		; return

		; move user IOCB to zero page
	
@2:		ldy	#0
@3:		lda	IOCB,X		; user IOCB
		sta	ZIOCB,Y		; to zero page
		inx
		iny
		cpy	#12		; 12 bytes
		bcc	@3

		; new stuff...
	
		lda	ICHIDZ
		cmp	#$7F
		bne	L6
		lda	ICCOMZ		; Handler Index Number == $7F
		cmp	#12
		beq	CICLOS		; Close command?
		lda	HNDLOD
		bne	L5		; Should we do a peripheral command?

E510:		ldy	#130		; ERROR: Nonexistent Device
L4:		jmp	E670

L5:		jsr	CA29		; Do peripheral command
		bmi	L4

		; Compute CIO internal vector for command
	
L6:		ldy	#132		; assume invalid code
		lda	ICCOMZ		; command code to index
		cmp	#3		; is command legal?
		bcc	E670_2		; no
		tay

		; move command to zero base for index
	
		cpy	#14		; is command special?
		bcc	@7		; no
		ldy	#14		; yes, set special offset index
@7:		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

		; OPEN command
		; find device handler in handler address table
	
CIOPEN:		lda	ICHIDZ		; get handler ID
		cmp	#$FF		; is this IOCB closed?
		beq	W1		; yes

		; error -- IOCB already open
	
	ldy	#129			; error code
E670_2:	jmp	E670			; return

		; new stuff...
	
W1:		lda	HNDLOD
		bne	W2

		; go find device
	
		jsr	DEVSRC		; call device search
		bcs	W2		; go if device not found

		; device found, initialize IOCB for open

		lda	#0		; reserve 0 bytes
		sta	DVSTAT
		sta	DVSTAT+1

		; compute handler entry point

E55C:		jsr	COMENT
		bcs	E670_2		; go if error in compute

		; go to handler for initialization
	
		jsr	GOHAND		; use indirect jump

		; store PUT BYTE address-1 into IOCB
		
		lda	#11		; simulate put character
		sta	ICCOMT
		jsr	COMENT		; compute entry point
		lda	ICAX3Z		; move computed value
		sta	ICPTLZ		; to PUT BYTE address
		lda	ICAX4Z
		sta	ICPTHZ
		jmp	E672		; return to user

		; new stuff...
	
W2:		jsr	EEF9
		jmp	E670

		; CLOSE command

CICLOS:		ldy	#1		; assume good CLOSE
		sty	ICSTAZ 
		jsr	COMENT		; compute handler entry point
		bcs	@1		; go if error in compute
		jsr	GOHAND		; go to handler to close device
@1:		lda	#$FF		; get IOCB "free" value
		sta	ICHIDZ		; set handler ID
		lda	#>(IIN-1)
		sta	ICPTHZ		; set PUT BYTE to point to error
		lda	#<(IIN-1)
		sta	ICPTLZ
		jmp	E672		; return

		; STATUS and SPECIAL requests
		; do implied open if necessary and go to device

CISTSP:		lda	ICHIDZ		; is there a handler ID?
		cmp	#$FF
		bne	@1		; yes

		; IOCB is free, do implied open

		jsr	DEVSRC		; find device in table
		bcs	E670_2		; go if error in compute

		; compute and go to entry point in handler

@1:		jsr	COMENT		; compute handler entry vector
		jsr	GOHAND		; go to handler

		; restore handler index (do implied close)

		ldx	ICAX5Z		; IOCB index
		lda	ICHID,X		; get original handler ID
		sta	ICHIDZ		; restore zero page
		jmp	E672		; return

		; read -- do GET commands
	
CIREAD:		lda	ICCOMZ		; get command byte
		and	ICAX1Z		; is this read legal?
		bne	rlegal		; yes

		; illegal read -- IOCB opened for write only
	
		ldy	#131		; error code
E670_3:		jmp	E670		; return

		; compute and check entry point
	
rlegal:		jsr	COMENT		; compute entry point
		bcs	E670_3		; go if error in compute

		; get record or characters
	
		lda	ICBLLZ
		ora	ICBLHZ		; is buffer length zero?
		bne	@2		; no
		jsr	GOHAND
		sta	ICAX6Z
		jmp	E672

		; loop to fill buffer or end record
	
@2:		jsr	GOHAND		; go to handler to get byte
		sta	ICAX6Z		; save byte
		bmi	@6		; 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	@3		; no

		; check for EOL on text records
	
		lda	ICAX6Z		; get byte
		cmp	#$9B		; is it an EOL?
		bne	@3		; no
		jsr	DECBFL		; yes, decrement buffer length
		jmp	@6		; end transfer

		; check buffer full

@3:		jsr	DECBFL		; decrement buffer length
		bne	@2		; continue if non zero

		; buffer full, record not ended
		; discard bytes until end of record
	
		lda	ICCOMZ		; get command byte
		and	#2		; is it GET CHARACTER? 
		bne	@6		; yes, end transfer
	
		; loop to wait for EOL

@4:		jsr	GOHAND		; get byte from handler
		sta	ICAX6Z		; save character
		bmi	@5		; go if error

		; text record, wait for EOL
	
		lda	ICAX6Z		; get got byte
		cmp	#$9B		; is it EOL?
		bne	@4		; no, continue

		; end of record, buffer full -- send truncated record message
	
		lda	#137		; ERROR: truncated record
		sta	ICSTAZ		; store in IOCB

		; new stuff...
	
@5:		jsr	DECBFA
		ldy	#0
		lda	#$9B
		sta	(ICBALZ),Y
		jsr	INCBFP

		; transfer done
	
@6:		jsr	SUBBFL		; set final buffer length
		jmp	E672		; return

		; write -- do PUT commands
	
CIWRIT:		lda	ICCOMZ		; get command byte
		and	ICAX1Z		; is this write legal?
		bne	Z1		; yes

		; illegal write -- device opened for read only
	
		ldy	#135		; error code
E670_4:		jmp	E670		; return

		; compute and check entry point

Z1:		jsr	COMENT		; compute handler entry point
		bcs	E670_4		; go if error in compute

		; put record or characters
	
		lda	ICBLLZ
		ora	ICBLHZ		; is buffer length zero?
		bne	@2		; no
		lda	ICAX6Z		; get character
		inc	ICBLLZ		; set buffer length=1
		bne	@3		; then just transfer one byte

		; loop to transfer bytes from buffer to handler
	
@2:		ldy	#0
		lda	(ICBALZ),Y	; get byte from buffer
		sta	ICAX6Z		; save
@3:		jsr	GOHAND		; go put byte

		; new stuff...
	
		php			; save N flag
		jsr	INCBFP		; increment buffer pointer
		jsr	DECBFL		; decrement buffer length
		plp			; restore N flag

		bmi	@5		; end if error
	
		lda	ICCOMZ		; get command byte
		and	#2		; is it PUT RECORD?
		bne	@4		; no

		; text record -- check for EOL transfer
	
		lda	ICAX6Z		; get last character
		cmp	#$9B		; is it an EOL?
		beq	@5		; no

		; new stuff..
	
@4:		lda	ICBLLZ
		ora	ICBLHZ		; check buffer length
		bne	@2		; continue if non-zero

		; buffer empty, record not filled
		; check type of transfer
	
		lda	ICCOMZ		; get command code
		and	#2		; is it PUT CHARACTER?
		bne	@5		; yes, end transfer

		; put record (text), buffer empty, send EOL
	
		lda	#$9B
		jsr	GOHAND		; go to handler

		; end PUT transfer
		
@5:		jsr	SUBBFL		; set actual PUT buffer length
		jmp	E672		; return

		; CIO returns with Y=status
	
E670:		sty	ICSTAZ

		; returns with status stored in ICSTAZ
		; move IOCB in zero page back to user area
	
E672:		ldy	ICAX5Z		; get IOCB index
		lda	ICBAL,Y
		sta	ICBALZ		; restore user buffer pointer
		lda	ICBAH,Y
		sta	ICBAHZ
		ldx	#0		; loop count and index
		stx	HNDLOD
@1:		lda	ZIOCB,X		; zero page
		sta	ICHID,Y		; to user area
		inx
		iny
		cpx	#12		; 12 bytes
		bcc	@1

		; restore A, X & Y
	
		lda	ICAX6Z		; get last character
		ldx	ICAX5Z		; IOCB index
		ldy	ICSTAZ		; get STATUS and set flags
		rts			; return to user

		; ***********************
		; **  CIO SUBROUTINES  **
		; ***********************

		; COMENT -- check and compute handler entry point
	
COMENT:		ldy	ICHIDZ		; get handler index
		cpy	#34		; is it a legal index?
		bcc	@1		; yes

		; illegal handler index means device not open for operation
	
		ldy	#133		; error code
		bcs	@2		; return

		; use handler address table and command table to get vector
	
@1:		lda	HATABS+1,Y	; get low byte of address
		sta	ICAX3Z		; and save in pointer
		lda	HATABS+2,Y	; get hi byte of address
		sta	ICAX4Z
		ldy	ICCOMT		; get command code
		lda	COMTAB-3,Y	; get command offset
		tay
		lda	(ICAX3Z),Y	; get low byte of vector from
		tax			; handler itself and save
		iny
		lda	(ICAX3Z),Y	; get hi byte of vector
		sta	ICAX4Z
		stx	ICAX3Z		; set lo byte 
		clc			; show no error
@2:		rts

		; DECBFL -- decrement buffer length double byte
		; Z-flag = 0 on return if length = 0 after decrement
	
DECBFL:		lda	ICBLLZ		; will low byte go below?
		bne	@1		; no
		dec	ICBLHZ		; decrement hi byte
@1:		dec	ICBLLZ		; decrement low byte
		lda	ICBLLZ
		ora	ICBLHZ		; set Z if both are zero
		rts

		; DECBFA -- decrement buffer address diuble byte
	
DECBFA:		lda	ICBALZ		; will low byte go below?
		bne	@1		; no
		dec	ICBAHZ		; decrement hi byte
@1:		dec	ICBALZ		; decrement lo byte
		rts

		; INCBFP -- increment working buffer pointer
	
INCBFP:		inc	ICBALZ		; bump low byte
		bne	@1		; go if not zero
		inc	ICBAHZ		; else, bump hi byte
@1:		rts

		; SUBBFL -- set buffer length = buffer length - working byte count
	
SUBBFL:		ldx	ICAX5Z		; get IOCB index
		sec
		lda	ICBLL,X		; get low byte of initial length
		sbc	ICBLLZ		; subtract final low byte
		sta	ICBLLZ		; and save back
		lda	ICBLH,X		; get hi byte
		sbc	ICBLHZ
		sta	ICBLHZ
		rts

		; GOHAND -- go indirect to a device handler
		; Y = status on return, N flag=1 if error on return
	
GOHAND:		ldy	#146		; "no function" status for handler rts
		jsr	@1		; use the indirect jump
		sty	ICSTAZ		; save status
		cpy	#0		; and set N flag
		rts

		; indirect jump to handler by Paul's method
	
@1:		tax			; save A
		lda	ICAX4Z		; get jump address hi byte
		pha			; put on stack
		lda	ICAX3Z		; get jump address lo byte
		pha			; put on stack
		txa			; restore A
		ldx	ICAX5Z		; get IOCB index
		rts			; go to handler indirectly

		; DEVSRC -- device search, find device in handler address table
		;           set ICDNO
	
DEVSRC:		SEC 
		ldy	#1
		lda	(ICBALZ),Y	; get device number (drive number)
		sbc	#'1'		; subtract ASCII one
		bmi	@1
		cmp	#9		; is number in range?
		bcc	@2		; yes

@1:		lda	#0		; no, default to one
@2:		sta	ICDNOZ		; save device number+1
		inc	ICDNOZ
	
		; loop to find device
		
		ldy	#0		; get device name from user
		lda	(ICBALZ),Y
	
E716:		beq	@4
		ldy	#33		; initial compare index
@3:		cmp	HATABS,Y	; is this the device?
		beq	@5		; yes
		dey
		dey			; else, point to next device
		dey
		bpl	@3		; continue for all devices

		; no device found, declare non-existent device error
	
@4:		ldy	#130		; error code
		sec			; show error
		rts			; and return

		; found device, set ICHID
	
@5:		tya
		sta	ICHIDZ		; save handler index
		clc			; show no error
		rts			; return

COMTAB:		.byte	0,4,4,4,4,6,6,6,6,2,8,10
