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