]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/crt0.s
5b6bb8b3cb8ff32e4e3840b2a60481924db9a64c
[cc65] / libsrc / cbm610 / crt0.s
1 ;
2 ; Startup code for cc65 (CBM 600/700 version)
3 ;
4
5         .export         _exit, BRKVec
6         .export         __STARTUP__ : absolute = 1      ; Mark as startup
7
8         .import         callirq_y, initlib, donelib
9         .import         push0, callmain
10         .import         __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
11         .import         __INTERRUPTOR_COUNT__
12         .import         scnkey, UDTIM
13
14         .include        "zeropage.inc"
15         .include        "extzp.inc"
16         .include        "cbm610.inc"
17
18
19 ; ------------------------------------------------------------------------
20 ; BASIC header and a small BASIC program. Since it is not possible to start
21 ; programs in other banks using SYS, the BASIC program will write a small
22 ; machine code program into memory at $100 and start that machine code
23 ; program. The machine code program will then start the machine language
24 ; code in bank 1, which will initialize the system by copying stuff from
25 ; the system bank, and start the application.
26 ;
27 ; Here's the basic program that's in the following lines:
28 ;
29 ; 10 for i=0 to 4
30 ; 20 read j
31 ; 30 poke 256+i,j
32 ; 40 next i
33 ; 50 sys 256
34 ; 60 data 120,169,1,133,0
35 ;
36 ; The machine program in the data lines is:
37 ;
38 ; sei
39 ; lda     #$01
40 ; sta     $00           <-- Switch to bank 1 after this command
41 ;
42 ; Initialization is not only complex because of the jumping from one bank
43 ; into another. but also because we want to save memory, and because of
44 ; this, we will use the system memory ($00-$3FF) for initialization stuff
45 ; that is overwritten later.
46 ;
47
48 .segment        "BASICHDR"
49
50         .byte   $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00
51         .byte   $19,$00,$14,$00,$87,$20,$4a,$00,$27,$00,$1e,$00,$97,$20,$32,$35
52         .byte   $36,$aa,$49,$2c,$4a,$00,$2f,$00,$28,$00,$82,$20,$49,$00,$39,$00
53         .byte   $32,$00,$9e,$20,$32,$35,$36,$00,$4f,$00,$3c,$00,$83,$20,$31,$32
54         .byte   $30,$2c,$31,$36,$39,$2c,$31,$2c,$31,$33,$33,$2c,$30,$00,$00,$00
55
56 ;------------------------------------------------------------------------------
57 ; A table that contains values that must be transfered from the system zero
58 ; page into out zero page. Contains pairs of bytes, first one is the address
59 ; in the system ZP, second one is our ZP address. The table goes into page 2,
60 ; but is declared here, because it is needed earlier.
61
62 .SEGMENT        "PAGE2"
63
64 .proc   transfer_table
65
66         .byte   $CA, CURS_Y
67         .byte   $CB, CURS_X
68         .byte   $CC, graphmode
69         .byte   $D4, config
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  = $FF05
87
88 .org    $FECB
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 <> $FF2E)
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     #$01
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 $FFF6
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           ; crtc
218         .word   $da00           ; sid
219         .word   $db00           ; ipccia
220         .word   $dc00           ; cia
221         .word   $dd00           ; acia
222         .word   $de00           ; tpi1
223         .word   $df00           ; tpi2
224         .word   $ea29           ; ktab1
225         .word   $ea89           ; ktab2
226         .word   $eae9           ; ktab3
227         .word   $eb49           ; 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 ; Activate chained interrupt handlers, then enable interrupts.
349
350 Init:   lda     #.lobyte(__INTERRUPTOR_COUNT__*2)
351         sta     irqcount
352         cli
353
354 ; Call module constructors.
355
356         jsr     initlib
357
358 ; Push arguments and call main()
359
360         jsr     callmain
361
362 ; Call module destructors. This is also the _exit entry and the default entry
363 ; point for the break vector.
364
365 _exit:  pha                     ; Save the return code
366         jsr     donelib         ; Run module destructors
367         lda     #$00
368         sta     irqcount        ; Disable custom irq handlers
369
370 ; Address the system bank
371
372         lda     #$0F
373         sta     IndReg
374
375 ; Copy stuff back from our zeropage to the systems
376
377 .if 0
378         lda     #.sizeof(transfer_table)
379         sta     ktmp
380 @L0:    ldx     ktmp
381         ldy     transfer_table-2,x
382         lda     transfer_table-1,x
383         tax
384         lda     $00,x
385         sta     (sysp0),y
386         dec     ktmp
387         dec     ktmp
388         bne     @L0
389 .endif
390
391 ; Place the program return code into ST
392
393         pla
394         ldy     #$9C            ; ST
395         sta     (sysp0),y
396
397 ; Setup the welcome code at the stack bottom in the system bank.
398
399         ldy     #$FF
400         lda     (sysp1),y       ; Load system bank sp
401         tax
402         iny                     ; Y = 0
403         lda     #$58            ; CLI opcode
404         sta     (sysp1),y
405         iny
406         lda     #$60            ; RTS opcode
407         sta     (sysp1),y
408         lda     IndReg
409         sei
410         txs
411         jmp     Back
412
413 ; -------------------------------------------------------------------------
414 ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
415 ; easier chaining, we do handle the IRQs in the execution bank (instead of
416 ; passing them to the system bank).
417
418 ; This is the mapping of the active irq register of the 6525 (tpi1):
419 ;
420 ; Bit   7       6       5       4       3       2       1       0
421 ;                               |       |       |       |       ^ 50 Hz
422 ;                               |       |       |       ^ SRQ IEEE 488
423 ;                               |       |       ^ cia
424 ;                               |       ^ IRQB ext. Port
425 ;                               ^ acia
426
427 irq:    pha
428         txa
429         pha
430         tya
431         pha
432         lda     IndReg
433         pha
434         lda     ExecReg
435         sta     IndReg                  ; Be sure to address our segment
436         tsx
437         lda     $105,x                  ; Get the flags from the stack
438         and     #$10                    ; Test break flag
439         bne     dobrk
440
441 ; It's an IRQ
442
443         cld
444
445 ; Call chained IRQ handlers
446
447         ldy     irqcount
448         beq     irqskip
449         jsr     callirq_y               ; Call the functions
450
451 ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
452
453 irqskip:lda     #$0F
454         sta     IndReg
455         ldy     #TPI::AIR
456         lda     (tpi1),y                ; Interrupt Register 6525
457         beq     noirq
458
459 ; 50/60Hz interrupt
460
461         cmp     #%00000001              ; ticker irq?
462         bne     irqend
463         jsr     scnkey                  ; Poll the keyboard
464         jsr     UDTIM                   ; Bump the time
465
466 ; Done
467
468 irqend: ldy     #TPI::AIR
469         sta     (tpi1),y                ; Clear interrupt
470
471 noirq:  pla
472         sta     IndReg
473         pla
474         tay
475         pla
476         tax
477         pla
478 nmi:    rti
479
480 dobrk:  jmp     (BRKVec)
481
482 ; -------------------------------------------------------------------------
483 ; Data area.
484
485 .bss
486 irqcount:       .byte   0
487