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