; ; Atari XL shadow RAM handlers ; ; Christian Groessler, chris@groessler.org, 2013 ; DEBUG = 1 .if .defined(__ATARIXL__) .include "atari.inc" .include "save_area.inc" .include "zeropage.inc" .import __CHARGEN_START__ .export sram_init .export KEYBDV_wrapper BUFSZ = 256 ; bounce buffer size .macro disable_rom lda PORTB and #$fe sta PORTB lda #>__CHARGEN_START__ sta CHBAS sta CHBASE .endmacro .macro enable_rom lda PORTB ora #1 sta PORTB lda #$E0 sta CHBAS sta CHBASE .endmacro .segment "INIT" ; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer sram_init: ; disable all interrupts sei ldx #0 stx NMIEN ; disable NMI ; 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 ; setup pointers to CIOV and SIOV wrappers lda #$4C ; JMP opcode sta CIOV lda #my_CIOV sta CIOV+2 lda #$4C ; JMP opcode sta SIOV lda #my_SIOV sta SIOV+2 ; enable interrupts lda #$40 sta NMIEN cli rts .segment "EXTZP" : zeropage zpptr1: .res 2 .segment "LOWBUFS" ; bounce buffers for CIO and SIO calls bounce_buffer: .res BUFSZ .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 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 ;--------------------------------------------------------- ; 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. my_SIOV: 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 lda cur_SIOV_PORTB sta PORTB pla plp rts ; SIO read handler ; ---------------- SIO_read: ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area ; 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: ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area ; 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 bne 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 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 ;--------------------------------------------------------- KEYBDV_wrapper: 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 lda cur_KEYBDV_PORTB sta PORTB pla 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 orig_ptr: .res 2 orig_len: .res 2 req_len: .res 2 retlen: .res 2 .endif ; .if .defined(__ATARIXL__)