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