mirror of
https://github.com/cc65/cc65.git
synced 2024-12-25 02:29:52 +00:00
1122 lines
25 KiB
ArmAsm
1122 lines
25 KiB
ArmAsm
;
|
|
; 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 $fffe
|
|
lda #>my_IRQ_han
|
|
sta $ffff
|
|
|
|
lda #<my_RESET_han
|
|
sta $fffc
|
|
lda #>my_RESET_han
|
|
sta $fffd
|
|
|
|
lda #<my_NMI_han
|
|
sta $fffa
|
|
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 #<ret
|
|
pha
|
|
php
|
|
jmp (orgvec)
|
|
ret: disable_rom_quick
|
|
pla
|
|
rti
|
|
.endmacro
|
|
|
|
my_IRQ_han:
|
|
.ifdef DEBUG
|
|
php
|
|
pha
|
|
tya
|
|
pha
|
|
ldy #0
|
|
lda (SAVMSC),y
|
|
clc
|
|
adc #1
|
|
sta (SAVMSC),y
|
|
pla
|
|
tay
|
|
pla
|
|
plp
|
|
.endif
|
|
int_wrap $FFFE
|
|
|
|
my_NMI_han:
|
|
.ifdef DEBUG
|
|
php
|
|
pha
|
|
tya
|
|
pha
|
|
ldy #39
|
|
lda (SAVMSC),y
|
|
clc
|
|
adc #1
|
|
sta (SAVMSC),y
|
|
pla
|
|
tay
|
|
pla
|
|
plp
|
|
.endif
|
|
; set I bit to interrupted value
|
|
pha
|
|
txa
|
|
pha
|
|
tsx
|
|
lda $103,x
|
|
pha
|
|
plp
|
|
pla
|
|
tax
|
|
pla
|
|
int_wrap $FFFA
|
|
|
|
my_RESET_han:
|
|
enable_rom
|
|
jmp ($FFFC)
|
|
|
|
|
|
; System request handlers
|
|
; -----------------------
|
|
|
|
|
|
; for filenames we assume they will fit into our bounce buffer
|
|
|
|
; one filename, terminated by "invalid character", located at ICBAL/ICBAH
|
|
|
|
CIO_filename:
|
|
.if CHKBUF
|
|
jsr chk_CIO_buf_fn
|
|
bcc CIO_call_a
|
|
.endif
|
|
jsr setup_zpptr1_y0
|
|
jsr copy_filename
|
|
CIO_fn_cont:
|
|
jsr bncbuf_to_iocb
|
|
ldy CIO_y
|
|
jsr CIO_call_a ; call CIO (maybe A isn't needed, then we could call CIO_call)
|
|
php
|
|
pha
|
|
jsr restore_icba ; restore original ICBAL/ICBAH
|
|
pla
|
|
plp
|
|
rts ; back to application
|
|
|
|
|
|
; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
|
|
|
|
CIO_filename2:
|
|
.if CHKBUF
|
|
jsr chk_CIO_buf_fn2
|
|
bcc CIO_call_a
|
|
.endif
|
|
jsr setup_zpptr1_y0
|
|
jsr copy_filename
|
|
iny
|
|
jsr copy_filename
|
|
jmp CIO_fn_cont
|
|
|
|
|
|
|
|
; CIO handler
|
|
; We have buffer pointer and length entries in the IOCB, but their
|
|
; usage depends on the function.
|
|
; Some functions don't care about any of them (pointer and length),
|
|
; and some only use the pointer (like e.g. OPEN), and some use both.
|
|
; So we need function specific handlers to correctly deal with
|
|
; buffers which are overlapping with the ROM area.
|
|
;
|
|
; FIXME: Currently only the requests used by the runtime lib are handled.
|
|
|
|
CIO_handler:
|
|
|
|
; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
|
|
|
|
sta CIO_a
|
|
sty CIO_y
|
|
stx CIO_x
|
|
|
|
lda ICCOM,x ; get function
|
|
cmp #OPEN
|
|
beq CIO_filename ; filename as input parameter in buffer, length not used
|
|
cmp #PUTREC
|
|
bcc CIO_read ; input (GETREC or GETCHR)
|
|
cmp #CLOSE
|
|
bcc CIO_write_jmp ; output (PUTREC or PUTCHR)
|
|
beq CIO_call_a ; pass through, buffer not used
|
|
cmp #RENAME ; 2 filenames as input parameters in buffer, length not used
|
|
beq CIO_filename2
|
|
cmp #GETCWD
|
|
bcc CIO_filename ; filename as input parameter in buffer, length not used
|
|
beq CIO_invalid ; GETCWD not supported yet
|
|
bcs CIO_call_a ; other commands: assume no buffer
|
|
; not reached
|
|
|
|
; enable ROM, call CIO, disable ROM
|
|
|
|
CIO_call_a:
|
|
lda CIO_a
|
|
|
|
CIOV_call:
|
|
pha
|
|
lda PORTB
|
|
sta cur_CIOV_PORTB
|
|
enable_rom
|
|
pla
|
|
jsr CIOV_org
|
|
php
|
|
pha
|
|
disable_rom_val cur_CIOV_PORTB
|
|
pla
|
|
plp
|
|
rts
|
|
|
|
|
|
CIO_write_jmp:
|
|
jmp CIO_write
|
|
|
|
CIO_invalid:
|
|
lda CIO_a
|
|
ldy #DINVCM
|
|
rts
|
|
|
|
; READ handler
|
|
; ------------
|
|
|
|
CIO_read:
|
|
lda ICBLL,x
|
|
ora ICBLH,x
|
|
beq CIO_call_a ; special I/O through A register in case buffer length is 0
|
|
|
|
.if CHKBUF
|
|
jsr chk_CIO_buf
|
|
bcc CIO_call_a
|
|
.endif
|
|
|
|
; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
|
|
; Otherwise we can get away with one call and a copy to the final destination afterwards.
|
|
|
|
lda ICBLH,x ; get high byte of length
|
|
bne big_read ; not zero -> data too large for our buffers
|
|
; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
|
|
lda #<BUFSZ
|
|
cmp ICBLL,x
|
|
bcc big_read
|
|
|
|
; Data size fits into bounce buffer
|
|
|
|
jsr setup_zpptr1
|
|
jsr bncbuf_to_iocb
|
|
jsr CIO_call_a ; call CIO
|
|
php
|
|
bpl @no_err
|
|
cpy #EOFERR
|
|
beq @no_err
|
|
pha
|
|
jsr restore_icba
|
|
pla
|
|
plp
|
|
rts ; return with error
|
|
|
|
@no_err:
|
|
sta CIO_a
|
|
sty CIO_y
|
|
|
|
jsr copy_to_user ; copy data into user buffer
|
|
jsr restore_icba
|
|
|
|
lda CIO_a
|
|
ldy CIO_y
|
|
plp
|
|
rts ; return with success
|
|
|
|
; Data size does not fit into bounce buffer
|
|
|
|
big_read:
|
|
lda #0
|
|
sta retlen ; initialize return length
|
|
sta retlen+1
|
|
jsr iocblen_to_orig_len
|
|
jsr iocbptr_to_orig_ptr
|
|
jsr setup_zpptr1
|
|
jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
|
|
|
|
br_loop:
|
|
jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
|
|
bcs br_last ; no, last transfer, use remaining size
|
|
|
|
lda #>BUFSZ
|
|
sta ICBLH,x ; set data length
|
|
lda #<BUFSZ
|
|
sta ICBLL,x
|
|
bne br_cont
|
|
|
|
br_last:
|
|
lda orig_len+1
|
|
sta ICBLH,x ; set data length
|
|
lda orig_len
|
|
sta ICBLL,x
|
|
|
|
br_cont:
|
|
sta req_len ; remember length of this request
|
|
lda ICBLH,x
|
|
sta req_len+1
|
|
jsr CIO_call_a ; do the request
|
|
php
|
|
bpl br_no_err
|
|
cpy #EOFERR
|
|
beq br_no_err
|
|
|
|
pha
|
|
jsr restore_icba
|
|
pla
|
|
plp
|
|
rts ; return with error
|
|
|
|
br_no_err:
|
|
sta CIO_a
|
|
sty CIO_y
|
|
pla
|
|
sta CIO_p
|
|
jsr copy_to_user
|
|
|
|
; update retlen
|
|
clc
|
|
lda retlen
|
|
adc ICBLL,x
|
|
sta retlen
|
|
lda retlen+1
|
|
adc #0
|
|
sta retlen+1
|
|
|
|
; if the request read less bytes than requested, we're done
|
|
lda ICBLL,x
|
|
cmp req_len
|
|
bne br_done
|
|
lda ICBLH,x
|
|
cmp req_len+1
|
|
bne br_done
|
|
|
|
; update user buffer pointer (zpptr1)
|
|
clc
|
|
lda zpptr1
|
|
adc ICBLL,x
|
|
sta zpptr1
|
|
lda zpptr1+1
|
|
adc #0
|
|
sta zpptr1+1
|
|
|
|
; update remaining length
|
|
sec
|
|
lda orig_len
|
|
sbc ICBLL,x
|
|
sta orig_len
|
|
lda orig_len+1
|
|
sbc #0
|
|
sta orig_len+1
|
|
|
|
; still something left to do (remaining length != 0)?
|
|
lda orig_len
|
|
ora orig_len+1
|
|
beq br_done
|
|
jmp br_loop
|
|
|
|
; done, write original buffer pointer and total transfer length to IOCB and return to application
|
|
br_done:
|
|
lda retlen
|
|
sta ICBLL,x
|
|
lda retlen+1
|
|
sta ICBLH,x
|
|
jsr orig_ptr_to_iocbptr
|
|
lda CIO_p
|
|
pha
|
|
lda CIO_a
|
|
ldy CIO_y
|
|
plp
|
|
rts ; return with success
|
|
|
|
|
|
|
|
CIO_call_a_jmp:
|
|
jmp CIO_call_a
|
|
|
|
|
|
|
|
; WRITE handler
|
|
; -------------
|
|
|
|
|
|
CIO_write:
|
|
lda ICBLL,x
|
|
ora ICBLH,x
|
|
beq CIO_call_a_jmp ; special I/O through A register in case buffer length is 0
|
|
|
|
.if CHKBUF
|
|
jsr chk_CIO_buf
|
|
bcc CIO_call_a_jmp
|
|
.endif
|
|
|
|
; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
|
|
; Otherwise we can get away with a copy to the bounce buffer and the call.
|
|
|
|
lda ICBLH,x ; get high byte of length
|
|
bne big_write ; not zero -> data too large for our buffers
|
|
; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
|
|
lda #<BUFSZ
|
|
cmp ICBLL,x
|
|
bcc big_write
|
|
|
|
|
|
; Data size fits into bounce buffer
|
|
|
|
jsr setup_zpptr1
|
|
jsr bncbuf_to_iocb
|
|
jsr copy_from_user
|
|
ldy CIO_y
|
|
jsr CIO_call_a
|
|
php
|
|
pha
|
|
jsr restore_icba
|
|
pla
|
|
plp
|
|
rts ; return to application
|
|
|
|
|
|
; Data size does not fit into bounce buffer
|
|
|
|
big_write:
|
|
lda #0
|
|
sta retlen ; initialize return length
|
|
sta retlen+1
|
|
jsr iocblen_to_orig_len
|
|
jsr iocbptr_to_orig_ptr
|
|
jsr setup_zpptr1
|
|
jsr bncbuf_to_iocb ; let ICBAL/ICBAH point to bounce buffer
|
|
|
|
bw_loop:
|
|
jsr cmp_orig_len_bnc_bufsz ; is transfer length > bounce buffer size?
|
|
bcs bw_last ; no, last transfer, use remaining size
|
|
|
|
lda #>BUFSZ
|
|
sta ICBLH,x ; set data length
|
|
lda #<BUFSZ
|
|
sta ICBLL,x
|
|
bne bw_cont
|
|
|
|
bw_last:
|
|
lda orig_len+1
|
|
sta ICBLH,x ; set data length
|
|
lda orig_len
|
|
sta ICBLL,x
|
|
|
|
bw_cont:
|
|
sta req_len ; remember length of this request
|
|
lda ICBLH,x
|
|
sta req_len+1
|
|
jsr copy_from_user
|
|
jsr CIO_call_a ; do the request
|
|
php
|
|
bpl bw_no_err
|
|
|
|
plp
|
|
rts ; error return
|
|
|
|
bw_no_err:
|
|
sta CIO_a
|
|
sty CIO_y
|
|
pla
|
|
sta CIO_p
|
|
|
|
; update retlen
|
|
clc
|
|
lda retlen
|
|
adc ICBLL,x
|
|
sta retlen
|
|
lda retlen+1
|
|
adc #0
|
|
sta retlen+1
|
|
|
|
; if the request wrote less bytes than requested, we're done
|
|
lda ICBLL,x
|
|
cmp req_len
|
|
bne bw_done
|
|
lda ICBLH,x
|
|
cmp req_len+1
|
|
bne bw_done
|
|
|
|
; update user buffer pointer (zpptr1)
|
|
clc
|
|
lda zpptr1
|
|
adc ICBLL,x
|
|
sta zpptr1
|
|
lda zpptr1+1
|
|
adc #0
|
|
sta zpptr1+1
|
|
|
|
; update remaining length
|
|
sec
|
|
lda orig_len
|
|
sbc ICBLL,x
|
|
sta orig_len
|
|
lda orig_len+1
|
|
sbc #0
|
|
sta orig_len+1
|
|
|
|
; still something left to do (remaining length != 0)?
|
|
lda orig_len
|
|
ora orig_len+1
|
|
beq bw_done
|
|
jmp bw_loop
|
|
|
|
bw_done:
|
|
lda retlen
|
|
sta ICBLL,x
|
|
lda retlen+1
|
|
sta ICBLH,x
|
|
jsr orig_ptr_to_iocbptr
|
|
lda CIO_p
|
|
pha
|
|
lda CIO_a
|
|
ldy CIO_y
|
|
plp
|
|
rts ; return with success
|
|
|
|
|
|
|
|
; check if length is larger than bounce buffer size
|
|
; input: orig_len - length
|
|
; output: A - destroyed
|
|
; CF - 0/1 for larger/not larger
|
|
cmp_orig_len_bnc_bufsz:
|
|
sec
|
|
lda #<BUFSZ
|
|
sbc orig_len
|
|
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 ICBAL,x
|
|
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 DBYTLO
|
|
lda #>BUFSZ_SIO
|
|
sbc DBYTHI
|
|
rts
|
|
|
|
; put bounce buffer address into DBUFLO/DBUFHI
|
|
; input: (--)
|
|
; output: A - destroyed
|
|
bncbuf_to_dbuf:
|
|
lda #<bounce_buffer
|
|
sta DBUFLO
|
|
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__
|