; ; Atari XL shadow RAM handlers ; ; Christian Groessler, chris@groessler.org, 2013 ; ;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" .include "zeropage.inc" .include "romswitch.inc" .import __CHARGEN_START__ .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 ldx #0 stx NMIEN ; disable NMI sei ; disable ROMs disable_rom ; setup interrupt vectors lda #my_IRQ_han sta $ffff lda #my_RESET_han sta $fffd lda #my_NMI_han sta $fffb ; enable interrupts cli lda #$40 sta NMIEN rts .segment "EXTZP" : zeropage zpptr1: .res 2 .segment "LOWBSS" ; bounce buffers for CIO and SIO calls bounce_buffer: .res BUFSZ_SIO .segment "LOWCODE" ; 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 # data too large for our buffers ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES lda # bounce buffer size? bcs br_last ; no, last transfer, use remaining size lda #>BUFSZ sta ICBLH,x ; set data length lda # data too large for our buffers ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES lda # bounce buffer size? bcs bw_last ; no, last transfer, use remaining size lda #>BUFSZ sta ICBLH,x ; set data length 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 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 DBYTHI rts ; put bounce buffer address into DBUFLO/DBUFHI ; input: (--) ; output: A - destroyed bncbuf_to_dbuf: 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_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 ; .ifdef __ATARIXL__