]> 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 IRQ_save
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 NMI_save
178
179 my_RESET_han:
180         enable_rom
181         jmp     (RESET_save)
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 ; All input and output registers need to be preserved (I'm not 100%
245 ; sure about Y as input, but let's preserve it for now.)
246 ;
247 ; FIXME: Currently only the requests used by the runtime lib are handled.
248
249 my_CIOV:
250
251 ; @@@ TODO: check X for valid IOCB index ((X < $80) and ((X & $F) == 0))
252
253         sta     CIO_a
254         sty     CIO_y
255         stx     CIO_x
256
257         lda     ICCOM,x                 ; get function
258         cmp     #OPEN
259         beq     CIO_filename            ; filename as input parameter in buffer, length not used
260         cmp     #PUTREC
261         bcc     CIO_read                ; input (GETREC or GETCHR)
262         cmp     #CLOSE
263         bcc     CIO_write_jmp           ; output (PUTREC or PUTCHR)
264         beq     CIO_call_a              ; pass through, buffer not used
265         cmp     #RENAME                 ; 2 filenames as input parameters in buffer, length not used
266         beq     CIO_filename2
267         cmp     #GETCWD
268         bcc     CIO_filename            ; filename as input parameter in buffer, length not used
269         beq     CIO_read                ; input
270         bcs     CIO_call_a              ; other commands: assume no buffer
271 ; not reached
272
273 CIO_write_jmp:
274         jmp     CIO_write
275
276
277 ; READ handler
278 ; ------------
279
280 CIO_read:
281         lda     ICBLL,x
282         ora     ICBLH,x
283         beq     CIO_call_a              ; special I/O through A register in case buffer length is 0
284
285 ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area
286
287 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
288 ; Otherwise we can get away with one call and a copy to the final destination afterwards.
289
290         lda     ICBLH,x                 ; get high byte of length
291         bne     big_read                ; not zero -> data too large for our buffers
292                                         ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
293         lda     #<BUFSZ_CIO
294         cmp     ICBLL,x
295         bcc     big_read
296
297 ; Data size fits into bounce buffer
298
299         jsr     setup_zpptr1
300         jsr     ciobuf_to_iocb
301         jsr     CIO_call_a              ; call CIO
302         php
303         bpl     @no_err
304         cpy     #EOFERR
305         beq     @no_err
306         pha
307         jsr     restore_icba
308         pla
309         plp
310         rts                             ; return with error
311
312 @no_err:
313         sta     CIO_a
314         sty     CIO_y
315
316         jsr     copy_to_user            ; copy data into user buffer
317         jsr     restore_icba
318
319         lda     CIO_a
320         ldy     CIO_y
321         plp
322         rts                             ; return with success
323
324 ; Data size does not fit into bounce buffer
325
326 big_read:
327         lda     #0
328         sta     retlen                  ; initialize return length
329         sta     retlen+1
330         jsr     iocblen_to_orig_len
331         jsr     iocbptr_to_orig_ptr
332         jsr     setup_zpptr1
333         jsr     ciobuf_to_iocb          ; let ICBAL/ICBAH point to bounce buffer
334
335 br_loop:
336         jsr     cmp_orig_len_cio_bufsz  ; is transfer length > bounce buffer size?
337         bcs     br_last                 ; no, last transfer, use remaining size
338
339         lda     #>BUFSZ_CIO
340         sta     ICBLH,x                 ; set data length
341         lda     #<BUFSZ_CIO
342         sta     ICBLL,x
343         bne     br_cont
344
345 br_last:
346         lda     orig_len+1
347         sta     ICBLH,x                 ; set data length
348         lda     orig_len
349         sta     ICBLL,x
350
351 br_cont:
352         sta     req_len                 ; remember length of this request
353         lda     ICBLH,x
354         sta     req_len+1
355         jsr     CIO_call_a              ; do the request
356         php
357         bpl     br_no_err
358         cpy     #EOFERR
359         beq     br_no_err
360
361         pha
362         jsr     restore_icba
363         pla
364         plp
365         rts                             ; return with error
366
367 br_no_err:
368         sta     CIO_a
369         sty     CIO_y
370         pla
371         sta     CIO_p
372         jsr     copy_to_user
373
374 ; update retlen
375         clc
376         lda     retlen
377         adc     ICBLL,x
378         sta     retlen
379         lda     retlen+1
380         adc     #0
381         sta     retlen+1
382
383 ; if the request read less bytes than requested, we're done
384         lda     ICBLL,x
385         cmp     req_len
386         bne     br_done
387         lda     ICBLH,x
388         cmp     req_len+1
389         bne     br_done
390
391 ; update user buffer pointer (zpptr1)
392         clc
393         lda     zpptr1
394         adc     ICBLL,x
395         sta     zpptr1
396         lda     zpptr1+1
397         adc     #0
398         sta     zpptr1+1
399
400 ; update remaining length
401         sec
402         lda     orig_len
403         sbc     ICBLL,x
404         sta     orig_len
405         lda     orig_len+1
406         sbc     #0
407         sta     orig_len+1
408
409 ; still something left to do (remaining length != 0)?
410         lda     orig_len
411         ora     orig_len+1
412         beq     br_done
413         jmp     br_loop
414
415 ; done, write original buffer pointer and total transfer length to IOCB and return to application
416 br_done:
417         lda     retlen
418         sta     ICBLL,x
419         lda     retlen+1
420         sta     ICBLH,x
421         jsr     orig_ptr_to_iocbptr
422         lda     CIO_p
423         pha
424         lda     CIO_a
425         ldy     CIO_y
426         plp
427         rts                             ; return with success
428
429
430
431 CIO_call_a_jmp:
432         jmp     CIO_call_a
433
434
435
436 ; WRITE handler
437 ; -------------
438
439
440 CIO_write:
441         lda     ICBLL,x
442         ora     ICBLH,x
443         beq     CIO_call_a_jmp          ; special I/O through A register in case buffer length is 0
444
445 ; @@@ TODO: check if bounce buffer is really needed because buffer is in ROM area
446
447 ; If the data length is larger than our bounce buffer, we have to split the request into smaller ones.
448 ; Otherwise we can get away with a copy to the bounce buffer and the call.
449
450         lda     ICBLH,x                 ; get high byte of length
451         bne     big_write               ; not zero -> data too large for our buffers
452                                         ; CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES
453         lda     #<BUFSZ_CIO
454         cmp     ICBLL,x
455         bcc     big_write
456
457
458 ; Data size fits into bounce buffer
459
460         jsr     setup_zpptr1
461         jsr     ciobuf_to_iocb
462         jsr     copy_from_user
463         ldy     CIO_y
464         jsr     CIO_call_a
465         php
466         pha
467         jsr     restore_icba
468         pla
469         plp
470         rts                             ; return to application
471
472
473 ; Data size does not fit into bounce buffer
474
475 big_write:
476         lda     #0
477         sta     retlen                  ; initialize return length
478         sta     retlen+1
479         jsr     iocblen_to_orig_len
480         jsr     iocbptr_to_orig_ptr
481         jsr     setup_zpptr1
482         jsr     ciobuf_to_iocb          ; let ICBAL/ICBAH point to bounce buffer
483
484 bw_loop:
485         jsr     cmp_orig_len_cio_bufsz  ; is transfer length > bounce buffer size?
486         bcs     bw_last                 ; no, last transfer, use remaining size
487
488         lda     #>BUFSZ_CIO
489         sta     ICBLH,x                 ; set data length
490         lda     #<BUFSZ_CIO
491         sta     ICBLL,x
492         bne     bw_cont
493
494 bw_last:
495         lda     orig_len+1
496         sta     ICBLH,x                 ; set data length
497         lda     orig_len
498         sta     ICBLL,x
499
500 bw_cont:
501         sta     req_len                 ; remember length of this request
502         lda     ICBLH,x
503         sta     req_len+1
504         jsr     copy_from_user
505         jsr     CIO_call_a              ; do the request
506         php
507         bpl     bw_no_err
508
509         plp
510         rts                             ; error return
511
512 bw_no_err:
513         sta     CIO_a
514         sty     CIO_y
515         pla
516         sta     CIO_p
517
518 ; update retlen
519         clc
520         lda     retlen
521         adc     ICBLL,x
522         sta     retlen
523         lda     retlen+1
524         adc     #0
525         sta     retlen+1
526
527 ; if the request wrote less bytes than requested, we're done
528         lda     ICBLL,x
529         cmp     req_len
530         bne     bw_done
531         lda     ICBLH,x
532         cmp     req_len+1
533         bne     bw_done
534
535 ; update user buffer pointer (zpptr1)
536         clc
537         lda     zpptr1
538         adc     ICBLL,x
539         sta     zpptr1
540         lda     zpptr1+1
541         adc     #0
542         sta     zpptr1+1
543
544 ; update remaining length
545         sec
546         lda     orig_len
547         sbc     ICBLL,x
548         sta     orig_len
549         lda     orig_len+1
550         sbc     #0
551         sta     orig_len+1
552
553 ; still something left to do (remaining length != 0)?
554         lda     orig_len
555         ora     orig_len+1
556         beq     bw_done
557         jmp     bw_loop
558
559 bw_done:
560         lda     retlen
561         sta     ICBLL,x
562         lda     retlen+1
563         sta     ICBLH,x
564         jsr     orig_ptr_to_iocbptr
565         lda     CIO_p
566         pha
567         lda     CIO_a
568         ldy     CIO_y
569         plp
570         rts                             ; return with success
571
572
573
574 ; check if length is larger than bounce buffer size
575 ; input:   orig_len - length
576 ; output:         A - destroyed
577 ;                CF - 0/1 for larger/not larger
578 cmp_orig_len_cio_bufsz:
579         sec
580         lda     #<BUFSZ_CIO
581         sbc     orig_len
582         lda     #>BUFSZ_CIO
583         sbc     orig_len+1
584         rts
585
586
587 ; copy data from bounce buffer into user buffer
588 ; input:   X - IOCB index
589 ;     zpptr1 - pointer to user buffer
590 ; output:  A - destroyed
591 ;          Y - 0
592 copy_to_user:
593         ldy     ICBLL,x                 ; get # of bytes read (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
594         beq     @copy_done
595 @copy:  dey
596         lda     CIO_buffer,y
597         sta     (zpptr1),y
598         cpy     #0
599         bne     @copy
600 @copy_done:
601         rts
602
603
604 ; copy data from user buffer into bounce buffer
605 ; input:   X - IOCB index
606 ;     zpptr1 - pointer to user buffer
607 ; output:  A - destroyed
608 ;          Y - 0
609 copy_from_user:
610         ldy     ICBLL,x                 ; get # of bytes to write (CHANGE HERE TO SUPPORT BOUNCE BUFFERS > 255 BYTES)
611         beq     @copy_done
612 @copy:  dey
613         lda     (zpptr1),y
614         sta     CIO_buffer,y
615         cpy     #0
616         bne     @copy
617 @copy_done:
618         rts
619
620
621 ; copy ICBLL/ICBLH to 'orig_len'
622 ; input:   X - IOCB index
623 ; output:  A - destroyed
624 iocblen_to_orig_len:
625         lda     ICBLL,x
626         sta     orig_len
627         lda     ICBLH,x
628         sta     orig_len+1
629         rts
630
631
632 ; copy ICBAL/ICBAH to 'orig_ptr'
633 ; input:   X - IOCB index
634 ; output:  A - destroyed
635 iocbptr_to_orig_ptr:
636         lda     ICBAL,x
637         sta     orig_ptr
638         lda     ICBAH,x
639         sta     orig_ptr+1
640         rts
641
642
643 ; copy 'orig_ptr' to ICBAL/ICBAH
644 ; input:   X - IOCB index
645 ; output:  A - destroyed
646 orig_ptr_to_iocbptr:
647         lda     orig_ptr
648         sta     ICBAL,x
649         lda     orig_ptr+1
650         sta     ICBAH,x
651         rts
652
653
654 ; restore original contents of ICBAL/ICBAH from 'zpptr1'
655 ; input:   X - IOCB index
656 ; output:  A - destroyed
657 restore_icba:
658         lda     zpptr1
659         sta     ICBAL,x
660         lda     zpptr1+1
661         sta     ICBAH,x
662         rts
663
664
665 ; put bounce buffer address into ICBAL/ICBAH
666 ; input:   X - IOCB index
667 ; output:  A - destroyed
668 ciobuf_to_iocb:
669         lda     #<CIO_buffer
670         sta     ICBAL,x
671         lda     #>CIO_buffer
672         sta     ICBAH,x
673         rts
674
675
676 ; copy file name pointed to by 'zpptr1' to bounce buffer 'CIO_buffer'
677 ; input:   Y - index into file name buffer and CIO_buffer
678 ; output:  Y - points to first invalid byte after file name
679 ;          A - destroyed
680 copy_filename:
681         lda     (zpptr1),y
682         sta     CIO_buffer,y
683         beq     copy_fn_done
684         iny
685         cmp     #ATEOL
686         bne     copy_filename
687         dey
688 copy_fn_done:
689         rts
690
691
692 ; write IOCB buffer address into zpptr1
693 ; input:   X - IOCB index
694 ; output:  Y - 0 (for setup_zpptr1_y0, else unchanged)
695 ;          A - destroyed
696 setup_zpptr1_y0:
697         ldy     #0
698 setup_zpptr1:
699         lda     ICBAL,x                 ; put buffer address into zp pointer
700         sta     zpptr1
701         lda     ICBAH,x
702         sta     zpptr1+1
703         rts
704
705 ;---------------------------------------------------------
706
707 my_SIOV:
708         pha
709         lda     PORTB
710         sta     cur_SIOV_PORTB
711         enable_rom
712         pla
713         jsr     SIOV_org
714         php
715         pha
716         lda     cur_SIOV_PORTB
717         sta     PORTB
718         pla
719         plp
720         rts
721
722 ;---------------------------------------------------------
723
724 KEYBDV_wrapper:
725
726         lda     #>(kret-1)
727         pha
728         lda     #<(kret-1)
729         pha
730         lda     PORTB
731         sta     cur_KEYBDV_PORTB
732         enable_rom
733         lda     KEYBDV+5
734         pha
735         lda     KEYBDV+4
736         pha
737         rts             ; call keyboard handler
738 kret:   pha
739         lda     cur_KEYBDV_PORTB
740         sta     PORTB
741         pla
742         rts
743
744 CIO_a:                  .res    1
745 CIO_x:                  .res    1
746 CIO_y:                  .res    1
747 CIO_p:                  .res    1
748 cur_CIOV_PORTB:         .res    1
749 cur_SIOV_PORTB:         .res    1
750 cur_KEYBDV_PORTB:       .res    1
751 orig_ptr:               .res    2
752 orig_len:               .res    2
753 req_len:                .res    2
754 retlen:                 .res    2
755
756 .endif  ; .if .defined(__ATARIXL__)