; Christian Groessler, chris@groessler.org, 2013
;
-.if .defined(__ATARIXL__)
+;DEBUG = 1
+CHKBUF = 1 ; check if bounce buffering is needed (bounce buffering is always done if set to 0)
+.ifdef __ATARIXL__
+
+SHRAM_HANDLERS = 1
.include "atari.inc"
- .include "save_area.inc"
- .import __CHARGEN_START__
-
- .export sram_init
- .export KEYBDV_wrapper
-
-.macro disable_rom
- lda PORTB
- and #$fe
- sta PORTB
- lda #>__CHARGEN_START__
- sta CHBAS
- sta CHBASE
-.endmacro
-.macro enable_rom
- lda PORTB
- ora #1
- sta PORTB
- lda #$E0
- sta CHBAS
- sta CHBASE
-.endmacro
+ .include "save_area.inc"
+ .include "zeropage.inc"
+ .include "romswitch.inc"
+
+ .import __CHARGEN_START__
-.segment "INIT"
+ .export sram_init
+ .export KEYBDV_handler
+ .export CIO_handler
+ .export SIO_handler
+ .export SETVBV_handler
+ .export XMOVE_handler
+
+BUFSZ = 128 ; bounce buffer size
+BUFSZ_SIO = 256
+
+.segment "ONCE"
; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer
sram_init:
; disable all interrupts
- sei
- ldx #0
- stx NMIEN ; disable NMI
+ ldx #0
+ stx NMIEN ; disable NMI
+ sei
; disable ROMs
- disable_rom
+ disable_rom
; setup interrupt vectors
- lda #<my_IRQ_han
- sta $fffe
- lda #>my_IRQ_han
- sta $ffff
-
- lda #<my_RESET_han
- sta $fffc
- lda #>my_RESET_han
- sta $fffd
-
- lda #<my_NMI_han
- sta $fffa
- lda #>my_NMI_han
- sta $fffb
-
-; setup pointers to CIOV and SIOV wrappers
- lda #$4C ; JMP opcode
- sta CIOV
- lda #<my_CIOV
- sta CIOV+1
- lda #>my_CIOV
- sta CIOV+2
- lda #$4C ; JMP opcode
- sta SIOV
- lda #<my_SIOV
- sta SIOV+1
- lda #>my_SIOV
- sta SIOV+2
+ lda #<my_IRQ_han
+ sta $fffe
+ lda #>my_IRQ_han
+ sta $ffff
+
+ lda #<my_RESET_han
+ sta $fffc
+ lda #>my_RESET_han
+ sta $fffd
+
+ lda #<my_NMI_han
+ sta $fffa
+ lda #>my_NMI_han
+ sta $fffb
; enable interrupts
- lda #$40
- sta NMIEN
- cli
+ cli
+ lda #$40
+ sta NMIEN
+
+ rts
+
+.segment "EXTZP" : zeropage
+
+zpptr1: .res 2
- rts
+
+.segment "LOWBSS"
+
+; bounce buffers for CIO and SIO calls
+bounce_buffer: .res BUFSZ_SIO
.segment "LOWCODE"
-.macro int_wrap orgvec
- .local ret
- pha
- enable_rom
- lda #>ret
- pha
- lda #<ret
- pha
- php
- jmp (orgvec)
-ret: disable_rom
- pla
- rti
+
+; Interrupt handlers
+; ------------------
+
+; The interrupt handlers don't look at the current state of PORTB and
+; unconditionally disable the ROMs on exit.
+; Please note that this works, since if the ROMs are enabled we anyway
+; aren't being called here because the vectors are pointing to their
+; original ROM locations.
+
+.macro int_wrap orgvec
+ .local ret
+ pha
+ enable_rom_quick
+ lda #>ret
+ pha
+ lda #<ret
+ pha
+ php
+ jmp (orgvec)
+ret: disable_rom_quick
+ pla
+ rti
.endmacro
my_IRQ_han:
- int_wrap IRQ_save
+.ifdef DEBUG
+ php
+ pha
+ tya
+ pha
+ ldy #0
+ lda (SAVMSC),y
+ clc
+ adc #1
+ sta (SAVMSC),y
+ pla
+ tay
+ pla
+ plp
+.endif
+ int_wrap $FFFE
my_NMI_han:
- int_wrap NMI_save
+.ifdef DEBUG
+ php
+ pha
+ tya
+ pha
+ ldy #39
+ lda (SAVMSC),y
+ clc
+ adc #1
+ sta (SAVMSC),y
+ pla
+ tay
+ pla
+ plp
+.endif
+; set I bit to interrupted value
+ pha
+ txa
+ pha
+ tsx
+ lda $103,x
+ pha
+ plp
+ pla
+ tax
+ pla
+ int_wrap $FFFA
my_RESET_han:
- int_wrap RESET_save
-
-
-my_CIOV:
- pha
- enable_rom
- pla
- jsr CIOV_org
- php
- pha
- disable_rom
- pla
- plp
- rts
-
-my_SIOV:
- pha
- enable_rom
- pla
- jsr SIOV_org
- php
- pha
- disable_rom
- pla
- plp
- rts
-
-KEYBDV_wrapper:
- lda #>(kret-1)
- pha
- lda #<(kret-1)
- pha
- enable_rom
- lda KEYBDV+5
+ enable_rom
+ jmp ($FFFC)
+
+
+; System request handlers
+; -----------------------
+
+
+; for filenames we assume they will fit into our bounce buffer
+
+; one filename, terminated by "invalid character", located at ICBAL/ICBAH
+
+CIO_filename:
+.if CHKBUF
+ jsr chk_CIO_buf_fn
+ bcc CIO_call_a
+.endif
+ jsr setup_zpptr1_y0
+ jsr copy_filename
+CIO_fn_cont:
+ jsr bncbuf_to_iocb
+ ldy CIO_y
+ jsr CIO_call_a ; call CIO (maybe A isn't needed, then we could call CIO_call)
+ php
+ pha
+ jsr restore_icba ; restore original ICBAL/ICBAH
+ pla
+ plp
+ rts ; back to application
+
+
+; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
+
+CIO_filename2:
+.if CHKBUF
+ jsr chk_CIO_buf_fn2
+ bcc CIO_call_a
+.endif
+ jsr setup_zpptr1_y0
+ jsr copy_filename
+ iny
+ jsr copy_filename
+ jmp CIO_fn_cont
+
+
+
+; CIO handler
+; We have buffer pointer and length entries in the IOCB, but their
+; usage depends on the function.
+; Some functions don't care about any of them (pointer and length),
+; and some only use the pointer (like e.g. OPEN), and some use both.
+; So we need function specific handlers to correctly deal with
+; buffers which are overlapping with the ROM area.
+;
+; FIXME: Currently only the requests used by the runtime lib are handled.
+
+CIO_handler:
+
+; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
+
+ sta CIO_a
+ sty CIO_y
+ stx CIO_x
+
+ lda ICCOM,x ; get function
+ cmp #OPEN
+ beq CIO_filename ; filename as input parameter in buffer, length not used
+ cmp #PUTREC
+ bcc CIO_read ; input (GETREC or GETCHR)
+ cmp #CLOSE
+ bcc CIO_write_jmp ; output (PUTREC or PUTCHR)
+ beq CIO_call_a ; pass through, buffer not used
+ cmp #RENAME ; 2 filenames as input parameters in buffer, length not used
+ beq CIO_filename2
+ cmp #GETCWD
+ bcc CIO_filename ; filename as input parameter in buffer, length not used
+ beq CIO_invalid ; GETCWD not supported yet
+ bcs CIO_call_a ; other commands: assume no buffer
+; not reached
+
+; enable ROM, call CIO, disable ROM
+
+CIO_call_a:
+ lda CIO_a
+
+CIOV_call:
+ pha
+ lda PORTB
+ sta cur_CIOV_PORTB
+ enable_rom
+ pla
+ jsr CIOV_org
+ php
+ pha
+ disable_rom_val cur_CIOV_PORTB
+ pla
+ plp
+ rts
+
+
+CIO_write_jmp:
+ jmp CIO_write
+
+CIO_invalid:
+ lda CIO_a
+ ldy #DINVCM
+ rts
+
+; READ handler
+; ------------
+
+CIO_read:
+ lda ICBLL,x
+ ora ICBLH,x
+ beq CIO_call_a ; special I/O through A register in case buffer length is 0
+
+.if CHKBUF
+ jsr chk_CIO_buf
+ bcc CIO_call_a
+.endif
+
+; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
+; Otherwise we can get away with one call and a copy to the final destination afterwards.
+
+ lda ICBLH,x ; get high byte of length
+ bne big_read ; not zero -> data too large for our buffers
+ ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
+ lda #<BUFSZ
+ cmp ICBLL,x
+ bcc big_read
+
+; Data size fits into bounce buffer
+
+ jsr setup_zpptr1
+ jsr bncbuf_to_iocb
+ jsr CIO_call_a ; call CIO
+ php
+ bpl @no_err
+ cpy #EOFERR
+ beq @no_err
+ pha
+ jsr restore_icba
+ pla
+ plp
+ rts ; return with error
+
+@no_err:
+ sta CIO_a
+ sty CIO_y
+
+ jsr copy_to_user ; copy data into user buffer
+ jsr restore_icba
+
+ lda CIO_a
+ ldy CIO_y
+ plp
+ rts ; return with success
+
+; Data size does not fit into bounce buffer
+
+big_read:
+ lda #0
+ sta retlen ; initialize return length
+ sta retlen+1
+ jsr iocblen_to_orig_len
+ jsr iocbptr_to_orig_ptr
+ jsr setup_zpptr1
+ jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
+
+br_loop:
+ jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
+ bcs br_last ; no, last transfer, use remaining size
+
+ lda #>BUFSZ
+ sta ICBLH,x ; set data length
+ lda #<BUFSZ
+ sta ICBLL,x
+ bne br_cont
+
+br_last:
+ lda orig_len+1
+ sta ICBLH,x ; set data length
+ lda orig_len
+ sta ICBLL,x
+
+br_cont:
+ sta req_len ; remember length of this request
+ lda ICBLH,x
+ sta req_len+1
+ jsr CIO_call_a ; do the request
+ php
+ bpl br_no_err
+ cpy #EOFERR
+ beq br_no_err
+
+ pha
+ jsr restore_icba
+ pla
+ plp
+ rts ; return with error
+
+br_no_err:
+ sta CIO_a
+ sty CIO_y
+ pla
+ sta CIO_p
+ jsr copy_to_user
+
+; update retlen
+ clc
+ lda retlen
+ adc ICBLL,x
+ sta retlen
+ lda retlen+1
+ adc #0
+ sta retlen+1
+
+; if the request read less bytes than requested, we're done
+ lda ICBLL,x
+ cmp req_len
+ bne br_done
+ lda ICBLH,x
+ cmp req_len+1
+ bne br_done
+
+; update user buffer pointer (zpptr1)
+ clc
+ lda zpptr1
+ adc ICBLL,x
+ sta zpptr1
+ lda zpptr1+1
+ adc #0
+ sta zpptr1+1
+
+; update remaining length
+ sec
+ lda orig_len
+ sbc ICBLL,x
+ sta orig_len
+ lda orig_len+1
+ sbc #0
+ sta orig_len+1
+
+; still something left to do (remaining length != 0)?
+ lda orig_len
+ ora orig_len+1
+ beq br_done
+ jmp br_loop
+
+; done, write original buffer pointer and total transfer length to IOCB and return to application
+br_done:
+ lda retlen
+ sta ICBLL,x
+ lda retlen+1
+ sta ICBLH,x
+ jsr orig_ptr_to_iocbptr
+ lda CIO_p
+ pha
+ lda CIO_a
+ ldy CIO_y
+ plp
+ rts ; return with success
+
+
+
+CIO_call_a_jmp:
+ jmp CIO_call_a
+
+
+
+; WRITE handler
+; -------------
+
+
+CIO_write:
+ lda ICBLL,x
+ ora ICBLH,x
+ beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
+
+.if CHKBUF
+ jsr chk_CIO_buf
+ bcc CIO_call_a_jmp
+.endif
+
+; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
+; Otherwise we can get away with a copy to the bounce buffer and the call.
+
+ lda ICBLH,x ; get high byte of length
+ bne big_write ; not zero -> data too large for our buffers
+ ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
+ lda #<BUFSZ
+ cmp ICBLL,x
+ bcc big_write
+
+
+; Data size fits into bounce buffer
+
+ jsr setup_zpptr1
+ jsr bncbuf_to_iocb
+ jsr copy_from_user
+ ldy CIO_y
+ jsr CIO_call_a
+ php
+ pha
+ jsr restore_icba
+ pla
+ plp
+ rts ; return to application
+
+
+; Data size does not fit into bounce buffer
+
+big_write:
+ lda #0
+ sta retlen ; initialize return length
+ sta retlen+1
+ jsr iocblen_to_orig_len
+ jsr iocbptr_to_orig_ptr
+ jsr setup_zpptr1
+ jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
+
+bw_loop:
+ jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
+ bcs bw_last ; no, last transfer, use remaining size
+
+ lda #>BUFSZ
+ sta ICBLH,x ; set data length
+ lda #<BUFSZ
+ sta ICBLL,x
+ bne bw_cont
+
+bw_last:
+ lda orig_len+1
+ sta ICBLH,x ; set data length
+ lda orig_len
+ sta ICBLL,x
+
+bw_cont:
+ sta req_len ; remember length of this request
+ lda ICBLH,x
+ sta req_len+1
+ jsr copy_from_user
+ jsr CIO_call_a ; do the request
+ php
+ bpl bw_no_err
+
+ plp
+ rts ; error return
+
+bw_no_err:
+ sta CIO_a
+ sty CIO_y
+ pla
+ sta CIO_p
+
+; update retlen
+ clc
+ lda retlen
+ adc ICBLL,x
+ sta retlen
+ lda retlen+1
+ adc #0
+ sta retlen+1
+
+; if the request wrote less bytes than requested, we're done
+ lda ICBLL,x
+ cmp req_len
+ bne bw_done
+ lda ICBLH,x
+ cmp req_len+1
+ bne bw_done
+
+; update user buffer pointer (zpptr1)
+ clc
+ lda zpptr1
+ adc ICBLL,x
+ sta zpptr1
+ lda zpptr1+1
+ adc #0
+ sta zpptr1+1
+
+; update remaining length
+ sec
+ lda orig_len
+ sbc ICBLL,x
+ sta orig_len
+ lda orig_len+1
+ sbc #0
+ sta orig_len+1
+
+; still something left to do (remaining length != 0)?
+ lda orig_len
+ ora orig_len+1
+ beq bw_done
+ jmp bw_loop
+
+bw_done:
+ lda retlen
+ sta ICBLL,x
+ lda retlen+1
+ sta ICBLH,x
+ jsr orig_ptr_to_iocbptr
+ lda CIO_p
+ pha
+ lda CIO_a
+ ldy CIO_y
+ plp
+ rts ; return with success
+
+
+
+; check if length is larger than bounce buffer size
+; input: orig_len - length
+; output: A - destroyed
+; CF - 0/1 for larger/not larger
+cmp_orig_len_bnc_bufsz:
+ sec
+ lda #<BUFSZ
+ sbc orig_len
+ lda #>BUFSZ
+ sbc orig_len+1
+ rts
+
+
+; copy data from bounce buffer into user buffer
+; input: X - IOCB index
+; zpptr1 - pointer to user buffer
+; output: A - destroyed
+; Y - 0
+copy_to_user:
+ ldy ICBLL,x ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
+ beq @copy_done
+@copy: dey
+ lda bounce_buffer,y
+ sta (zpptr1),y
+ cpy #0
+ bne @copy
+@copy_done:
+ rts
+
+
+; copy data from user buffer into bounce buffer
+; input: X - IOCB index
+; zpptr1 - pointer to user buffer
+; output: A - destroyed
+; Y - 0
+copy_from_user:
+ ldy ICBLL,x ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
+ beq @copy_done
+@copy: dey
+ lda (zpptr1),y
+ sta bounce_buffer,y
+ cpy #0
+ bne @copy
+@copy_done:
+ rts
+
+
+; copy ICBLL/ICBLH to 'orig_len'
+; input: X - IOCB index
+; output: A - destroyed
+iocblen_to_orig_len:
+ lda ICBLL,x
+ sta orig_len
+ lda ICBLH,x
+ sta orig_len+1
+ rts
+
+
+; copy ICBAL/ICBAH to 'orig_ptr'
+; input: X - IOCB index
+; output: A - destroyed
+iocbptr_to_orig_ptr:
+ lda ICBAL,x
+ sta orig_ptr
+ lda ICBAH,x
+ sta orig_ptr+1
+ rts
+
+
+; copy 'orig_ptr' to ICBAL/ICBAH
+; input: X - IOCB index
+; output: A - destroyed
+orig_ptr_to_iocbptr:
+ lda orig_ptr
+ sta ICBAL,x
+ lda orig_ptr+1
+ sta ICBAH,x
+ rts
+
+
+; restore original contents of ICBAL/ICBAH from 'zpptr1'
+; input: X - IOCB index
+; output: A - destroyed
+restore_icba:
+ lda zpptr1
+ sta ICBAL,x
+ lda zpptr1+1
+ sta ICBAH,x
+ rts
+
+
+; put bounce buffer address into ICBAL/ICBAH
+; input: X - IOCB index
+; output: A - destroyed
+bncbuf_to_iocb:
+ lda #<bounce_buffer
+ sta ICBAL,x
+ lda #>bounce_buffer
+ sta ICBAH,x
+ rts
+
+
+; copy file name pointed to by 'zpptr1' to 'bounce_buffer'
+; input: Y - index into file name buffer and bounce_buffer
+; output: Y - points to first invalid byte after file name
+; A - destroyed
+copy_filename:
+ lda (zpptr1),y
+ sta bounce_buffer,y
+ beq copy_fn_done
+ iny
+ cmp #ATEOL
+ bne copy_filename
+ dey
+copy_fn_done:
+ rts
+
+
+; write IOCB buffer address into zpptr1
+; input: X - IOCB index
+; output: Y - 0 (for setup_zpptr1_y0, else unchanged)
+; A - destroyed
+setup_zpptr1_y0:
+ ldy #0
+setup_zpptr1:
+ lda ICBAL,x ; put buffer address into zp pointer
+ sta zpptr1
+ lda ICBAH,x
+ sta zpptr1+1
+ rts
+
+
+.if CHKBUF
+
+; get length of file name pointed to by 'zpptr1'
+; input: Y - index into file name
+; output: Y - length
+; A - destroyed
+get_fn_len:
+ lda (zpptr1),y
+ beq @done
+ iny
+ cmp #ATEOL
+ bne get_fn_len
+ dey
+@done:
+ rts
+
+
+chk_CIO_buf_fn2:
+ tya
+ pha
+ lda ICBLL,x
+ pha
+ lda ICBLH,x
+ pha
+ jsr setup_zpptr1_y0
+ jsr get_fn_len
+ iny ; include terminating zero
+ bne fn_cont
+
+chk_CIO_buf_fn:
+ tya
+ pha
+ lda ICBLL,x
+ pha
+ lda ICBLH,x
+ pha
+ jsr setup_zpptr1_y0
+fn_cont:jsr get_fn_len
+ iny ; include terminating zero
+ tya
+ sta ICBLL,x
+ lda #0
+ sta ICBLH,x
+ jsr chk_CIO_buf
+ pla
+ sta ICBLH,x
+ pla
+ sta ICBLL,x
+ pla
+ tay
+ rts
+
+
+; check if a CIO input/output buffer overlaps with ROM area (>= $C000)
+; input: X - IOCB index
+; ICBAL/ICBAH/ICBLL/ICBLH - buffer address and length
+; output: CF - 1/0 for overlap/no overlap
+; A - destroyed
+
+chk_CIO_buf:
+ lda ICBAH,x
+ cmp #$c0
+ bcc @cont
+@ret:
+.ifdef DEBUG
+ jsr CIO_buf_noti
+.endif
+ rts
+
+@cont: lda ICBAL,x
+ clc
+ adc ICBLL,x
+ lda ICBAH,x
+ adc ICBLH,x
+ bcs @ret ; ??? wraparound
+ cmp #$c0
+.ifdef DEBUG
+ jsr CIO_buf_noti
+.endif
+ rts
+
+.ifdef DEBUG
+; write to screen memory on 2nd line:
+; pos 0: # of accesses without buffering
+; pos 1: # of accesses with buffering
+CIO_buf_noti:
+ php
+ pha
+ tya
+ pha
+ bcc @nobuf
+
+ inc CIObnval_dobuf
+ jmp @cont
+
+@nobuf: inc CIObnval_nobuf
+
+@cont: ldy #40
+ lda CIObnval_nobuf
+ sta (SAVMSC),y
+ ldy #41
+ lda CIObnval_dobuf
+ sta (SAVMSC),y
+
+ pla
+ tay
+ pla
+ plp
+ rts
+
+CIObnval_dobuf:
+ .byte 0
+CIObnval_nobuf:
+ .byte 0
+.endif
+
+.endif ; .if CHKBUF
+
+;---------------------------------------------------------
+
+; SIO handler
+; We only handle SIO_STAT, SIO_READ, SIO_WRITE, and SIO_WRITEV.
+; These are the only functions used by the runtime library currently.
+; For other function we return NVALID status code.
+
+SIO_handler:
+ lda DCOMND ; get command
+ cmp #SIO_STAT
+ beq SIO_stat
+ cmp #SIO_READ
+ beq SIO_read
+ cmp #SIO_WRITE
+ beq SIO_write
+ cmp #SIO_WRITEV
+ beq SIO_write
+
+ ; unhandled command
+ lda #NVALID
+SIO_err:sta DSTATS
+ rts
+
+; SIO_STAT is always called with a low buffer (by the runtime)
+SIO_stat:
+ ; fall thru
+
+SIO_call:
+ lda PORTB
+ sta cur_SIOV_PORTB
+ enable_rom
+ jsr SIOV_org
+ php
+ pha
+ disable_rom_val cur_SIOV_PORTB
+ pla
+ plp
+ rts
+
+
+; SIO read handler
+; ----------------
+
+SIO_read:
+
+.if CHKBUF
+ jsr chk_SIO_buf
+ bcc SIO_call
+.endif
+
+; we only support transfers <= bounce buffer size
+ jsr cmp_sio_len_bnc_bufsz
+ bcs sio_read_len_ok
+
+ lda #DERROR ; don't know a better status code for this
+ bne SIO_err
+
+sio_read_len_ok:
+ lda DBUFLO
+ sta zpptr1 ; remember destination buffer address
+ lda DBUFHI
+ sta zpptr1+1
+
+ jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
+
+ jsr SIO_call ; do the operation
+ pha
+ lda DSTATS ; get status
+ bmi sio_read_ret ; error
+
+ ; copy data to user buffer
+sio_read_ok:
+ lda DBYTHI ; could be 1 for 256 bytes
+ beq srok1
+ ldy #0
+ beq srok2
+srok1: ldy DBYTLO
+srok2: dey
+sio_read_copy:
+ lda bounce_buffer,y
+ sta (zpptr1),y
+ dey
+ cpy #$ff
+ bne sio_read_copy
+
+sio_read_ret:
+ jsr orgbuf_to_dbuf
+
+ pla
+ rts ; success return
+
+
+; SIO write handler
+; -----------------
+
+SIO_write:
+
+.if CHKBUF
+ jsr chk_SIO_buf
+ bcc SIO_call
+.endif
+
+; we only support transfers <= bounce buffer size
+ jsr cmp_sio_len_bnc_bufsz
+ bcs sio_write_len_ok
+
+ lda #DERROR ; don't know a better status code for this
+ jmp SIO_err
+
+sio_write_len_ok:
+ lda DBUFLO
+ sta zpptr1 ; get source buffer address
+ lda DBUFHI
+ sta zpptr1+1
+
+ ; copy data from user buffer to bounce buffer
+ lda DBYTHI ; could be 1 for 256 bytes
+ beq swok1
+ ldy #0
+ beq swok2
+swok1: ldy DBYTLO
+swok2: dey
+sio_write_copy:
+ lda (zpptr1),y
+ sta bounce_buffer,y
+ dey
+ cpy #$ff
+ bne sio_write_copy
+
+ jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
+
+ jsr SIO_call ; do the operation
+ pha
+ jsr orgbuf_to_dbuf
+ pla
+ rts
+
+
+; check if SIO length is larger than bounce buffer size
+; input: orig_len - length
+; output: A - destroyed
+; CF - 0/1 for larger/not larger
+cmp_sio_len_bnc_bufsz:
+ sec
+ lda #<BUFSZ_SIO
+ sbc DBYTLO
+ lda #>BUFSZ_SIO
+ sbc DBYTHI
+ rts
+
+; put bounce buffer address into DBUFLO/DBUFHI
+; input: (--)
+; output: A - destroyed
+bncbuf_to_dbuf:
+ lda #<bounce_buffer
+ sta DBUFLO
+ lda #>bounce_buffer
+ sta DBUFHI
+ rts
+
+; put original buffer address into DBUFLO/DBUFHI
+; input: zpptr1 - original pointer
+; output: A - destroyed
+orgbuf_to_dbuf:
+ lda zpptr1
+ sta DBUFLO
+ lda zpptr1+1
+ sta DBUFHI
+ rts
+
+
+.if CHKBUF
+
+; check if a SIO input/output buffer overlaps with ROM area (>= $C000)
+; input: DBUFLO/DBUFHI/DBYTLO/DBYTHI - buffer address and length
+; output: CF - 1/0 for overlap/no overlap
+; A - destroyed
+
+chk_SIO_buf:
+ lda DBUFHI
+ cmp #$c0
+ bcc @cont
+@ret:
+.ifdef DEBUG
+ jsr SIO_buf_noti
+.endif
+ rts
+
+@cont: lda DBUFLO
+ clc
+ adc DBYTLO
+ lda DBUFHI
+ adc DBYTHI
+ bcs @ret ; ??? wraparound
+ cmp #$c0
+.ifdef DEBUG
+ jsr SIO_buf_noti
+.endif
+ rts
+
+.ifdef DEBUG
+; write to screen memory on 2nd line:
+; pos 38: # of accesses without buffering
+; pos 39: # of accesses with buffering
+SIO_buf_noti:
+ php
+ pha
+ tya
+ pha
+ bcc @nobuf
+
+ inc SIObnval_dobuf
+ jmp @cont
+
+@nobuf: inc SIObnval_nobuf
+
+@cont: ldy #78
+ lda SIObnval_nobuf
+ sta (SAVMSC),y
+ ldy #79
+ lda SIObnval_dobuf
+ sta (SAVMSC),y
+
+ pla
+ tay
+ pla
+ plp
+ rts
+
+SIObnval_dobuf:
+ .byte 0
+SIObnval_nobuf:
+ .byte 0
+.endif
+
+.endif ; .if CHKBUF
+
+;---------------------------------------------------------
+
+KEYBDV_handler:
+
+ lda #>(kret-1)
+ pha
+ lda #<(kret-1)
+ pha
+ lda PORTB
+ sta cur_KEYBDV_PORTB
+ enable_rom
+ lda KEYBDV+5
pha
lda KEYBDV+4
pha
- rts ; call keyboard handler
-kret: pha
- disable_rom
- pla
- rts
+ rts ; call keyboard handler
+kret: pha
+ disable_rom_val cur_KEYBDV_PORTB
+ pla
+ rts
+
+;---------------------------------------------------------
+
+SETVBV_handler:
+
+ pha
+ lda PORTB
+ sta cur_SETVBV_PORTB
+ enable_rom
+ pla
+ jsr SETVBV_org
+ php
+ pha
+ disable_rom_val cur_SETVBV_PORTB
+ pla
+ plp
+ rts
+
+;---------------------------------------------------------
+
+XMOVE_handler:
+
+ pha
+ lda PORTB
+ sta cur_XMOVE_PORTB
+ enable_rom
+ pla
+ jsr XMOVE_org
+ php
+ pha
+ disable_rom_val cur_XMOVE_PORTB
+ pla
+ plp
+ rts
+
+
+CIO_a: .res 1
+CIO_x: .res 1
+CIO_y: .res 1
+CIO_p: .res 1
+cur_CIOV_PORTB: .res 1
+cur_SIOV_PORTB: .res 1
+cur_KEYBDV_PORTB: .res 1
+cur_SETVBV_PORTB: .res 1
+cur_XMOVE_PORTB: .res 1
+orig_ptr: .res 2
+orig_len: .res 2
+req_len: .res 2
+retlen: .res 2
-.endif ; .if .defined(__ATARIXL__)
+.endif ; .ifdef __ATARIXL__