;
; Christian Groessler, Dec-2001
;
; RS232 routines using the R: device (currently tested with an 850 only)
;
; unsigned char __fastcall__ rs232_init (char hacked);
; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
; unsigned char __fastcall__ rs232_done (void);
; unsigned char __fastcall__ rs232_get (char* B);
; unsigned char __fastcall__ rs232_put (char B);
; unsigned char __fastcall__ rs232_pause (void); [TODO]
; unsigned char __fastcall__ rs232_unpause (void); [TODO]
; unsigned char __fastcall__ rs232_status (unsigned char* status,
;                                          unsigned char* errors); [TODO]
;

        .import         findfreeiocb
        .import         __do_oserror
        .import         fddecusage
        .import         fdtoiocb
        .import         __inviocb
        .import         clriocb
        .import         newfd
        .import         _close, pushax, popax, popa
        .importzp       ptr1, tmp2, tmp3

        .export         _rs232_init, _rs232_params, _rs232_done, _rs232_get
        .export         _rs232_put, _rs232_pause, _rs232_unpause, _rs232_status

        .include        "atari.inc"
        .include        "errno.inc"
        .include        "rs232.inc"

        .rodata

rdev:   .byte   "R:", ATEOL, 0

        .bss

; receive buffer
RECVBUF_SZ = 256
recv_buf: .res  RECVBUF_SZ

cm_run: .res    1       ; concurrent mode running?

        .data

rshand: .word   $ffff

        .code

;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_init (char hacked);
; /* Initialize the serial port. The parameter is ignored in the Atari version.
;  * return 0/-1 for OK/Error
;  */
;

.proc   _rs232_init

        jsr     findfreeiocb
        bne     init_err
        txa
        tay                     ; move iocb # into Y
        lda     #3
        sta     tmp3            ; name length + 1
        lda     #<rdev
        ldx     #>rdev
        jsr     newfd
        tya
        bcs     doopen          ; C set: open needed / device not already open

        pha
        jsr     _rs232_done     ;** shut down if started  @@@TODO check this out!!
        pla

doopen: tax
        pha
        jsr     clriocb
        pla
        tax
        lda     #<rdev
        sta     ICBAL,x
        lda     #>rdev
        sta     ICBAH,x
        lda     #OPEN
        sta     ICCOM,x

        lda     #$0D            ; mode in+out+concurrent
        sta     ICAX1,x
        lda     #0
        sta     ICAX2,x
        sta     ICBLL,x         ; zap buf len
        sta     ICBLH,x
        jsr     CIOV
        bmi     cioerr1

        lda     tmp2            ; get fd
        sta     rshand
        ldx     #0
        stx     rshand+1
        txa
        rts

cioerr1:jsr     fddecusage      ; decrement usage counter of fd as open failed

init_err:
        ldx     #0
        lda     #RS_ERR_INIT_FAILED
        rts

.endproc        ; _rs232_init


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_params (unsigned char params, unsigned char parity);
;
; Set communication parameters.
;
; params contains baud rate, stop bits and word size
; parity contains parity
;
; 850 manual documents restrictions on the baud rate (not > 300), when not
; using 8 bit word size. So only 8 bit is currently tested.
;

.proc   _rs232_params

        sta     tmp2
        lda     rshand
        cmp     #$ff
        bne     work            ; work only if initialized
        lda     #RS_ERR_NOT_INITIALIZED
        bne     done
work:   lda     rshand
        ldx     #0
        jsr     fdtoiocb        ; get iocb index into X
        bmi     inverr          ; shouldn't happen
        tax

        ; set handshake lines

        lda     #34             ; xio 34, set cts, dtr etc
        sta     ICCOM,x
        lda     #192+48+3       ; DTR on, RTS on, XMT on
        sta     ICAX1,x
        lda     #0
        sta     ICBLL,x
        sta     ICBLH,x
        sta     ICBAL,x
        sta     ICBAH,x
        sta     ICAX2,x
        jsr     CIOV
        bmi     cioerr

        ; set baud rate, word size, stop bits and ready monitoring

        lda     #36             ; xio 36, baud rate
        sta     ICCOM,x
        jsr     popa            ; get parameter
        sta     ICAX1,x
        ;ICAX2 = 0, monitor nothing
        jsr     CIOV
        bmi     cioerr

        ; set translation and parity

        lda     #38             ; xio 38, translation and parity
        sta     ICCOM,x
        lda     tmp2
        ora     #32             ; no translation
        sta     ICAX1,x
        jsr     CIOV
        bmi     cioerr

        lda     #0
done:   ldx     #0
        rts

inverr: jmp     __inviocb

