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)
11 .if .defined(__ATARIXL__)
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
317 beq CIO_call_a ; special I/O through A register in case buffer length is 0
324 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
325 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
327 lda ICBLH,x ; get high byte of length
328 bne big_read ; not zero -> data too large for our buffers
329 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
334 ; Data size fits into bounce buffer
338 jsr CIO_call_a ; call CIO
347 rts ; return with error
353 jsr copy_to_user ; copy data into user buffer
359 rts ; return with success
361 ; Data size does not fit into bounce buffer
365 sta retlen ; initialize return length
367 jsr iocblen_to_orig_len
368 jsr iocbptr_to_orig_ptr
370 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
373 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
374 bcs br_last ; no, last transfer, use remaining size
377 sta ICBLH,x ; set data length
384 sta ICBLH,x ; set data length
389 sta req_len ; remember length of this request
392 jsr CIO_call_a ; do the request
402 rts ; return with error
420 ; if the request read less bytes than requested, we're done
428 ; update user buffer pointer (zpptr1)
437 ; update remaining length
446 ; still something left to do (remaining length != 0)?
452 ; done, write original buffer pointer and total transfer length to IOCB and return to application
458 jsr orig_ptr_to_iocbptr
464 rts ; return with success
480 beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
487 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
488 ; Otherwise we can get away with a copy to the bounce buffer and the call.
490 lda ICBLH,x ; get high byte of length
491 bne big_write ; not zero -> data too large for our buffers
492 ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
498 ; Data size fits into bounce buffer
510 rts ; return to application
513 ; Data size does not fit into bounce buffer
517 sta retlen ; initialize return length
519 jsr iocblen_to_orig_len
520 jsr iocbptr_to_orig_ptr
522 jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
525 jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
526 bcs bw_last ; no, last transfer, use remaining size
529 sta ICBLH,x ; set data length
536 sta ICBLH,x ; set data length
541 sta req_len ; remember length of this request
545 jsr CIO_call_a ; do the request
567 ; if the request wrote less bytes than requested, we're done
575 ; update user buffer pointer (zpptr1)
584 ; update remaining length
593 ; still something left to do (remaining length != 0)?
604 jsr orig_ptr_to_iocbptr
610 rts ; return with success
614 ; check if length is larger than bounce buffer size
615 ; input: orig_len - length
616 ; output: A - destroyed
617 ; CF - 0/1 for larger/not larger
618 cmp_orig_len_bnc_bufsz:
627 ; copy data from bounce buffer into user buffer
628 ; input: X - IOCB index
629 ; zpptr1 - pointer to user buffer
630 ; output: A - destroyed
633 ldy ICBLL,x ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
644 ; copy data from user buffer into bounce buffer
645 ; input: X - IOCB index
646 ; zpptr1 - pointer to user buffer
647 ; output: A - destroyed
650 ldy ICBLL,x ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
661 ; copy ICBLL/ICBLH to 'orig_len'
662 ; input: X - IOCB index
663 ; output: A - destroyed
672 ; copy ICBAL/ICBAH to 'orig_ptr'
673 ; input: X - IOCB index
674 ; output: A - destroyed
683 ; copy 'orig_ptr' to ICBAL/ICBAH
684 ; input: X - IOCB index
685 ; output: A - destroyed
694 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
695 ; input: X - IOCB index
696 ; output: A - destroyed
705 ; put bounce buffer address into ICBAL/ICBAH
706 ; input: X - IOCB index
707 ; output: A - destroyed
716 ; copy file name pointed to by 'zpptr1' to 'bounce_buffer'
717 ; input: Y - index into file name buffer and bounce_buffer
718 ; output: Y - points to first invalid byte after file name
732 ; write IOCB buffer address into zpptr1
733 ; input: X - IOCB index
734 ; output: Y - 0 (for setup_zpptr1_y0, else unchanged)
739 lda ICBAL,x ; put buffer address into zp pointer
748 ; get length of file name pointed to by 'zpptr1'
749 ; input: Y - index into file name
772 iny ; include terminating zero
783 fn_cont:jsr get_fn_len
784 iny ; include terminating zero
799 ; check if a CIO input/output buffer overlaps with ROM area (>= $C000)
800 ; input: X - IOCB index
801 ; ICBAL/ICBAH/ICBLL/ICBLH - buffer address and length
802 ; output: CF - 1/0 for overlap/no overlap
820 bcs @ret ; ??? wraparound
828 ; write to screen memory on 2nd line:
829 ; pos 0: # of accesses without buffering
830 ; pos 1: # of accesses with buffering
841 @nobuf: inc CIObnval_nobuf
864 ;---------------------------------------------------------
867 ; We only handle SIO_STAT, SIO_READ, SIO_WRITE, and SIO_WRITEV.
868 ; These are the only functions used by the runtime library currently.
869 ; For other function we return NVALID status code.
872 lda DCOMND ; get command
887 ; SIO_STAT is always called with a low buffer (by the runtime)
898 disable_rom_val cur_SIOV_PORTB
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 ; remember destination buffer address
929 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
931 jsr SIO_call ; do the operation
933 lda DSTATS ; get status
934 bmi sio_read_ret ; error
936 ; copy data to user buffer
938 lda DBYTHI ; could be 1 for 256 bytes
968 ; we only support transfers <= bounce buffer size
969 jsr cmp_sio_len_bnc_bufsz
972 lda #DERROR ; don't know a better status code for this
977 sta zpptr1 ; get source buffer address
981 ; copy data from user buffer to bounce buffer
982 lda DBYTHI ; could be 1 for 256 bytes
995 jsr bncbuf_to_dbuf ; put bounce buffer address to DBUFLO/DBUFHI
997 jsr SIO_call ; do the operation
1004 ; check if SIO length is larger than bounce buffer size
1005 ; input: orig_len - length
1006 ; output: A - destroyed
1007 ; CF - 0/1 for larger/not larger
1008 cmp_sio_len_bnc_bufsz:
1016 ; put bounce buffer address into DBUFLO/DBUFHI
1018 ; output: A - destroyed
1026 ; put original buffer address into DBUFLO/DBUFHI
1027 ; input: zpptr1 - original pointer
1028 ; output: A - destroyed
1039 ; check if a SIO input/output buffer overlaps with ROM area (>= $C000)
1040 ; input: DBUFLO/DBUFHI/DBYTLO/DBYTHI - buffer address and length
1041 ; output: CF - 1/0 for overlap/no overlap
1059 bcs @ret ; ??? wraparound
1067 ; write to screen memory on 2nd line:
1068 ; pos 38: # of accesses without buffering
1069 ; pos 39: # of accesses with buffering
1080 @nobuf: inc SIObnval_nobuf
1103 ;---------------------------------------------------------
1112 sta cur_KEYBDV_PORTB
1118 rts ; call keyboard handler
1120 disable_rom_val cur_KEYBDV_PORTB
1121 ; lda cur_KEYBDV_PORTB
1126 ;---------------------------------------------------------
1132 sta cur_SETVBV_PORTB
1138 disable_rom_val cur_SETVBV_PORTB
1139 ; lda cur_SETVBV_PORTB
1149 cur_CIOV_PORTB: .res 1
1150 cur_SIOV_PORTB: .res 1
1151 cur_KEYBDV_PORTB: .res 1
1152 cur_SETVBV_PORTB: .res 1
1158 .endif ; .if .defined(__ATARIXL__)