]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/crt0.s
Made Olivers devnum patch (r4588) work with the PET-II models. On these
[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        "EXEHDR"
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   $9F, DEVNUM
67         .byte   $CA, CURS_Y
68         .byte   $CB, CURS_X
69         .byte   $CC, graphmode
70         .byte   $D4, config
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  = $FF05
88
89 .org    $FECB
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 <> $FF2E)
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     #$01
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 $FFF6
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           ; crtc
219         .word   $da00           ; sid
220         .word   $db00           ; ipccia
221         .word   $dc00           ; cia
222         .word   $dd00           ; acia
223         .word   $de00           ; tpi1
224         .word   $df00           ; tpi2
225         .word   $ea29           ; ktab1
226         .word   $ea89           ; ktab2
227         .word   $eae9           ; ktab3
228         .word   $eb49           ; 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 ; Activate chained interrupt handlers, then enable interrupts.
350
351 Init:   lda     #.lobyte(__INTERRUPTOR_COUNT__*2)
352         sta     irqcount
353         cli
354
355 ; Call module constructors.
356
357         jsr     initlib
358
359 ; Push arguments and call main()
360
361         jsr     callmain
362
363 ; Call module destructors. This is also the _exit entry and the default entry
364 ; point for the break vector.
365
366 _exit:  pha                     ; Save the return code
367         jsr     donelib         ; Run module destructors
368         lda     #$00
369         sta     irqcount        ; Disable custom irq handlers
370
371 ; Address the system bank
372
373         lda     #$0F
374         sta     IndReg
375
376 ; Copy stuff back from our zeropage to the systems
377
378 .if 0
379         lda     #.sizeof(transfer_table)
380         sta     ktmp
381 @L0:    ldx     ktmp
382         ldy     transfer_table-2,x
383         lda     transfer_table-1,x
384         tax
385         lda     $00,x
386         sta     (sysp0),y
387         dec     ktmp
388         dec     ktmp
389         bne     @L0
390 .endif
391
392 ; Place the program return code into ST
393
394         pla
395         ldy     #$9C            ; ST
396         sta     (sysp0),y
397
398 ; Setup the welcome code at the stack bottom in the system bank.
399
400         ldy     #$FF
401         lda     (sysp1),y       ; Load system bank sp
402         tax
403         iny                     ; Y = 0
404         lda     #$58            ; CLI opcode
405         sta     (sysp1),y
406         iny
407         lda     #$60            ; RTS opcode
408         sta     (sysp1),y
409         lda     IndReg
410         sei
411         txs
412         jmp     Back
413
414 ; -------------------------------------------------------------------------
415 ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
416 ; easier chaining, we do handle the IRQs in the execution bank (instead of
417 ; passing them to the system bank).
418
419 ; This is the mapping of the active irq register of the 6525 (tpi1):
420 ;
421 ; Bit   7       6       5       4       3       2       1       0
422 ;                               |       |       |       |       ^ 50 Hz
423 ;                               |       |       |       ^ SRQ IEEE 488
424 ;                               |       |       ^ cia
425 ;                               |       ^ IRQB ext. Port
426 ;                               ^ acia
427
428 irq:    pha
429         txa
430         pha
431         tya
432         pha
433         lda     IndReg
434         pha
435         lda     ExecReg
436         sta     IndReg                  ; Be sure to address our segment
437         tsx
438         lda     $105,x                  ; Get the flags from the stack
439         and     #$10                    ; Test break flag
440         bne     dobrk
441
442 ; It's an IRQ
443
444         cld
445
446 ; Call chained IRQ handlers
447
448         ldy     irqcount
449         beq     irqskip
450         jsr     callirq_y               ; Call the functions
451
452 ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
453
454 irqskip:lda     #$0F
455         sta     IndReg
456         ldy     #TPI::AIR
457         lda     (tpi1),y                ; Interrupt Register 6525
458         beq     noirq
459
460 ; 50/60Hz interrupt
461
462         cmp     #%00000001              ; ticker irq?
463         bne     irqend
464         jsr     scnkey                  ; Poll the keyboard
465         jsr     UDTIM                   ; Bump the time
466
467 ; Done
468
469 irqend: ldy     #TPI::AIR
470         sta     (tpi1),y                ; Clear interrupt
471
472 noirq:  pla
473         sta     IndReg
474         pla
475         tay
476         pla
477         tax
478         pla
479 nmi:    rti
480
481 dobrk:  jmp     (BRKVec)
482
483 ; -------------------------------------------------------------------------
484 ; Data area.
485
486 .bss
487 irqcount:       .byte   0
488