]> git.sur5r.net Git - cc65/blob - libsrc/atari/shadow_ram_handlers.s
Merge remote-tracking branch 'upstream/master'
[cc65] / libsrc / atari / shadow_ram_handlers.s
1 ;
2 ; Atari XL shadow RAM handlers
3 ;
4 ; Christian Groessler, chris@groessler.org, 2013
5 ;
6
7 DEBUG   =       1
8
9 .if .defined(__ATARIXL__)
10
11         .include        "atari.inc"
12         .include        "save_area.inc"
13         .include        "zeropage.inc"
14         .import         __CHARGEN_START__
15
16         .export         sram_init
17         .export         KEYBDV_wrapper
18
19 BUFSZ           =       128
20 BUFSZ_CIO       =       BUFSZ
21 BUFSZ_SIO       =       BUFSZ
22
23 .macro  disable_rom
24         lda     PORTB
25         and     #$fe
26         sta     PORTB
27         lda     #>__CHARGEN_START__
28         sta     CHBAS
29         sta     CHBASE
30 .endmacro
31 .macro  enable_rom
32         lda     PORTB
33         ora     #1
34         sta     PORTB
35         lda     #$E0
36         sta     CHBAS
37         sta     CHBASE
38 .endmacro
39
40 .segment "INIT"
41
42 ;enable_count:  .res    1
43
44 ; Turn off ROMs, install system and interrupt wrappers, set new chargen pointer
45
46 sram_init:
47
48 ; disable all interrupts
49         sei
50         ldx     #0
51         stx     NMIEN           ; disable NMI
52
53 ; disable ROMs
54         disable_rom
55
56 ; setup interrupt vectors
57         lda     #<my_IRQ_han
58         sta     $fffe
59         lda     #>my_IRQ_han
60         sta     $ffff
61
62         lda     #<my_RESET_han
63         sta     $fffc
64         lda     #>my_RESET_han
65         sta     $fffd
66
67         lda     #<my_NMI_han
68         sta     $fffa
69         lda     #>my_NMI_han
70         sta     $fffb
71
72 ; setup pointers to CIOV and SIOV wrappers
73         lda     #$4C            ; JMP opcode
74         sta     CIOV
75         lda     #<my_CIOV
76         sta     CIOV+1
77         lda     #>my_CIOV
78         sta     CIOV+2
79         lda     #$4C            ; JMP opcode
80         sta     SIOV
81         lda     #<my_SIOV
82         sta     SIOV+1
83         lda     #>my_SIOV
84         sta     SIOV+2
85
86 ; enable interrupts
87         lda     #$40
88         sta     NMIEN
89         cli
90
91         rts
92
93 .segment        "EXTZP" : zeropage
94
95 zpptr1: .res    2
96
97
98 .segment "LOWBUFS"
99
100 ; bounce buffers for CIO and SIO calls
101 CIO_buffer:     .res    BUFSZ_CIO
102 SIO_buffer:     .res    BUFSZ_SIO
103
104
105 .segment "LOWCODE"
106
107
108 ; Interrupt handlers
109 ; ------------------
110
111 ; The interrupt handlers don't look at the current state of PORTB and
112 ; unconditionally disable the ROMs on exit.
113 ; Please note that this works, since if the ROMs are enabled we anyway
114 ; aren't being called here because the vectors are pointing to their
115 ; original ROM locations.
116
117 .macro  int_wrap orgvec
118         .local  ret
119         pha
120         enable_rom
121         lda     #>ret
122         pha
123         lda     #<ret
124         pha
125         php
126         jmp     (orgvec)
127 ret:    disable_rom
128         pla
129         rti
130 .endmacro
131
132 my_IRQ_han:
133 .ifdef DEBUG
134         php
135         pha
136         tya
137         pha
138         ldy     #0
139         lda     (SAVMSC),y
140         clc
141         adc     #1
142         sta     (SAVMSC),y
143         pla
144         tay
145         pla
146         plp
147 .endif
148         int_wrap $FFFE
149
150 my_NMI_han:
151 .ifdef DEBUG
152         php
153         pha
154         tya
155         pha
156         ldy     #39
157         lda     (SAVMSC),y
158         clc
159         adc     #1
160         sta     (SAVMSC),y
161         pla
162         tay
163         pla
164         plp
165 .endif
166 ; set I bit to interrupted value
167         pha
168         txa
169         pha
170         tsx
171         lda     $103,x
172         pha
173         plp
174         pla
175         tax
176         pla
177         int_wrap $FFFA
178
179 my_RESET_han:
180         enable_rom
181         jmp     ($FFFC)
182
183
184 ; System request handlers
185 ; -----------------------
186
187
188 ; for filenames we assume they will fit into our bounce buffer
189
190 ; one filename, terminated by "invalid character", located at ICBAL/ICBAH
191
192 CIO_filename:
193         jsr     setup_zpptr1_y0
194         jsr     copy_filename
195 CIO_fn_cont:
196         jsr     ciobuf_to_iocb
197         ldy     CIO_y
198         jsr     CIO_call_a              ; call CIO (maybe A isn't needed, then we could call CIO_call)
199         php
200         pha
201         jsr     restore_icba            ; restore original ICBAL/ICBAH
202         pla
203         plp
204         rts                             ; back to application
205
206
207 ; two filenames, terminated and separated by "invalid character", located at ICBAL/ICBAH
208
209 CIO_filename2:
210         jsr     setup_zpptr1_y0
211         jsr     copy_filename
212         iny
213         jsr     copy_filename
214         jmp     CIO_fn_cont
215
216
217
218 CIO_call_a:
219         lda     CIO_a
220
221 CIOV_call:
222         pha
223         lda     PORTB
224         sta     cur_CIOV_PORTB
225         enable_rom
226         pla
227         jsr     CIOV_org
228         php
229         pha
230         lda     cur_CIOV_PORTB
231         sta     PORTB
232         pla
233         plp
234         rts
235
236
237 ; CIO handler
238 ; We have buffer pointer and length entries in the IOCB, but their
239 ; usage depends on the function.
240 ; Some functions don't care about any of them (pointer and length),
241 ; and some only use the pointer (like e.g. OPEN), and some use both.
242 ; So we need function specific handlers to correctly deal with
243 ; buffers which are overlapping with the ROM area.
244 ;
245 ; FIXME: Currently only the requests used by the runtime lib are handled.
246
247 my_CIOV:
248
249 ; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
250
251         sta     CIO_a
252         sty     CIO_y
253         stx     CIO_x
254
255         lda     ICCOM,x                 ; get function
256         cmp     #OPEN
257         beq     CIO_filename            ; filename as input parameter in buffer, length not used
258         cmp     #PUTREC
259         bcc     CIO_read                ; input (GETREC or GETCHR)
260         cmp     #CLOSE
261         bcc     CIO_write_jmp           ; output (PUTREC or PUTCHR)
262         beq     CIO_call_a              ; pass through, buffer not used
263         cmp     #RENAME                 ; 2 filenames as input parameters in buffer, length not used
264         beq     CIO_filename2
265         cmp     #GETCWD
266         bcc     CIO_filename            ; filename as input parameter in buffer, length not used
267         beq     CIO_invalid             ; GETCWD not supported yet
268         bcs     CIO_call_a              ; other commands: assume no buffer
269 ; not reached
270
271 CIO_write_jmp:
272         jmp     CIO_write
273
274 CIO_invalid:
275         lda     CIO_a
276         ldy     #DINVCM
277         rts
278
279 ; READ handler
280 ; ------------
281
282 CIO_read:
283         lda     ICBLL,x
284         ora     ICBLH,x
285         beq     CIO_call_a              ; special I/O through A register in case buffer length is 0
286
287 ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area
288
289 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
290 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
291
292         lda     ICBLH,x                 ; get high byte of length
293         bne     big_read                ; not zero -> data too large for our buffers
294                                         ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
295         lda     #<BUFSZ_CIO
296         cmp     ICBLL,x
297         bcc     big_read
298
299 ; Data size fits into bounce buffer
300
301         jsr     setup_zpptr1
302         jsr     ciobuf_to_iocb
303         jsr     CIO_call_a              ; call CIO
304         php
305         bpl     @no_err
306         cpy     #EOFERR
307         beq     @no_err
308         pha
309         jsr     restore_icba
310         pla
311         plp
312         rts                             ; return with error
313
314 @no_err:
315         sta     CIO_a
316         sty     CIO_y
317
318         jsr     copy_to_user            ; copy data into user buffer
319         jsr     restore_icba
320
321         lda     CIO_a
322         ldy     CIO_y
323         plp
324         rts                             ; return with success
325
326 ; Data size does not fit into bounce buffer
327
328 big_read:
329         lda     #0
330         sta     retlen                  ; initialize return length
331         sta     retlen+1
332         jsr     iocblen_to_orig_len
333         jsr     iocbptr_to_orig_ptr
334         jsr     setup_zpptr1
335         jsr     ciobuf_to_iocb          ; let ICBAL/ICBAH point to bounce buffer
336
337 br_loop:
338         jsr     cmp_orig_len_cio_bufsz  ; is transfer length > bounce buffer size?
339         bcs     br_last                 ; no, last transfer, use remaining size
340
341         lda     #>BUFSZ_CIO
342         sta     ICBLH,x                 ; set data length
343         lda     #<BUFSZ_CIO
344         sta     ICBLL,x
345         bne     br_cont
346
347 br_last:
348         lda     orig_len+1
349         sta     ICBLH,x                 ; set data length
350         lda     orig_len
351         sta     ICBLL,x
352
353 br_cont:
354         sta     req_len                 ; remember length of this request
355         lda     ICBLH,x
356         sta     req_len+1
357         jsr     CIO_call_a              ; do the request
358         php
359         bpl     br_no_err
360         cpy     #EOFERR
361         beq     br_no_err
362
363         pha
364         jsr     restore_icba
365         pla
366         plp
367         rts                             ; return with error
368
369 br_no_err:
370         sta     CIO_a
371         sty     CIO_y
372         pla
373         sta     CIO_p
374         jsr     copy_to_user
375
376 ; update retlen
377         clc
378         lda     retlen
379         adc     ICBLL,x
380         sta     retlen
381         lda     retlen+1
382         adc     #0
383         sta     retlen+1
384
385 ; if the request read less bytes than requested, we're done
386         lda     ICBLL,x
387         cmp     req_len
388         bne     br_done
389         lda     ICBLH,x
390         cmp     req_len+1
391         bne     br_done
392
393 ; update user buffer pointer (zpptr1)
394         clc
395         lda     zpptr1
396         adc     ICBLL,x
397         sta     zpptr1
398         lda     zpptr1+1
399         adc     #0
400         sta     zpptr1+1
401
402 ; update remaining length
403         sec
404         lda     orig_len
405         sbc     ICBLL,x
406         sta     orig_len
407         lda     orig_len+1
408         sbc     #0
409         sta     orig_len+1
410
411 ; still something left to do (remaining length != 0)?
412         lda     orig_len
413         ora     orig_len+1
414         beq     br_done
415         jmp     br_loop
416
417 ; done, write original buffer pointer and total transfer length to IOCB and return to application
418 br_done:
419         lda     retlen
420         sta     ICBLL,x
421         lda     retlen+1
422         sta     ICBLH,x
423         jsr     orig_ptr_to_iocbptr
424         lda     CIO_p
425         pha
426         lda     CIO_a
427         ldy     CIO_y
428         plp
429         rts                             ; return with success
430
431
432
433 CIO_call_a_jmp:
434         jmp     CIO_call_a
435
436
437
438 ; WRITE handler
439 ; -------------
440
441
442 CIO_write:
443         lda     ICBLL,x
444         ora     ICBLH,x
445         beq     CIO_call_a_jmp          ; special I/O through A register in case buffer length is 0
446
447 ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area
448
449 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
450 ; Otherwise we can get away with a copy to the bounce buffer and the call.
451
452         lda     ICBLH,x                 ; get high byte of length
453         bne     big_write               ; not zero -> data too large for our buffers
454                                         ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
455         lda     #<BUFSZ_CIO
456         cmp     ICBLL,x
457         bcc     big_write
458
459
460 ; Data size fits into bounce buffer
461
462         jsr     setup_zpptr1
463         jsr     ciobuf_to_iocb
464         jsr     copy_from_user
465         ldy     CIO_y
466         jsr     CIO_call_a
467         php
468         pha
469         jsr     restore_icba
470         pla
471         plp
472         rts                             ; return to application
473
474
475 ; Data size does not fit into bounce buffer
476
477 big_write:
478         lda     #0
479         sta     retlen                  ; initialize return length
480         sta     retlen+1
481         jsr     iocblen_to_orig_len
482         jsr     iocbptr_to_orig_ptr
483         jsr     setup_zpptr1
484         jsr     ciobuf_to_iocb          ; let ICBAL/ICBAH point to bounce buffer
485
486 bw_loop:
487         jsr     cmp_orig_len_cio_bufsz  ; is transfer length > bounce buffer size?
488         bcs     bw_last                 ; no, last transfer, use remaining size
489
490         lda     #>BUFSZ_CIO
491         sta     ICBLH,x                 ; set data length
492         lda     #<BUFSZ_CIO
493         sta     ICBLL,x
494         bne     bw_cont
495
496 bw_last:
497         lda     orig_len+1
498         sta     ICBLH,x                 ; set data length
499         lda     orig_len
500         sta     ICBLL,x
501
502 bw_cont:
503         sta     req_len                 ; remember length of this request
504         lda     ICBLH,x
505         sta     req_len+1
506         jsr     copy_from_user
507         jsr     CIO_call_a              ; do the request
508         php
509         bpl     bw_no_err
510
511         plp
512         rts                             ; error return
513
514 bw_no_err:
515         sta     CIO_a
516         sty     CIO_y
517         pla
518         sta     CIO_p
519
520 ; update retlen
521         clc
522         lda     retlen
523         adc     ICBLL,x
524         sta     retlen
525         lda     retlen+1
526         adc     #0
527         sta     retlen+1
528
529 ; if the request wrote less bytes than requested, we're done
530         lda     ICBLL,x
531         cmp     req_len
532         bne     bw_done
533         lda     ICBLH,x
534         cmp     req_len+1
535         bne     bw_done
536
537 ; update user buffer pointer (zpptr1)
538         clc
539         lda     zpptr1
540         adc     ICBLL,x
541         sta     zpptr1
542         lda     zpptr1+1
543         adc     #0
544         sta     zpptr1+1
545
546 ; update remaining length
547         sec
548         lda     orig_len
549         sbc     ICBLL,x
550         sta     orig_len
551         lda     orig_len+1
552         sbc     #0
553         sta     orig_len+1
554
555 ; still something left to do (remaining length != 0)?
556         lda     orig_len
557         ora     orig_len+1
558         beq     bw_done
559         jmp     bw_loop
560
561 bw_done:
562         lda     retlen
563         sta     ICBLL,x
564         lda     retlen+1
565         sta     ICBLH,x
566         jsr     orig_ptr_to_iocbptr
567         lda     CIO_p
568         pha
569         lda     CIO_a
570         ldy     CIO_y
571         plp
572         rts                             ; return with success
573
574
575
576 ; check if length is larger than bounce buffer size
577 ; input:   orig_len - length
578 ; output:         A - destroyed
579 ;                CF - 0/1 for larger/not larger
580 cmp_orig_len_cio_bufsz:
581         sec
582         lda     #<BUFSZ_CIO
583         sbc     orig_len
584         lda     #>BUFSZ_CIO
585         sbc     orig_len+1
586         rts
587
588
589 ; copy data from bounce buffer into user buffer
590 ; input:   X - IOCB index
591 ;     zpptr1 - pointer to user buffer
592 ; output:  A - destroyed
593 ;          Y - 0
594 copy_to_user:
595         ldy     ICBLL,x                 ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
596         beq     @copy_done
597 @copy:  dey
598         lda     CIO_buffer,y
599         sta     (zpptr1),y
600         cpy     #0
601         bne     @copy
602 @copy_done:
603         rts
604
605
606 ; copy data from user buffer into bounce buffer
607 ; input:   X - IOCB index
608 ;     zpptr1 - pointer to user buffer
609 ; output:  A - destroyed
610 ;          Y - 0
611 copy_from_user:
612         ldy     ICBLL,x                 ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
613         beq     @copy_done
614 @copy:  dey
615         lda     (zpptr1),y
616         sta     CIO_buffer,y
617         cpy     #0
618         bne     @copy
619 @copy_done:
620         rts
621
622
623 ; copy ICBLL/ICBLH to 'orig_len'
624 ; input:   X - IOCB index
625 ; output:  A - destroyed
626 iocblen_to_orig_len:
627         lda     ICBLL,x
628         sta     orig_len
629         lda     ICBLH,x
630         sta     orig_len+1
631         rts
632
633
634 ; copy ICBAL/ICBAH to 'orig_ptr'
635 ; input:   X - IOCB index
636 ; output:  A - destroyed
637 iocbptr_to_orig_ptr:
638         lda     ICBAL,x
639         sta     orig_ptr
640         lda     ICBAH,x
641         sta     orig_ptr+1
642         rts
643
644
645 ; copy 'orig_ptr' to ICBAL/ICBAH
646 ; input:   X - IOCB index
647 ; output:  A - destroyed
648 orig_ptr_to_iocbptr:
649         lda     orig_ptr
650         sta     ICBAL,x
651         lda     orig_ptr+1
652         sta     ICBAH,x
653         rts
654
655
656 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
657 ; input:   X - IOCB index
658 ; output:  A - destroyed
659 restore_icba:
660         lda     zpptr1
661         sta     ICBAL,x
662         lda     zpptr1+1
663         sta     ICBAH,x
664         rts
665
666
667 ; put bounce buffer address into ICBAL/ICBAH
668 ; input:   X - IOCB index
669 ; output:  A - destroyed
670 ciobuf_to_iocb:
671         lda     #<CIO_buffer
672         sta     ICBAL,x
673         lda     #>CIO_buffer
674         sta     ICBAH,x
675         rts
676
677
678 ; copy file name pointed to by 'zpptr1' to bounce buffer 'CIO_buffer'
679 ; input:   Y - index into file name buffer and CIO_buffer
680 ; output:  Y - points to first invalid byte after file name
681 ;          A - destroyed
682 copy_filename:
683         lda     (zpptr1),y
684         sta     CIO_buffer,y
685         beq     copy_fn_done
686         iny
687         cmp     #ATEOL
688         bne     copy_filename
689         dey
690 copy_fn_done:
691         rts
692
693
694 ; write IOCB buffer address into zpptr1
695 ; input:   X - IOCB index
696 ; output:  Y - 0 (for setup_zpptr1_y0, else unchanged)
697 ;          A - destroyed
698 setup_zpptr1_y0:
699         ldy     #0
700 setup_zpptr1:
701         lda     ICBAL,x                 ; put buffer address into zp pointer
702         sta     zpptr1
703         lda     ICBAH,x
704         sta     zpptr1+1
705         rts
706
707 ;---------------------------------------------------------
708
709 my_SIOV:
710         pha
711         lda     PORTB
712         sta     cur_SIOV_PORTB
713         enable_rom
714         pla
715         jsr     SIOV_org
716         php
717         pha
718         lda     cur_SIOV_PORTB
719         sta     PORTB
720         pla
721         plp
722         rts
723
724 ;---------------------------------------------------------
725
726 KEYBDV_wrapper:
727
728         lda     #>(kret-1)
729         pha
730         lda     #<(kret-1)
731         pha
732         lda     PORTB
733         sta     cur_KEYBDV_PORTB
734         enable_rom
735         lda     KEYBDV+5
736         pha
737         lda     KEYBDV+4
738         pha
739         rts             ; call keyboard handler
740 kret:   pha
741         lda     cur_KEYBDV_PORTB
742         sta     PORTB
743         pla
744         rts
745
746 CIO_a:                  .res    1
747 CIO_x:                  .res    1
748 CIO_y:                  .res    1
749 CIO_p:                  .res    1
750 cur_CIOV_PORTB:         .res    1
751 cur_SIOV_PORTB:         .res    1
752 cur_KEYBDV_PORTB:       .res    1
753 orig_ptr:               .res    2
754 orig_len:               .res    2
755 req_len:                .res    2
756 retlen:                 .res    2
757
758 .endif  ; .if .defined(__ATARIXL__)