.endproc        ;_rs232_params

cioerr: jmp     __do_oserror


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_done (void);
; /* Close the port, deinstall the interrupt hander. You MUST call this function
;  * before terminating the program, otherwise the machine may crash later. If
;  * in doubt, install an exit handler using atexit(). The function will do
;  * nothing, if it was already called.
;  */
;

.proc   _rs232_done

        lda     rshand
        cmp     #$ff
        beq     done
work:   ldx     rshand+1
        jsr     pushax
        jsr     _close
        pha
        txa
        pha
        ldx     #$ff
        stx     rshand
        stx     rshand+1
        inx
        stx     cm_run
        pla
        tax
        pla
done:   rts

.endproc        ;rs232_done


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_get (char* B);
; /* Get a character from the serial port. If no characters are available, the
;  * function will return RS_ERR_NO_DATA, so this is not a fatal error.
;  */
;

.proc   _rs232_get

        ldy     rshand
        cpy     #$ff
        bne     work            ; work only if initialized
        lda     #RS_ERR_NOT_INITIALIZED
        bne     nierr

work:   sta     ptr1
        stx     ptr1+1          ; store pointer to received char

        lda     rshand
        ldx     #0
        jsr     fdtoiocb
        tax
        lda     cm_run          ; concurrent mode already running?
        bne     go
        jsr     ena_cm          ; turn on concurrent mode

go:     ; check whether there is any input available

        lda     #STATIS         ; status request, returns bytes pending
        sta     ICCOM,x
        jsr     CIOV
        bmi     cioerr          ; @@@ error handling

        lda     DVSTAT+1        ; get byte count pending
        ora     DVSTAT+2
        beq     nix_da          ; no input waiting...

        ; input is available: get it!

        lda     #GETCHR         ; get raw bytes
        sta     ICCOM,x         ; in command code
        lda     #0
        sta     ICBLL,x
        sta     ICBLH,x
        sta     ICBAL,x
        sta     ICBAH,x
        jsr     CIOV            ; go get it
        bmi     cioerr          ; @@@ error handling

        ldx     #0
        sta     (ptr1,x)        ; return received byte
        txa
        rts

nierr:  ldx     #0
        rts

nix_da: lda     #RS_ERR_NO_DATA
        ldx     #0
        rts

.endproc        ;_rs232_get


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_put (char B);
; /* Send a character via the serial port. There is a transmit buffer, but
;  * transmitting is not done via interrupt. The function returns
;  * RS_ERR_OVERFLOW if there is no space left in the transmit buffer.
;  */
;

.proc   _rs232_put

        ldy     rshand
        cpy     #$ff
        bne     work            ; work only if initialized
        lda     #RS_ERR_NOT_INITIALIZED
        bne     nierr

work:   pha
        lda     rshand
        ldx     #0
        jsr     fdtoiocb
        tax
        lda     cm_run          ; concurrent mode already running?
        bne     go
        jsr     ena_cm          ; turn on concurrent mode

        ; @@@TODO:       check output buffer overflow
go:     lda     #PUTCHR         ; put raw bytes
        sta     ICCOM,x         ; in command code
        lda     #0
        sta     ICBLL,x
        sta     ICBLH,x
        sta     ICBAL,x
        sta     ICBAH,x
        pla                     ; get the char back
        jsr     CIOV            ; go do it
        rts

nierr:  ldx     #0
        rts

.endproc        ;_rs232_put

;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_pause (void);
; /* Assert flow control and disable interrupts. */
;

_rs232_pause:


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_unpause (void);
; /* Re-enable interrupts and release flow control */
;

_rs232_unpause:


;----------------------------------------------------------------------------
;
; unsigned char __fastcall__ rs232_status (unsigned char* status,
;                                          unsigned char* errors);
; /* Return the serial port status. */
;

_rs232_status:

        lda     #255
        tax
        rts


; enable concurrent rs232 mode
; gets iocb index in X
; all registers destroyed

.proc   ena_cm

        lda     #40             ; XIO 40, start concurrent IO
        sta     ICCOM,x
        sta     cm_run          ; indicate concurrent mode is running
        lda     #0
        sta     ICAX1,x
        sta     ICAX2,x
        lda     #<recv_buf
        sta     ICBAL,x
        lda     #>recv_buf
        sta     ICBAH,x
        lda     #<RECVBUF_SZ
        sta     ICBLL,x
        lda     #>RECVBUF_SZ
        sta     ICBLH,x
        lda     #$0D            ; value from 850 man, p62.  must be 0D?,
        sta     ICAX1,x         ;  or any non-zero?
        jmp     CIOV

.endproc        ;ena_cm

        .end
