;
; Oliver Schmidt, 30.12.2004
;
; int open (const char* name, int flags, ...);
;
; Be sure to keep the value priority of closeallfiles lower than that of
; closeallstreams (which is the high level C file I/O counterpart and must be
; called before closeallfiles).

        .export 	_open, closedirect, freebuffer
        .destructor	closeallfiles, 17

        .import		pushname, popname
        .import 	errnoexit, oserrexit
        .import		_posix_memalign, _free
        .import 	addysp, incsp4, incaxy, pushax, popax

        .include	"zeropage.inc"
        .include	"errno.inc"
        .include	"fcntl.inc"
        .include	"mli.inc"
        .include	"filedes.inc"

_open:
        ; Throw away all parameters except name
        ; and flags occupying together 4 bytes
        dey
        dey
        dey
        dey
        jsr	addysp

        ; Start with first fdtab slot
        ldy	#$00

        ; Check for free fdtab slot
:       lda	fdtab + FD::REF_NUM,y
        beq	found

        ; Advance to next fdtab slot
        .assert .sizeof(FD) = 4, error
        iny
        iny
        iny
        iny

        ; Check for end of fdtab
        cpy	#MAX_FDS * .sizeof(FD)
        bcc	:-

        ; Load errno code
        lda	#EMFILE

        ; Cleanup stack
errno:  jsr	incsp4		; Preserves A

        ; Return errno
        jmp	errnoexit

        ; Save fdtab slot
found:  tya
        pha

        ; Alloc I/O buffer
	lda	#<(fdtab + FD::BUFFER)
	ldx	#>(fdtab + FD::BUFFER)
	jsr	incaxy
	jsr	pushax
        lda	#$00
        ldx	#>$0100
        jsr	pushax		; Preserves A
        ldx	#>$0400
        jsr	_posix_memalign
	tay			; Save errno code

        ; Restore fdtab slot
        pla
        sta	tmp2		; Save fdtab slot

        ; Check for error
        tya			; Restore errno code
        bne	errno

        ; Get and save flags
        jsr	popax
        sta	tmp3

        ; Get and push name
        jsr	popax
        jsr	pushname
        bne	oserr1

        ; Set pushed name
        lda	sp
        ldx	sp+1
        sta	mliparam + MLI::OPEN::PATHNAME
        stx	mliparam + MLI::OPEN::PATHNAME+1

        ; Check for create flag
        lda	tmp3		; Restore flags
        and	#O_CREAT
        beq	open

        ; PATHNAME already set
        .assert MLI::CREATE::PATHNAME = MLI::OPEN::PATHNAME, error

        ; Set all other parameters from template
        ldx	#(MLI::CREATE::CREATE_TIME+1) - (MLI::CREATE::PATHNAME+1) - 1
:       lda	CREATE,x
        sta	mliparam + MLI::CREATE::ACCESS,x
        dex
        bpl	:-

        ; Create file
        lda	#CREATE_CALL
        ldx	#CREATE_COUNT
        jsr	callmli
        bcc	open

        ; Check for ordinary errors
        cmp	#$47		; "Duplicate filename"
        bne	oserr2

        ; Check for exclusive flag
        lda	tmp3		; Restore flags
        and	#O_EXCL
        beq	open

        lda	#$47		; "Duplicate filename"

        ; Cleanup name
oserr2: jsr	popname		; Preserves A

oserr1: ldy	tmp2		; Restore fdtab slot

        ; Cleanup I/O buffer
        pha			; Save oserror code
        jsr	freebuffer
        pla			; Restore oserror code

        ; Return oserror
        jmp	oserrexit

open:   ldy	tmp2		; Restore fdtab slot

        ; Set allocated I/O buffer
        ldx	fdtab + FD::BUFFER+1,y
        sta	mliparam + MLI::OPEN::IO_BUFFER		; A = 0
        stx	mliparam + MLI::OPEN::IO_BUFFER+1

        ; Open file
        lda	#OPEN_CALL
        ldx	#OPEN_COUNT
        jsr	callmli
        bcs	oserr2

        ; Get and save fd
        ldx	mliparam + MLI::OPEN::REF_NUM
        stx	tmp1		; Save fd

        ; Set flags and check for truncate flag
        lda	tmp3		; Restore flags
        sta	fdtab + FD::FLAGS,y
        and	#O_TRUNC
        beq	done

        ; Set fd and zero size
        stx	mliparam + MLI::EOF::REF_NUM
        ldx	#$02
        lda	#$00
:       sta	mliparam + MLI::EOF::EOF,x
        dex
        bpl	:-

        ; Set file size
        lda	#SET_EOF_CALL
        ldx	#EOF_COUNT
        jsr	callmli
        bcc	done

        ; Cleanup file
        pha			; Save oserror code
        lda	tmp1		; Restore fd
        jsr	closedirect
        pla			; Restore oserror code
        bne	oserr2		; Branch always

        ; Store fd
done:   lda	tmp1		; Restore fd
        sta	fdtab + FD::REF_NUM,y

        ; Convert fdtab slot to handle
        .assert .sizeof(FD) = 4, error
        tya
        lsr
        lsr

        ; Cleanup name
        jsr	popname		; Preserves A

        ; Return success
        ldx	#$00
        rts

freebuffer:
        ; Free I/O buffer
        lda	#$00
        ldx	fdtab + FD::BUFFER+1,y
        jmp	_free

closeallfiles:
        ; All open files
        lda	#$00

closedirect:
        ; Set fd
        sta	mliparam + MLI::CLOSE::REF_NUM

        ; Call close
        lda	#CLOSE_CALL
        ldx	#CLOSE_COUNT
        jmp	callmli

        .rodata

CREATE: .byte	%11000011	; ACCESS:	Standard full access
        .byte	$06		; FILE_TYPE:	Standard binary file
        .word	$0000		; AUX_TYPE:	Load address N/A
        .byte	$01		; STORAGE_TYPE:	Standard seedling file
        .word	$0000		; CREATE_DATE:	Current date
        .word	$0000		; CREATE_TIME:	Current time