]> git.sur5r.net Git - cc65/blob - libsrc/cbm510/crt0.s
* Moved the BASIC stub that calls the compiled program into it's own segment
[cc65] / libsrc / cbm510 / crt0.s
1 ;
2 ; Startup code for cc65 (CBM 500 version)
3 ;
4
5         .export         _exit
6         .export         __STARTUP__ : absolute = 1      ; Mark as startup
7
8         .import         _clrscr, initlib, donelib, callirq_y
9         .import         push0, callmain
10         .import         __CHARRAM_START__, __CHARRAM_SIZE__, __VIDRAM_START__
11         .import         __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
12         .import         __INTERRUPTOR_COUNT__
13         .import         scnkey, UDTIM
14
15         .include        "zeropage.inc"
16         .include        "extzp.inc"
17         .include        "cbm510.inc"
18
19
20 ; ------------------------------------------------------------------------
21 ; BASIC header and a small BASIC program. Since it is not possible to start
22 ; programs in other banks using SYS, the BASIC program will write a small
23 ; machine code program into memory at $100 and start that machine code
24 ; program. The machine code program will then start the machine language
25 ; code in bank 0, which will initialize the system by copying stuff from
26 ; the system bank, and start the application.
27 ;
28 ; Here's the basic program that's in the following lines:
29 ;
30 ; 10 for i=0 to 4
31 ; 20 read j
32 ; 30 poke 256+i,j
33 ; 40 next i
34 ; 50 sys 256
35 ; 60 data 120,169,0,133,0
36 ;
37 ; The machine program in the data lines is:
38 ;
39 ; sei
40 ; lda     #$00
41 ; sta     $00           <-- Switch to bank 0 after this command
42 ;
43 ; Initialization is not only complex because of the jumping from one bank
44 ; into another. but also because we want to save memory, and because of
45 ; this, we will use the system memory ($00-$3FF) for initialization stuff
46 ; that is overwritten later.
47 ;
48
49 .segment        "EXEHDR"
50
51         .byte   $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00
52         .byte   $19,$00,$14,$00,$87,$20,$4a,$00,$27,$00,$1e,$00,$97,$20,$32,$35
53         .byte   $36,$aa,$49,$2c,$4a,$00,$2f,$00,$28,$00,$82,$20,$49,$00,$39,$00
54         .byte   $32,$00,$9e,$20,$32,$35,$36,$00,$4f,$00,$3c,$00,$83,$20,$31,$32
55         .byte   $30,$2c,$31,$36,$39,$2c,$30,$2c,$31,$33,$33,$2c,$30,$00,$00,$00
56
57 ;------------------------------------------------------------------------------
58 ; A table that contains values that must be transfered from the system zero
59 ; page into out zero page. Contains pairs of bytes, first one is the address
60 ; in the system ZP, second one is our ZP address. The table goes into page 2,
61 ; but is declared here, because it is needed earlier.
62
63 .SEGMENT        "PAGE2"
64
65 .proc   transfer_table
66
67         .byte   $CA, CURS_Y
68         .byte   $CB, CURS_X
69         .byte   $EC, CHARCOLOR
70
71 .endproc
72
73
74 ;------------------------------------------------------------------------------
75 ; Page 3 data. This page contains the break vector and the bankswitch
76 ; subroutine that is copied into high memory on startup. The space occupied by
77 ; this routine will later be used for a copy of the bank 15 stack. It must be
78 ; saved, since we're going to destroy it when calling bank 15.
79
80 .segment        "PAGE3"
81
82 BRKVec: .addr   _exit           ; BRK indirect vector
83
84 .proc   callbank15
85
86         excrts  = $FEFE
87
88 .org    $FEC3
89
90 entry:  php
91         pha
92         lda     #$0F                    ; Bank 15
93         sta     IndReg
94         txa
95         pha
96         tya
97         pha
98         sei
99         ldy     #$FF
100         lda     (sysp1),y
101         tay
102         lda     ExecReg
103         sta     (sysp1),y
104         dey
105
106         lda     #.hibyte(excrts-1)
107         sta     (sysp1),y
108         dey
109         lda     #.lobyte(excrts-1)
110         sta     (sysp1),y
111
112         tya
113         sec
114         sbc     #7
115         sta     $1FF                    ; Save new sp
116         tay
117
118         tsx
119
120         pla
121         iny
122         sta     (sysp1),y
123         pla
124         iny
125         sta     (sysp1),y
126         pla
127         iny
128         sta     (sysp1),y
129         pla
130         iny
131         sta     (sysp1),y
132
133         lda     $105,x
134         sec
135         sbc     #3
136         iny
137         sta     (sysp1),y
138         lda     $106,x
139         sbc     #0
140         iny
141         sta     (sysp1),y
142
143         ldy     $1FF                    ; Restore sp in bank 15
144
145         lda     #.hibyte(expull-1)
146         sta     (sysp1),y
147         dey
148         lda     #.lobyte(expull-1)
149         sta     (sysp1),y
150         dey
151         pla
152         pla
153         tsx
154         stx     $1FF
155         tya
156         tax
157         txs
158         lda     IndReg
159         jmp     $FFF6
160
161 expull: pla
162         tay
163         pla
164         tax
165         pla
166         plp
167         rts
168
169 .if (expull <> $FF26)
170 .error "Symbol expull must be aligned with kernal in bank 15"
171 .endif
172
173 .reloc
174
175 .endproc
176
177 ;------------------------------------------------------------------------------
178 ; The code in the target bank when switching back will be put at the bottom
179 ; of the stack. We will jump here to switch segments. The range $F2..$FF is
180 ; not used by any kernal routine.
181
182 .segment        "STARTUP"
183
184 Back:   sta     ExecReg
185
186 ; We are at $100 now. The following snippet is a copy of the code that is poked
187 ; in the system bank memory by the basic header program, it's only for
188 ; documentation and not actually used here:
189
190         sei
191         lda     #$00
192         sta     ExecReg
193
194 ; This is the actual starting point of our code after switching banks for
195 ; startup. Beware: The following code will get overwritten as soon as we
196 ; use the stack (since it's in page 1)! We jump to another location, since
197 ; we need some space for subroutines that aren't used later.
198
199         jmp     Origin
200
201 ; Hardware vectors, copied to $FFFA
202
203 .proc   vectors
204         sta     ExecReg
205         rts
206         nop
207         .word   nmi             ; NMI vector
208         .word   0               ; Reset - not used
209         .word   irq             ; IRQ vector
210 .endproc
211
212 ; Initializers for the extended zeropage. See extzp.s
213
214 .proc   extzp
215         .word   $0100           ; sysp1
216         .word   $0300           ; sysp3
217         .word   $d800           ; vic
218         .word   $da00           ; sid
219         .word   $db00           ; cia1
220         .word   $dc00           ; cia2
221         .word   $dd00           ; acia
222         .word   $de00           ; tpi1
223         .word   $df00           ; tpi2
224         .word   $eab1           ; ktab1
225         .word   $eb11           ; ktab2
226         .word   $eb71           ; ktab3
227         .word   $ebd1           ; ktab4
228 .endproc
229
230 ; Switch the indirect segment to the system bank
231
232 Origin: lda     #$0F
233         sta     IndReg
234
235 ; Initialize the extended zeropage
236
237         ldx     #.sizeof(extzp)-1
238 L1:     lda     extzp,x
239         sta     <__EXTZP_RUN__,x
240         dex
241         bpl     L1
242
243 ; Save the old stack pointer from the system bank and setup our hw sp
244
245         tsx
246         txa
247         ldy     #$FF
248         sta     (sysp1),y       ; Save system stack point into $F:$1FF
249         ldx     #$FE            ; Leave $1FF untouched for cross bank calls
250         txs                     ; Set up our own stack
251
252 ; Copy stuff from the system zeropage to ours
253
254         lda     #.sizeof(transfer_table)
255         sta     ktmp
256 L2:     ldx     ktmp
257         ldy     transfer_table-2,x
258         lda     transfer_table-1,x
259         tax
260         lda     (sysp0),y
261         sta     $00,x
262         dec     ktmp
263         dec     ktmp
264         bne     L2
265
266 ; Set the interrupt, NMI and other vectors
267
268         ldx     #.sizeof(vectors)-1
269 L3:     lda     vectors,x
270         sta     $10000 - .sizeof(vectors),x
271         dex
272         bpl     L3
273
274 ; Setup the C stack
275
276         lda     #.lobyte(callbank15::entry)
277         sta     sp
278         lda     #.hibyte(callbank15::entry)
279         sta     sp+1
280
281 ; Setup the subroutine and jump vector table that redirects kernal calls to
282 ; the system bank.
283
284         ldy     #.sizeof(callbank15)
285 @L1:    lda     callbank15-1,y
286         sta     callbank15::entry-1,y
287         dey
288         bne     @L1
289
290 ; Setup the jump vector table. Y is zero on entry.
291
292         ldx     #45-1                   ; Number of vectors
293 @L2:    lda     #$20                    ; JSR opcode
294         sta     $FF6F,y
295         iny
296         lda     #.lobyte(callbank15::entry)
297         sta     $FF6F,y
298         iny
299         lda     #.hibyte(callbank15::entry)
300         sta     $FF6F,y
301         iny
302         dex
303         bpl     @L2
304
305 ; Set the indirect segment to bank we're executing in
306
307         lda     ExecReg
308         sta     IndReg
309
310 ; Zero the BSS segment. We will do that here instead calling the routine
311 ; in the common library, since we have the memory anyway, and this way,
312 ; it's reused later.
313
314         lda     #<__BSS_RUN__
315         sta     ptr1
316         lda     #>__BSS_RUN__
317         sta     ptr1+1
318         lda     #0
319         tay
320
321 ; Clear full pages
322
323         ldx     #>__BSS_SIZE__
324         beq     Z2
325 Z1:     sta     (ptr1),y
326         iny
327         bne     Z1
328         inc     ptr1+1                  ; Next page
329         dex
330         bne     Z1
331
332 ; Clear the remaining page
333
334 Z2:     ldx     #<__BSS_SIZE__
335         beq     Z4
336 Z3:     sta     (ptr1),y
337         iny
338         dex
339         bne     Z3
340 Z4:     jmp     Init
341
342 ; ------------------------------------------------------------------------
343 ; We are at $200 now. We may now start calling subroutines safely, since
344 ; the code we execute is no longer in the stack page.
345
346 .segment        "PAGE2"
347
348 ; Copy the character rom from the system bank into the execution bank
349
350 Init:   lda     #<$C000
351         sta     ptr1
352         lda     #>$C000
353         sta     ptr1+1
354         lda     #<__CHARRAM_START__
355         sta     ptr2
356         lda     #>__CHARRAM_START__
357         sta     ptr2+1
358         lda     #>__CHARRAM_SIZE__      ; 16 * 256 bytes to copy
359         sta     tmp1
360         ldy     #$00
361 ccopy:  lda     #$0F
362         sta     IndReg                  ; Access the system bank
363 ccopy1: lda     (ptr1),y
364         sta     __VIDRAM_START__,y
365         iny
366         bne     ccopy1
367         lda     ExecReg
368         sta     IndReg
369 ccopy2: lda     __VIDRAM_START__,y
370         sta     (ptr2),y
371         iny
372         bne     ccopy2
373         inc     ptr1+1
374         inc     ptr2+1                  ; Bump high pointer bytes
375         dec     tmp1
376         bne     ccopy
377
378 ; Clear the video memory. We will do this before switching the video to bank 0
379 ; to avoid garbage when doing so.
380
381         jsr     _clrscr
382
383 ; Reprogram the VIC so that the text screen and the character ROM is in the
384 ; execution bank. This is done in three steps:
385
386         lda     #$0F                    ; We need access to the system bank
387         sta     IndReg
388
389 ; Place the VIC video RAM into bank 0
390 ; CA (STATVID)   = 0
391 ; CB (VICDOTSEL) = 0
392
393         ldy     #TPI::CR
394         lda     (tpi1),y
395         sta     vidsave+0
396         and     #%00001111
397         ora     #%10100000
398         sta     (tpi1),y
399
400 ; Set bit 14/15 of the VIC address range to the high bits of __VIDRAM_START__
401 ; PC6/PC7 (VICBANKSEL 0/1) = 11
402
403         ldy     #TPI::PRC
404         lda     (tpi2),y
405         sta     vidsave+1
406         and     #$3F
407         ora     #<((>__VIDRAM_START__) & $C0)
408         sta     (tpi2),y
409
410 ; Set the VIC base address register to the addresses of the video and
411 ; character RAM.
412
413         ldy     #VIC_VIDEO_ADR
414         lda     (vic),y
415         sta     vidsave+2
416         and     #$01
417         ora     #<(((__VIDRAM_START__ >> 6) & $F0) | ((__CHARRAM_START__ >> 10) & $0E) | $02)
418 ;       and     #$0F
419 ;       ora     #<(((>__VIDRAM_START__) << 2) & $F0)
420         sta     (vic),y
421
422 ; Switch back to the execution bank
423
424         lda     ExecReg
425         sta     IndReg
426
427 ; Activate chained interrupt handlers, then enable interrupts.
428
429         lda     #.lobyte(__INTERRUPTOR_COUNT__*2)
430         sta     irqcount
431         cli
432
433 ; Call module constructors.
434
435         jsr     initlib
436
437 ; Push arguments and call main()
438
439         jsr     callmain
440
441 ; Call module destructors. This is also the _exit entry and the default entry
442 ; point for the break vector.
443
444 _exit:  pha                     ; Save the return code on stack
445         jsr     donelib         ; Run module destructors
446         lda     #$00
447         sta     irqcount        ; Disable custom irq handlers
448
449 ; Address the system bank
450
451         lda     #$0F
452         sta     IndReg
453
454 ; Switch back the video to the system bank
455
456         ldy     #TPI::CR
457         lda     vidsave+0
458         sta     (tpi1),y
459
460         ldy     #TPI::PRC
461         lda     vidsave+1
462         sta     (tpi2),y
463
464         ldy     #VIC_VIDEO_ADR
465         lda     vidsave+2
466         sta     (vic),y
467
468 ; Copy stuff back from our zeropage to the systems
469
470 .if 0
471         lda     #.sizeof(transfer_table)
472         sta     ktmp
473 @L0:    ldx     ktmp
474         ldy     transfer_table-2,x
475         lda     transfer_table-1,x
476         tax
477         lda     $00,x
478         sta     (sysp0),y
479         dec     ktmp
480         dec     ktmp
481         bne     @L0
482 .endif
483
484 ; Place the program return code into ST
485
486         pla
487         ldy     #$9C            ; ST
488         sta     (sysp0),y
489
490 ; Setup the welcome code at the stack bottom in the system bank.
491
492         ldy     #$FF
493         lda     (sysp1),y       ; Load system bank sp
494         tax
495         iny                     ; Y = 0
496         lda     #$58            ; CLI opcode
497         sta     (sysp1),y
498         iny
499         lda     #$60            ; RTS opcode
500         sta     (sysp1),y
501         lda     IndReg
502         sei
503         txs
504         jmp     Back
505
506 ; -------------------------------------------------------------------------
507 ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
508 ; easier chaining, we do handle the IRQs in the execution bank (instead of
509 ; passing them to the system bank).
510
511 ; This is the mapping of the active irq register of the 6525 (tpi1):
512 ;
513 ; Bit   7       6       5       4       3       2       1       0
514 ;                               |       |       |       |       ^ 50 Hz
515 ;                               |       |       |       ^ SRQ IEEE 488
516 ;                               |       |       ^ cia
517 ;                               |       ^ IRQB ext. Port
518 ;                               ^ acia
519
520 irq:    pha
521         txa
522         pha
523         tya
524         pha
525         lda     IndReg
526         pha
527         lda     ExecReg
528         sta     IndReg                  ; Be sure to address our segment
529         tsx
530         lda     $105,x                  ; Get the flags from the stack
531         and     #$10                    ; Test break flag
532         bne     dobrk
533
534 ; It's an IRQ
535
536         cld
537
538 ; Call chained IRQ handlers
539
540         ldy     irqcount
541         beq     irqskip
542         jsr     callirq_y               ; Call the functions
543
544 ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
545
546 irqskip:lda     #$0F
547         sta     IndReg
548         ldy     #TPI::AIR
549         lda     (tpi1),y                ; Interrupt Register 6525
550         beq     noirq
551
552 ; 50/60Hz interrupt
553
554         cmp     #%00000001              ; ticker irq?
555         bne     irqend
556         jsr     scnkey                  ; Poll the keyboard
557         jsr     UDTIM                   ; Bump the time
558
559 ; Done
560
561 irqend: ldy     #TPI::AIR
562         sta     (tpi1),y                ; Clear interrupt
563
564 noirq:  pla
565         sta     IndReg
566         pla
567         tay
568         pla
569         tax
570         pla
571 nmi:    rti
572
573 dobrk:  jmp     (BRKVec)
574
575 ; -------------------------------------------------------------------------
576 ; Data area
577
578 .data
579 vidsave:.res    3
580
581 .bss
582 irqcount:       .byte   0
583
584