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