2 ; Atari XL shadow RAM handlers
4 ; Christian Groessler, chris@groessler.org, 2013
9 CHKBUF = 1 ; check if bounce buffering is needed (bounce buffering is always done if set to 0)
15 .include "save_area.inc"
16 .include "zeropage.inc"
17 .import __CHARGEN_START__
20 .export KEYBDV_handler
23 .export SETVBV_handler
25 BUFSZ = 128 ; bounce buffer size
39 lda #>__CHARGEN_START__
43 .macro disable_rom_quick
47 lda #>__CHARGEN_START__
51 .macro disable_rom_val val
55 lda #>__CHARGEN_START__
69 .macro enable_rom_quick
80 ; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer
84 ; disable all interrupts
86 stx NMIEN ; disable NMI
92 ; setup interrupt vectors
115 .segment "EXTZP" : zeropage
122 ; bounce buffers for CIO and SIO calls
123 bounce_buffer: .res BUFSZ_SIO
132 ; The interrupt handlers don't look at the current state of PORTB and
133 ; unconditionally disable the ROMs on exit.
134 ; Please note that this works, since if the ROMs are enabled we anyway
135 ; aren't being called here because the vectors are pointing to their
136 ; original ROM locations.
138 .macro int_wrap orgvec
148 ret: disable_rom_quick
187 ; set I bit to interrupted value
205 ; System request handlers
206 ; -----------------------
209 ; for filenames we assume they will fit into our bounce buffer
211 ; one filename, terminated by "invalid character", located at ICBAL/ICBAH
223 jsr CIO_call_a ; call CIO (maybe A isn't needed, then we could call CIO_call)
226 jsr restore_icba ; restore original ICBAL/ICBAH
229 rts ; back to application
232 ; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
248 ; We have buffer pointer and length entries in the IOCB, but their
249 ; usage depends on the function.
250 ; Some functions don't care about any of them (pointer and length),
251 ; and some only use the pointer (like e.g. OPEN), and some use both.
252 ; So we need function specific handlers to correctly deal with
253 ; buffers which are overlapping with the ROM area.
255 ; FIXME: Currently only the requests used by the runtime lib are handled.
259 ; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
265 lda ICCOM,x ; get function
267 beq CIO_filename ; filename as input parameter in buffer, length not used
269 bcc CIO_read ; input (GETREC or GETCHR)
271 bcc CIO_write_jmp ; output (PUTREC or PUTCHR)
272 beq CIO_call_a ; pass through, buffer not used
273 cmp #RENAME ; 2 filenames as input parameters in buffer, length not used
276 bcc CIO_filename ; filename as input parameter in buffer, length not used
277 beq CIO_invalid ; GETCWD not supported yet
278 bcs CIO_call_a ; other commands: assume no buffer
281 ; enable ROM, call CIO, disable ROM
295 disable_rom_val cur_CIOV_PORTB
315 beq CIO_call_a ; special I/O through A register in case buffer length is 0
322 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
323 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
325 lda ICBLH,x ; get high byte of length
326 bne big_read ; not zero -> data too large for our buffers
327 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
332 ; Data size fits into bounce buffer
336 jsr CIO_call_a ; call CIO
345 rts ; return with error
351 jsr copy_to_user ; copy data into user buffer
357 rts ; return with success
359 ; Data size does not fit into bounce buffer
363 sta retlen ; initialize return length
365 jsr iocblen_to_orig_len
366 jsr iocbptr_to_orig_ptr
368 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
371 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
372 bcs br_last ; no, last transfer, use remaining size
375 sta ICBLH,x ; set data length
382 sta ICBLH,x ; set data length
387 sta req_len ; remember length of this request
390 jsr CIO_call_a ; do the request
400 rts ; return with error
418 ; if the request read less bytes than requested, we're done
426 ; update user buffer pointer (zpptr1)
435 ; update remaining length
444 ; still something left to do (remaining length != 0)?
450 ; done, write original buffer pointer and total transfer length to IOCB and return to application
456 jsr orig_ptr_to_iocbptr
462 rts ; return with success
478 beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
485 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
486 ; Otherwise we can get away with a copy to the bounce buffer and the call.
488 lda ICBLH,x ; get high byte of length
489 bne big_write ; not zero -> data too large for our buffers
490 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
496 ; Data size fits into bounce buffer
508 rts ; return to application
511 ; Data size does not fit into bounce buffer
515 sta retlen ; initialize return length
517 jsr iocblen_to_orig_len
518 jsr iocbptr_to_orig_ptr
520 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
523 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
524 bcs bw_last ; no, last transfer, use remaining size
527 sta ICBLH,x ; set data length
534 sta ICBLH,x ; set data length
539 sta req_len ; remember length of this request
543 jsr CIO_call_a ; do the request
565 ; if the request wrote less bytes than requested, we're done
573 ; update user buffer pointer (zpptr1)
582 ; update remaining length
591 ; still something left to do (remaining length != 0)?
602 jsr orig_ptr_to_iocbptr
608 rts ; return with success
612 ; check if length is larger than bounce buffer size
613 ; input: orig_len - length
614 ; output: A - destroyed
615 ; CF - 0/1 for larger/not larger
616 cmp_orig_len_bnc_bufsz:
625 ; copy data from bounce buffer into user buffer
626 ; input: X - IOCB index
627 ; zpptr1 - pointer to user buffer
628 ; output: A - destroyed
631 ldy ICBLL,x ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
642 ; copy data from user buffer into bounce buffer
643 ; input: X - IOCB index
644 ; zpptr1 - pointer to user buffer
645 ; output: A - destroyed
648 ldy ICBLL,x ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
659 ; copy ICBLL/ICBLH to 'orig_len'
660 ; input: X - IOCB index
661 ; output: A - destroyed
670 ; copy ICBAL/ICBAH to 'orig_ptr'
671 ; input: X - IOCB index
672 ; output: A - destroyed
681 ; copy 'orig_ptr' to ICBAL/ICBAH
682 ; input: X - IOCB index
683 ; output: A - destroyed
692 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
693 ; input: X - IOCB index
694 ; output: A - destroyed
703 ; put bounce buffer address into ICBAL/ICBAH
704 ; input: X - IOCB index
705 ; output: A - destroyed
714 ; copy file name pointed to by 'zpptr1' to 'bounce_buffer'
715 ; input: Y - index into file name buffer and bounce_buffer
716 ; output: Y - points to first invalid byte after file name
730 ; write IOCB buffer address into zpptr1
731 ; input: X - IOCB index
732 ; output: Y - 0 (for setup_zpptr1_y0, else unchanged)
737 lda ICBAL,x ; put buffer address into zp pointer
746 ; get length of file name pointed to by 'zpptr1'
747 ; input: Y - index into file name
770 iny ; include terminating zero
781 fn_cont:jsr get_fn_len
782 iny ; include terminating zero
797 ; check if a CIO input/output buffer overlaps with ROM area (>= $C000)
798 ; input: X - IOCB index
799 ; ICBAL/ICBAH/ICBLL/ICBLH - buffer address and length
800 ; output: CF - 1/0 for overlap/no overlap
818 bcs @ret ; ??? wraparound
826 ; write to screen memory on 2nd line:
827 ; pos 0: # of accesses without buffering
828 ; pos 1: # of accesses with buffering
839 @nobuf: inc CIObnval_nobuf
862 ;---------------------------------------------------------
865 ; We only handle SIO_STAT, SIO_READ, SIO_WRITE, and SIO_WRITEV.
866 ; These are the only functions used by the runtime library currently.
867 ; For other function we return NVALID status code.
870 lda DCOMND ; get command
885 ; SIO_STAT is always called with a low buffer (by the runtime)
896 disable_rom_val cur_SIOV_PORTB
912 ; we only support transfers <= bounce buffer size
913 jsr cmp_sio_len_bnc_bufsz
916 lda #DERROR ; don't know a better status code for this
921 sta zpptr1 ; remember destination buffer address
925 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
927 jsr SIO_call ; do the operation
929 lda DSTATS ; get status
930 bmi sio_read_ret ; error
932 ; copy data to user buffer
934 lda DBYTHI ; could be 1 for 256 bytes
964 ; we only support transfers <= bounce buffer size
965 jsr cmp_sio_len_bnc_bufsz
968 lda #DERROR ; don't know a better status code for this
973 sta zpptr1 ; get source buffer address
977 ; copy data from user buffer to bounce buffer
978 lda DBYTHI ; could be 1 for 256 bytes
991 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
993 jsr SIO_call ; do the operation
1000 ; check if SIO length is larger than bounce buffer size
1001 ; input: orig_len - length
1002 ; output: A - destroyed
1003 ; CF - 0/1 for larger/not larger
1004 cmp_sio_len_bnc_bufsz:
1012 ; put bounce buffer address into DBUFLO/DBUFHI
1014 ; output: A - destroyed
1022 ; put original buffer address into DBUFLO/DBUFHI
1023 ; input: zpptr1 - original pointer
1024 ; output: A - destroyed
1035 ; check if a SIO input/output buffer overlaps with ROM area (>= $C000)
1036 ; input: DBUFLO/DBUFHI/DBYTLO/DBYTHI - buffer address and length
1037 ; output: CF - 1/0 for overlap/no overlap
1055 bcs @ret ; ??? wraparound
1063 ; write to screen memory on 2nd line:
1064 ; pos 38: # of accesses without buffering
1065 ; pos 39: # of accesses with buffering
1076 @nobuf: inc SIObnval_nobuf
1099 ;---------------------------------------------------------
1108 sta cur_KEYBDV_PORTB
1114 rts ; call keyboard handler
1116 disable_rom_val cur_KEYBDV_PORTB
1120 ;---------------------------------------------------------
1126 sta cur_SETVBV_PORTB
1132 disable_rom_val cur_SETVBV_PORTB
1141 cur_CIOV_PORTB: .res 1
1142 cur_SIOV_PORTB: .res 1
1143 cur_KEYBDV_PORTB: .res 1
1144 cur_SETVBV_PORTB: .res 1
1150 .endif ; .ifdef __ATARIXL__