2 ; Atari XL shadow RAM handlers
4 ; Christian Groessler, chris@groessler.org, 2013
8 CHKBUF = 1 ; check if bounce buffering is needed (bounce buffering is always done if set to 0)
14 .include "save_area.inc"
15 .include "zeropage.inc"
16 .include "romswitch.inc"
18 .import __CHARGEN_START__
21 .export KEYBDV_handler
24 .export SETVBV_handler
26 BUFSZ = 128 ; bounce buffer size
31 ; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer
35 ; disable all interrupts
37 stx NMIEN ; disable NMI
43 ; setup interrupt vectors
66 .segment "EXTZP" : zeropage
73 ; bounce buffers for CIO and SIO calls
74 bounce_buffer: .res BUFSZ_SIO
83 ; The interrupt handlers don't look at the current state of PORTB and
84 ; unconditionally disable the ROMs on exit.
85 ; Please note that this works, since if the ROMs are enabled we anyway
86 ; aren't being called here because the vectors are pointing to their
87 ; original ROM locations.
89 .macro int_wrap orgvec
99 ret: disable_rom_quick
138 ; set I bit to interrupted value
156 ; System request handlers
157 ; -----------------------
160 ; for filenames we assume they will fit into our bounce buffer
162 ; one filename, terminated by "invalid character", located at ICBAL/ICBAH
174 jsr CIO_call_a ; call CIO (maybe A isn't needed, then we could call CIO_call)
177 jsr restore_icba ; restore original ICBAL/ICBAH
180 rts ; back to application
183 ; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
199 ; We have buffer pointer and length entries in the IOCB, but their
200 ; usage depends on the function.
201 ; Some functions don't care about any of them (pointer and length),
202 ; and some only use the pointer (like e.g. OPEN), and some use both.
203 ; So we need function specific handlers to correctly deal with
204 ; buffers which are overlapping with the ROM area.
206 ; FIXME: Currently only the requests used by the runtime lib are handled.
210 ; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
216 lda ICCOM,x ; get function
218 beq CIO_filename ; filename as input parameter in buffer, length not used
220 bcc CIO_read ; input (GETREC or GETCHR)
222 bcc CIO_write_jmp ; output (PUTREC or PUTCHR)
223 beq CIO_call_a ; pass through, buffer not used
224 cmp #RENAME ; 2 filenames as input parameters in buffer, length not used
227 bcc CIO_filename ; filename as input parameter in buffer, length not used
228 beq CIO_invalid ; GETCWD not supported yet
229 bcs CIO_call_a ; other commands: assume no buffer
232 ; enable ROM, call CIO, disable ROM
246 disable_rom_val cur_CIOV_PORTB
266 beq CIO_call_a ; special I/O through A register in case buffer length is 0
273 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
274 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
276 lda ICBLH,x ; get high byte of length
277 bne big_read ; not zero -> data too large for our buffers
278 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
283 ; Data size fits into bounce buffer
287 jsr CIO_call_a ; call CIO
296 rts ; return with error
302 jsr copy_to_user ; copy data into user buffer
308 rts ; return with success
310 ; Data size does not fit into bounce buffer
314 sta retlen ; initialize return length
316 jsr iocblen_to_orig_len
317 jsr iocbptr_to_orig_ptr
319 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
322 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
323 bcs br_last ; no, last transfer, use remaining size
326 sta ICBLH,x ; set data length
333 sta ICBLH,x ; set data length
338 sta req_len ; remember length of this request
341 jsr CIO_call_a ; do the request
351 rts ; return with error
369 ; if the request read less bytes than requested, we're done
377 ; update user buffer pointer (zpptr1)
386 ; update remaining length
395 ; still something left to do (remaining length != 0)?
401 ; done, write original buffer pointer and total transfer length to IOCB and return to application
407 jsr orig_ptr_to_iocbptr
413 rts ; return with success
429 beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
436 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
437 ; Otherwise we can get away with a copy to the bounce buffer and the call.
439 lda ICBLH,x ; get high byte of length
440 bne big_write ; not zero -> data too large for our buffers
441 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
447 ; Data size fits into bounce buffer
459 rts ; return to application
462 ; Data size does not fit into bounce buffer
466 sta retlen ; initialize return length
468 jsr iocblen_to_orig_len
469 jsr iocbptr_to_orig_ptr
471 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
474 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
475 bcs bw_last ; no, last transfer, use remaining size
478 sta ICBLH,x ; set data length
485 sta ICBLH,x ; set data length
490 sta req_len ; remember length of this request
494 jsr CIO_call_a ; do the request
516 ; if the request wrote less bytes than requested, we're done
524 ; update user buffer pointer (zpptr1)
533 ; update remaining length
542 ; still something left to do (remaining length != 0)?
553 jsr orig_ptr_to_iocbptr
559 rts ; return with success
563 ; check if length is larger than bounce buffer size
564 ; input: orig_len - length
565 ; output: A - destroyed
566 ; CF - 0/1 for larger/not larger
567 cmp_orig_len_bnc_bufsz:
576 ; copy data from bounce buffer into user buffer
577 ; input: X - IOCB index
578 ; zpptr1 - pointer to user buffer
579 ; output: A - destroyed
582 ldy ICBLL,x ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
593 ; copy data from user buffer into bounce buffer
594 ; input: X - IOCB index
595 ; zpptr1 - pointer to user buffer
596 ; output: A - destroyed
599 ldy ICBLL,x ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
610 ; copy ICBLL/ICBLH to 'orig_len'
611 ; input: X - IOCB index
612 ; output: A - destroyed
621 ; copy ICBAL/ICBAH to 'orig_ptr'
622 ; input: X - IOCB index
623 ; output: A - destroyed
632 ; copy 'orig_ptr' to ICBAL/ICBAH
633 ; input: X - IOCB index
634 ; output: A - destroyed
643 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
644 ; input: X - IOCB index
645 ; output: A - destroyed
654 ; put bounce buffer address into ICBAL/ICBAH
655 ; input: X - IOCB index
656 ; output: A - destroyed
665 ; copy file name pointed to by 'zpptr1' to 'bounce_buffer'
666 ; input: Y - index into file name buffer and bounce_buffer
667 ; output: Y - points to first invalid byte after file name
681 ; write IOCB buffer address into zpptr1
682 ; input: X - IOCB index
683 ; output: Y - 0 (for setup_zpptr1_y0, else unchanged)
688 lda ICBAL,x ; put buffer address into zp pointer
697 ; get length of file name pointed to by 'zpptr1'
698 ; input: Y - index into file name
721 iny ; include terminating zero
732 fn_cont:jsr get_fn_len
733 iny ; include terminating zero
748 ; check if a CIO input/output buffer overlaps with ROM area (>= $C000)
749 ; input: X - IOCB index
750 ; ICBAL/ICBAH/ICBLL/ICBLH - buffer address and length
751 ; output: CF - 1/0 for overlap/no overlap
769 bcs @ret ; ??? wraparound
777 ; write to screen memory on 2nd line:
778 ; pos 0: # of accesses without buffering
779 ; pos 1: # of accesses with buffering
790 @nobuf: inc CIObnval_nobuf
813 ;---------------------------------------------------------
816 ; We only handle SIO_STAT, SIO_READ, SIO_WRITE, and SIO_WRITEV.
817 ; These are the only functions used by the runtime library currently.
818 ; For other function we return NVALID status code.
821 lda DCOMND ; get command
836 ; SIO_STAT is always called with a low buffer (by the runtime)
847 disable_rom_val cur_SIOV_PORTB
863 ; we only support transfers <= bounce buffer size
864 jsr cmp_sio_len_bnc_bufsz
867 lda #DERROR ; don't know a better status code for this
872 sta zpptr1 ; remember destination buffer address
876 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
878 jsr SIO_call ; do the operation
880 lda DSTATS ; get status
881 bmi sio_read_ret ; error
883 ; copy data to user buffer
885 lda DBYTHI ; could be 1 for 256 bytes
915 ; we only support transfers <= bounce buffer size
916 jsr cmp_sio_len_bnc_bufsz
919 lda #DERROR ; don't know a better status code for this
924 sta zpptr1 ; get source buffer address
928 ; copy data from user buffer to bounce buffer
929 lda DBYTHI ; could be 1 for 256 bytes
942 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
944 jsr SIO_call ; do the operation
951 ; check if SIO length is larger than bounce buffer size
952 ; input: orig_len - length
953 ; output: A - destroyed
954 ; CF - 0/1 for larger/not larger
955 cmp_sio_len_bnc_bufsz:
963 ; put bounce buffer address into DBUFLO/DBUFHI
965 ; output: A - destroyed
973 ; put original buffer address into DBUFLO/DBUFHI
974 ; input: zpptr1 - original pointer
975 ; output: A - destroyed
986 ; check if a SIO input/output buffer overlaps with ROM area (>= $C000)
987 ; input: DBUFLO/DBUFHI/DBYTLO/DBYTHI - buffer address and length
988 ; output: CF - 1/0 for overlap/no overlap
1006 bcs @ret ; ??? wraparound
1014 ; write to screen memory on 2nd line:
1015 ; pos 38: # of accesses without buffering
1016 ; pos 39: # of accesses with buffering
1027 @nobuf: inc SIObnval_nobuf
1050 ;---------------------------------------------------------
1059 sta cur_KEYBDV_PORTB
1065 rts ; call keyboard handler
1067 disable_rom_val cur_KEYBDV_PORTB
1071 ;---------------------------------------------------------
1077 sta cur_SETVBV_PORTB
1083 disable_rom_val cur_SETVBV_PORTB
1092 cur_CIOV_PORTB: .res 1
1093 cur_SIOV_PORTB: .res 1
1094 cur_KEYBDV_PORTB: .res 1
1095 cur_SETVBV_PORTB: .res 1
1101 .endif ; .ifdef __ATARIXL__