]> git.sur5r.net Git - cc65/blob - libsrc/cbm610/crt0.s
Design change: Do keyboard polling internally without calling the kernal
[cc65] / libsrc / cbm610 / crt0.s
1 ;
2 ; Startup code for cc65 (CBM 600/700 version)
3 ;
4 ; This must be the *first* file on the linker command line
5 ;
6
7         .export         _exit, BRKVec
8
9         .import         condes, initlib, donelib
10         .import         push0, callmain
11         .import         __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
12         .import         __IRQFUNC_TABLE__, __IRQFUNC_COUNT__
13         .import         scnkey, UDTIM
14
15         .include        "zeropage.inc"
16         .include        "extzp.inc"
17         .include        "cbm610.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 1, 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,1,133,0
36 ;
37 ; The machine program in the data lines is:
38 ;
39 ; sei
40 ; lda     #$01
41 ; sta     $00           <-- Switch to bank 1 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        "BASICHDR"
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,$31,$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 out 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   $CA, CURS_Y
68         .byte   $CB, CURS_X
69         .byte   $CC, graphmode
70         .byte   $D4, config
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     #$01
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           ; crtc
120         .word   $da00           ; sid
121         .word   $db00           ; ipccia
122         .word   $dc00           ; cia
123         .word   $dd00           ; acia
124         .word   $de00           ; tpi1
125         .word   $df00           ; tpi2
126         .word   $ea29           ; ktab1
127         .word   $ea89           ; ktab2
128         .word   $eae9           ; ktab3
129         .word   $eb49           ; 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($FEB5 - .sizeof(callsysbank_15))
188         sta     sp
189         lda     #.hibyte($FEB5 - .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 $FEB5 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     $FEB5 - .sizeof(callsysbank_15),y
200         dey
201         bpl     @L1
202
203         lda     #.lobyte($FEB5)                 ; Copy the ROM part
204         sta     ptr1
205         lda     #.hibyte($FEB5)
206         sta     ptr1+1
207         ldy     #$00
208 @L2:    lda     (ptr1),y
209         sta     $FEB5,y
210         iny
211         cpy     #<($FF6F-$FEB5)
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($FEB5 - .sizeof(callsysbank_15))
222         sta     $FF6F,y
223         iny
224         lda     #.hibyte($FEB5 - .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 ; Call module constructors, enable chained IRQs afterwards.
283
284 Init:   jsr     initlib
285         lda     #.lobyte(__IRQFUNC_COUNT__*2)
286         sta     irqcount
287
288 ; Enable interrupts
289
290         cli
291
292 ; Push arguments and call main()
293
294         jsr     callmain
295
296 ; Disable Call module destructors. This is also the _exit entry and the default entry
297 ; point for the break vector.
298
299 _exit:  lda     #$00
300         sta     irqcount        ; Disable custom irq handlers
301         jsr     donelib         ; Run module destructors
302
303 ; Address the system bank
304
305         lda     #$0F
306         sta     IndReg
307
308 ; Copy stuff back from our zeropage to the systems
309      
310 .if 0
311         lda     #.sizeof(transfer_table)
312         sta     ktmp
313 @L0:    ldx     ktmp
314         ldy     transfer_table-2,x
315         lda     transfer_table-1,x
316         tax
317         lda     $00,x
318         sta     (sysp0),y
319         dec     ktmp
320         dec     ktmp
321         bne     @L0
322 .endif     
323
324 ; Copy back the old system bank stack contents
325
326         ldy     #$FF
327 @L1:    lda     $300,y
328         sta     (sysp1),y
329         dey
330         cpy     spsave
331         bne     @L1
332
333 ; Setup the welcome code at the stack bottom in the system bank.
334
335         ldy     #$00
336         lda     #$58            ; CLI opcode
337         sta     (sysp1),y
338         iny
339         lda     #$60            ; RTS opcode
340         sta     (sysp1),y
341         jmp     Back
342
343 ; -------------------------------------------------------------------------
344 ; The IRQ handler goes into PAGE2. For performance reasons, and to allow
345 ; easier chaining, we do handle the IRQs in the execution bank (instead of
346 ; passing them to the system bank).
347
348 ; This is the mapping of the active irq register of the 6525 (tpi1):
349 ;
350 ; Bit   7       6       5       4       3       2       1       0
351 ;                               |       |       |       |       ^ 50 Hz
352 ;                               |       |       |       ^ SRQ IEEE 488
353 ;                               |       |       ^ cia
354 ;                               |       ^ IRQB ext. Port
355 ;                               ^ acia
356
357 irq:    pha
358         txa
359         pha
360         tya
361         pha
362         lda     IndReg
363         pha
364         lda     ExecReg
365         sta     IndReg                  ; Be sure to address our segment
366         tsx
367         lda     $105,x                  ; Get the flags from the stack
368         and     #$10                    ; Test break flag
369         bne     dobrk
370
371 ; It's an IRQ
372
373         cld
374
375 ; Call chained IRQ handlers
376
377         ldy     irqcount
378         beq     irqskip
379         lda     #<__IRQFUNC_TABLE__
380         ldx     #>__IRQFUNC_TABLE__
381         jsr     condes                  ; Call the functions
382
383 ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
384
385 irqskip:lda     #$0F
386         sta     IndReg
387         ldy     #TPI::AIR
388         lda     (tpi1),y                ; Interrupt Register 6525
389         beq     noirq
390
391 ; 50/60Hz interrupt
392
393         cmp     #%00000001              ; ticker irq?
394         bne     irqend
395         jsr     scnkey                  ; Poll the keyboard
396         jsr     UDTIM                   ; Bump the time
397
398 ; Done
399
400 irqend: ldy     #TPI::AIR
401         sta     (tpi1),y                ; Clear interrupt
402
403 noirq:  pla
404         sta     IndReg
405         pla
406         tay
407         pla
408         tax
409         pla
410 nmi:    rti
411
412 dobrk:  jmp     (BRKVec)
413
414 ; -------------------------------------------------------------------------
415 ; Page 3
416
417 .segment        "PAGE3"
418
419 BRKVec: .addr   _exit           ; BRK indirect vector
420
421
422 ; -------------------------------------------------------------------------
423 ; Data area.
424
425 .data
426 spsave: .res    1
427
428 .bss
429 irqcount:       .byte   0
430