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