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
27 BUFSZ = 128 ; bounce buffer size
32 ; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer
36 ; disable all interrupts
38 stx NMIEN ; disable NMI
44 ; setup interrupt vectors
67 .segment "EXTZP" : zeropage
74 ; bounce buffers for CIO and SIO calls
75 bounce_buffer: .res BUFSZ_SIO
84 ; The interrupt handlers don't look at the current state of PORTB and
85 ; unconditionally disable the ROMs on exit.
86 ; Please note that this works, since if the ROMs are enabled we anyway
87 ; aren't being called here because the vectors are pointing to their
88 ; original ROM locations.
90 .macro int_wrap orgvec
100 ret: disable_rom_quick
139 ; set I bit to interrupted value
157 ; System request handlers
158 ; -----------------------
161 ; for filenames we assume they will fit into our bounce buffer
163 ; one filename, terminated by "invalid character", located at ICBAL/ICBAH
175 jsr CIO_call_a ; call CIO (maybe A isn't needed, then we could call CIO_call)
178 jsr restore_icba ; restore original ICBAL/ICBAH
181 rts ; back to application
184 ; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
200 ; We have buffer pointer and length entries in the IOCB, but their
201 ; usage depends on the function.
202 ; Some functions don't care about any of them (pointer and length),
203 ; and some only use the pointer (like e.g. OPEN), and some use both.
204 ; So we need function specific handlers to correctly deal with
205 ; buffers which are overlapping with the ROM area.
207 ; FIXME: Currently only the requests used by the runtime lib are handled.
211 ; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
217 lda ICCOM,x ; get function
219 beq CIO_filename ; filename as input parameter in buffer, length not used
221 bcc CIO_read ; input (GETREC or GETCHR)
223 bcc CIO_write_jmp ; output (PUTREC or PUTCHR)
224 beq CIO_call_a ; pass through, buffer not used
225 cmp #RENAME ; 2 filenames as input parameters in buffer, length not used
228 bcc CIO_filename ; filename as input parameter in buffer, length not used
229 beq CIO_invalid ; GETCWD not supported yet
230 bcs CIO_call_a ; other commands: assume no buffer
233 ; enable ROM, call CIO, disable ROM
247 disable_rom_val cur_CIOV_PORTB
267 beq CIO_call_a ; special I/O through A register in case buffer length is 0
274 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
275 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
277 lda ICBLH,x ; get high byte of length
278 bne big_read ; not zero -> data too large for our buffers
279 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
284 ; Data size fits into bounce buffer
288 jsr CIO_call_a ; call CIO
297 rts ; return with error
303 jsr copy_to_user ; copy data into user buffer
309 rts ; return with success
311 ; Data size does not fit into bounce buffer
315 sta retlen ; initialize return length
317 jsr iocblen_to_orig_len
318 jsr iocbptr_to_orig_ptr
320 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
323 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
324 bcs br_last ; no, last transfer, use remaining size
327 sta ICBLH,x ; set data length
334 sta ICBLH,x ; set data length
339 sta req_len ; remember length of this request
342 jsr CIO_call_a ; do the request
352 rts ; return with error
370 ; if the request read less bytes than requested, we're done
378 ; update user buffer pointer (zpptr1)
387 ; update remaining length
396 ; still something left to do (remaining length != 0)?
402 ; done, write original buffer pointer and total transfer length to IOCB and return to application
408 jsr orig_ptr_to_iocbptr
414 rts ; return with success
430 beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
437 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
438 ; Otherwise we can get away with a copy to the bounce buffer and the call.
440 lda ICBLH,x ; get high byte of length
441 bne big_write ; not zero -> data too large for our buffers
442 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
448 ; Data size fits into bounce buffer
460 rts ; return to application
463 ; Data size does not fit into bounce buffer
467 sta retlen ; initialize return length
469 jsr iocblen_to_orig_len
470 jsr iocbptr_to_orig_ptr
472 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
475 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
476 bcs bw_last ; no, last transfer, use remaining size
479 sta ICBLH,x ; set data length
486 sta ICBLH,x ; set data length
491 sta req_len ; remember length of this request
495 jsr CIO_call_a ; do the request
517 ; if the request wrote less bytes than requested, we're done
525 ; update user buffer pointer (zpptr1)
534 ; update remaining length
543 ; still something left to do (remaining length != 0)?
554 jsr orig_ptr_to_iocbptr
560 rts ; return with success
564 ; check if length is larger than bounce buffer size
565 ; input: orig_len - length
566 ; output: A - destroyed
567 ; CF - 0/1 for larger/not larger
568 cmp_orig_len_bnc_bufsz:
577 ; copy data from bounce buffer into user buffer
578 ; input: X - IOCB index
579 ; zpptr1 - pointer to user buffer
580 ; output: A - destroyed
583 ldy ICBLL,x ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
594 ; copy data from user buffer into bounce buffer
595 ; input: X - IOCB index
596 ; zpptr1 - pointer to user buffer
597 ; output: A - destroyed
600 ldy ICBLL,x ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
611 ; copy ICBLL/ICBLH to 'orig_len'
612 ; input: X - IOCB index
613 ; output: A - destroyed
622 ; copy ICBAL/ICBAH to 'orig_ptr'
623 ; input: X - IOCB index
624 ; output: A - destroyed
633 ; copy 'orig_ptr' to ICBAL/ICBAH
634 ; input: X - IOCB index
635 ; output: A - destroyed
644 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
645 ; input: X - IOCB index
646 ; output: A - destroyed
655 ; put bounce buffer address into ICBAL/ICBAH
656 ; input: X - IOCB index
657 ; output: A - destroyed
666 ; copy file name pointed to by 'zpptr1' to 'bounce_buffer'
667 ; input: Y - index into file name buffer and bounce_buffer
668 ; output: Y - points to first invalid byte after file name
682 ; write IOCB buffer address into zpptr1
683 ; input: X - IOCB index
684 ; output: Y - 0 (for setup_zpptr1_y0, else unchanged)
689 lda ICBAL,x ; put buffer address into zp pointer
698 ; get length of file name pointed to by 'zpptr1'
699 ; input: Y - index into file name
722 iny ; include terminating zero
733 fn_cont:jsr get_fn_len
734 iny ; include terminating zero
749 ; check if a CIO input/output buffer overlaps with ROM area (>= $C000)
750 ; input: X - IOCB index
751 ; ICBAL/ICBAH/ICBLL/ICBLH - buffer address and length
752 ; output: CF - 1/0 for overlap/no overlap
770 bcs @ret ; ??? wraparound
778 ; write to screen memory on 2nd line:
779 ; pos 0: # of accesses without buffering
780 ; pos 1: # of accesses with buffering
791 @nobuf: inc CIObnval_nobuf
814 ;---------------------------------------------------------
817 ; We only handle SIO_STAT, SIO_READ, SIO_WRITE, and SIO_WRITEV.
818 ; These are the only functions used by the runtime library currently.
819 ; For other function we return NVALID status code.
822 lda DCOMND ; get command
837 ; SIO_STAT is always called with a low buffer (by the runtime)
848 disable_rom_val cur_SIOV_PORTB
864 ; we only support transfers <= bounce buffer size
865 jsr cmp_sio_len_bnc_bufsz
868 lda #DERROR ; don't know a better status code for this
873 sta zpptr1 ; remember destination buffer address
877 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
879 jsr SIO_call ; do the operation
881 lda DSTATS ; get status
882 bmi sio_read_ret ; error
884 ; copy data to user buffer
886 lda DBYTHI ; could be 1 for 256 bytes
916 ; we only support transfers <= bounce buffer size
917 jsr cmp_sio_len_bnc_bufsz
920 lda #DERROR ; don't know a better status code for this
925 sta zpptr1 ; get source buffer address
929 ; copy data from user buffer to bounce buffer
930 lda DBYTHI ; could be 1 for 256 bytes
943 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
945 jsr SIO_call ; do the operation
952 ; check if SIO length is larger than bounce buffer size
953 ; input: orig_len - length
954 ; output: A - destroyed
955 ; CF - 0/1 for larger/not larger
956 cmp_sio_len_bnc_bufsz:
964 ; put bounce buffer address into DBUFLO/DBUFHI
966 ; output: A - destroyed
974 ; put original buffer address into DBUFLO/DBUFHI
975 ; input: zpptr1 - original pointer
976 ; output: A - destroyed
987 ; check if a SIO input/output buffer overlaps with ROM area (>= $C000)
988 ; input: DBUFLO/DBUFHI/DBYTLO/DBYTHI - buffer address and length
989 ; output: CF - 1/0 for overlap/no overlap
1007 bcs @ret ; ??? wraparound
1015 ; write to screen memory on 2nd line:
1016 ; pos 38: # of accesses without buffering
1017 ; pos 39: # of accesses with buffering
1028 @nobuf: inc SIObnval_nobuf
1051 ;---------------------------------------------------------
1060 sta cur_KEYBDV_PORTB
1066 rts ; call keyboard handler
1068 disable_rom_val cur_KEYBDV_PORTB
1072 ;---------------------------------------------------------
1078 sta cur_SETVBV_PORTB
1084 disable_rom_val cur_SETVBV_PORTB
1089 ;---------------------------------------------------------
1101 disable_rom_val cur_XMOVE_PORTB
1111 cur_CIOV_PORTB: .res 1
1112 cur_SIOV_PORTB: .res 1
1113 cur_KEYBDV_PORTB: .res 1
1114 cur_SETVBV_PORTB: .res 1
1115 cur_XMOVE_PORTB: .res 1
1121 .endif ; .ifdef __ATARIXL__