]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/crt0.s
Atari: clock_gettime() and clock_settime() implementations
[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 ; The BASIC header and a small BASIC program. Since it isn't 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 complex not only because of the jumping from one bank
43 ; into another. but also because we want to save memory; and because of
44 ; that, 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 transferred from the system zero-
58 ; page into our 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 ; (We use .proc because we need both a label and a scope.)
65
66 .proc   transfer_table
67
68         .byte   $9F, DEVNUM
69         .byte   $CA, CURS_Y
70         .byte   $CB, CURS_X
71         .byte   $CC, graphmode
72         .byte   $D4, config
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  := $FF05        ; In bank 15 ROM
90
91 .org    $FECB
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 <> $FF2E)
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     #$01
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 $FFF6
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 zero-page. See "extzp.s".
216
217 .proc   extzp
218         .word   $0100           ; sysp1
219         .word   $0300           ; sysp3
220         .word   $d800           ; crtc
221         .word   $da00           ; sid
222         .word   $db00           ; ipccia
223         .word   $dc00           ; cia
224         .word   $dd00           ; acia
225         .word   $de00           ; tpi1
226         .word   $df00           ; tpi2
227         .word   $ea29           ; ktab1
228         .word   $ea89           ; ktab2
229         .word   $eae9           ; ktab3
230         .word   $eb49           ; 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 zero-page.
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, set up 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 zero-page 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 ; Set up the C stack.
278
279         lda     #.lobyte(callbank15::entry)
280         sta     sp
281         lda     #.hibyte(callbank15::entry)
282         sta     sp+1
283
284 ; Set up 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 ; Set up 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 the bank that we're executing in.
309
310         lda     ExecReg
311         sta     IndReg
312
313 ; Zero the BSS segment. We will do that here instead of 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 ; Activate the chained interrupt handlers; then, enable interrupts.
352
353 Init:   lda     #.lobyte(__INTERRUPTOR_COUNT__*2)
354         sta     irqcount
355         cli
356
357 ; Call module constructors.
358
359         jsr     initlib
360
361 ; Push the command-line arguments; and, call main().
362
363         jsr     callmain
364
365 ; Call the module destructors. This is also the exit() entry and the default entry
366 ; point for the break vector.
367
368 _exit:  pha                     ; Save the return code
369         jsr     donelib         ; Run module destructors
370         lda     #$00
371         sta     irqcount        ; Disable custom irq handlers
372
373 ; Address the system bank.
374
375         lda     #$0F
376         sta     IndReg
377
378 ; Copy stuff back from our zero-page to the system's.
379
380 .if 0
381         lda     #.sizeof(transfer_table)
382         sta     ktmp
383 @L0:    ldx     ktmp
384         ldy     transfer_table-2,x
385         lda     transfer_table-1,x
386         tax
387         lda     $00,x
388         sta     (sysp0),y
389         dec     ktmp
390         dec     ktmp
391         bne     @L0
392 .endif
393
394 ; Place the program return code into BASIC's status variable.
395
396         pla
397         ldy     #$9C            ; ST
398         sta     (sysp0),y
399
400 ; Set up the welcome code at the stack bottom in the system bank.
401
402         ldy     #$FF
403         lda     (sysp1),y       ; Load system bank sp
404         tax
405         iny                     ; Y = 0
406         lda     #$58            ; CLI opcode
407         sta     (sysp1),y
408         iny
409         lda     #$60            ; RTS opcode
410         sta     (sysp1),y
411         lda     IndReg
412         sei
413         txs
414         jmp     Back
415
416 ; -------------------------------------------------------------------------
417 ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
418 ; easier chaining, we do handle the IRQs in the execution bank (instead of
419 ; passing them to the system bank).
420
421 ; This is the mapping of the active IRQ register of the 6525 (tpi1):
422 ;
423 ; Bit   7       6       5       4       3       2       1       0
424 ;                               |       |       |       |       ^ 50 Hz.
425 ;                               |       |       |       ^ SRQ IEEE 488
426 ;                               |       |       ^ CIA
427 ;                               |       ^ IRQB ext. Port
428 ;                               ^ ACIA
429
430 irq:    pha
431         txa
432         pha
433         tya
434         pha
435         lda     IndReg
436         pha
437         lda     ExecReg
438         sta     IndReg          ; Be sure to address our segment
439         tsx
440         lda     $105,x          ; Get the flags from the stack
441         and     #$10            ; Test break flag
442         bne     dobrk
443
444 ; It's an IRQ.
445
446         cld
447
448 ; Call the chained IRQ handlers.
449
450         ldy     irqcount
451         beq     irqskip
452         jsr     callirq_y       ; Call the functions
453
454 ; Done with the chained IRQ handlers; check the TPI for IRQs, and handle them.
455
456 irqskip:lda     #$0F
457         sta     IndReg
458         ldy     #TPI::AIR
459         lda     (tpi1),y        ; Interrupt Register 6525
460         beq     noirq
461
462 ; 50/60Hz. interrupt
463
464         cmp     #%00000001      ; ticker IRQ?
465         bne     irqend
466         jsr     scnkey          ; Poll the keyboard
467         jsr     UDTIM           ; Bump the time
468
469 ; Done.
470
471 irqend: ldy     #TPI::AIR
472         sta     (tpi1),y        ; Clear interrupt
473
474 noirq:  pla
475         sta     IndReg
476         pla
477         tay
478         pla
479         tax
480         pla
481 nmi:    rti
482
483 dobrk:  jmp     (BRKVec)
484
485 ; -------------------------------------------------------------------------
486 ; Data area
487
488 .bss
489 irqcount:       .byte   0