]> git.sur5r.net Git - cc65/blob - libsrc/apple2/crt0.s
Allow to run programs as ProDOS SYS files (beside BIN files). To do so detect at...
[cc65] / libsrc / apple2 / crt0.s
1 ;
2 ; Startup code for cc65 (Apple2 version)
3 ;
4
5         .export         _exit
6         .export         __STARTUP__ : absolute = 1      ; Mark as startup
7         .import         zerobss
8         .import         initlib, donelib
9         .import         callmain, callirq
10         .import         __RAM_START__ , __RAM_LAST__    ; Linker generated
11         .import         __MOVE_START__, __MOVE_LAST__   ; Linker generated
12         .import         __LC_START__  , __LC_LAST__     ; Linker generated
13         .import         __ZPSAVE_RUN__, __INIT_SIZE__   ; Linker generated
14         .import         __INTERRUPTOR_COUNT__           ; Linker generated
15
16         .include        "zeropage.inc"
17         .include        "apple2.inc"
18
19         .linecont       +
20
21 ; ------------------------------------------------------------------------
22
23         .segment        "EXEHDR"
24
25         .addr           __RAM_START__                   ; Start address
26         .word           __ZPSAVE_RUN__ - __RAM_START__ + \
27                         __MOVE_LAST__  - __MOVE_START__ ; Size
28
29 ; ------------------------------------------------------------------------
30
31         .segment        "STARTUP"
32
33         ; ProDOS TechRefMan, chapter 5.2.1:
34         ; "For maximum interrupt efficiency, a system program should not
35         ;  use more than the upper 3/4 of the stack."
36         ldx     #$FF
37         txs                     ; Init stack pointer
38
39         ; Switch in LC bank 2 for W/O
40         bit     $C081
41         bit     $C081
42
43         ; Set source start address
44         lda     #<(__ZPSAVE_RUN__ + __INIT_SIZE__)
45         ldy     #>(__ZPSAVE_RUN__ + __INIT_SIZE__)
46         sta     $9B
47         sty     $9C
48
49         ; Set source last address
50         lda     #<(__ZPSAVE_RUN__ + __INIT_SIZE__ + __LC_LAST__ - __LC_START__)
51         ldy     #>(__ZPSAVE_RUN__ + __INIT_SIZE__ + __LC_LAST__ - __LC_START__)
52         sta     $96
53         sty     $97
54
55         ; Set destination last address
56         lda     #<__LC_LAST__
57         ldy     #>__LC_LAST__
58         sta     $94
59         sty     $95
60
61         ; Call into Applesoft Block Transfer Utility - which handles zero
62         ; sized blocks well - to move content of the LC memory area
63         jsr     $D396           ; BLTU + 3
64
65         ; Set source start address
66         lda     #<__ZPSAVE_RUN__
67         ldy     #>__ZPSAVE_RUN__
68         sta     $9B
69         sty     $9C
70
71         ; Set source last address
72         lda     #<(__ZPSAVE_RUN__ + __INIT_SIZE__)
73         ldy     #>(__ZPSAVE_RUN__ + __INIT_SIZE__)
74         sta     $96
75         sty     $97
76
77         ; Set destination last address
78         lda     #<__RAM_LAST__
79         ldy     #>__RAM_LAST__
80         sta     $94
81         sty     $95
82
83         ; Call into Applesoft Block Transfer Utility - which handles moving
84         ; overlapping blocks upwards well - to move the INIT segment
85         jsr     $D396           ; BLTU + 3
86
87         ; Delegate all further processing to keep the STARTUP segment small
88         jsr     init
89
90         ; Avoid re-entrance of donelib. This is also the _exit entry
91 _exit:  ldx     #<exit
92         lda     #>exit
93         jsr     reset           ; Setup RESET vector
94
95         ; Switch in ROM in case it wasn't already switched in by a RESET
96         bit     $C082
97
98         ; Call module destructors
99         jsr     donelib
100
101         ; Check for valid interrupt vector table entry number
102         lda     int_num
103         beq     exit
104
105         ; Deallocate interrupt vector table entry
106         dec     i_param         ; Adjust parameter count
107         jsr     $BF00           ; MLI call entry point
108         .byte   $41             ; Dealloc interrupt
109         .addr   i_param
110
111         ; Restore the original RESET vector
112 exit:   ldx     #$02
113 :       lda     rvsave,x
114         sta     SOFTEV,x
115         dex
116         bpl     :-
117
118         ; Copy back the zero page stuff
119         ldx     #zpspace-1
120 :       lda     zpsave,x
121         sta     sp,x
122         dex
123         bpl     :-
124
125         ; ProDOS TechRefMan, chapter 5.2.1:
126         ; "System programs should set the stack pointer to $FF at the
127         ;  warm-start entry point."
128         ldx     #$FF
129         txs                     ; Re-init stack pointer
130
131         ; We're done
132         jmp     (done)
133
134 ; ------------------------------------------------------------------------
135
136         .segment        "INIT"
137
138         ; Save the zero page locations we need
139 init:   ldx     #zpspace-1
140 :       lda     sp,x
141         sta     zpsave,x
142         dex
143         bpl     :-
144
145         ; Clear the BSS data
146         jsr     zerobss
147
148         ; Save the original RESET vector
149         ldx     #$02
150 :       lda     SOFTEV,x
151         sta     rvsave,x
152         dex
153         bpl     :-
154
155         ; ProDOS TechRefMan, chapter 5.3.5:
156         ; "Your system program should place in the RESET vector the
157         ;  address of a routine that ... closes the files."
158         ldx     #<_exit
159         lda     #>_exit
160         jsr     reset           ; Setup RESET vector
161
162         ; Check for ProDOS
163         ldy     $BF00           ; MLI call entry point
164         cpy     #$4C            ; Is MLI present? (JMP opcode)
165         bne     basic
166         
167         ; Check ProDOS system bit map
168         lda     $BF6F           ; protection for pages $B8 - $BF
169         cmp     #%00000001      ; exactly page $BF is protected
170         bne     basic
171
172         ; No BASIC.SYSTEM so quit to ProDOS dispatcher instead
173         lda     #<quit
174         ldx     #>quit
175         sta     done
176         stx     done+1
177         
178         ; No BASIC.SYSTEM so use addr of ProDOS system global page
179         lda     #<$BF00
180         ldx     #>$BF00
181         bne     :+              ; Branch always
182
183         ; Get highest available mem addr from BASIC interpreter
184 basic:  lda     HIMEM
185         ldx     HIMEM+1
186
187         ; Setup the C stack
188 :       sta     sp
189         stx     sp+1
190
191         ; Check for interruptors
192         lda     #<__INTERRUPTOR_COUNT__
193         beq     :+
194
195         ; Check for ProDOS
196         cpy     #$4C            ; Is MLI present? (JMP opcode)
197         bne     prterr
198
199         ; Allocate interrupt vector table entry
200         jsr     $BF00           ; MLI call entry point
201         .byte   $40             ; Alloc interrupt
202         .addr   i_param
203         bcs     prterr
204
205         ; Enable interrupts as old ProDOS versions (i.e. 1.1.1)
206         ; jump to SYS and BIN programs with interrupts disabled
207         cli
208
209         ; Call module constructors
210 :       jsr     initlib
211
212         ; Switch in LC bank 2 for R/O
213         bit     $C080
214
215         ; Push arguments and call main()
216         jmp     callmain
217
218         ; Print error message and return
219 prterr: ldx     #msglen-1
220 :       lda     errmsg,x
221         jsr     $FDED           ; COUT
222         dex
223         bpl     :-
224         rts
225
226 errmsg: .ifdef  __APPLE2ENH__
227         .byte   $8D,     't'|$80, 'p'|$80, 'u'|$80, 'r'|$80, 'r'|$80
228         .byte   'e'|$80, 't'|$80, 'n'|$80, 'i'|$80, ' '|$80, 'c'|$80
229         .byte   'o'|$80, 'l'|$80, 'l'|$80, 'a'|$80, ' '|$80, 'o'|$80
230         .byte   't'|$80, ' '|$80, 'd'|$80, 'e'|$80, 'l'|$80, 'i'|$80
231         .byte   'a'|$80, 'F'|$80, $8D
232         .else
233         .byte   $8D,     'T'|$80, 'P'|$80, 'U'|$80, 'R'|$80, 'R'|$80
234         .byte   'E'|$80, 'T'|$80, 'N'|$80, 'I'|$80, ' '|$80, 'C'|$80
235         .byte   'O'|$80, 'L'|$80, 'L'|$80, 'A'|$80, ' '|$80, 'O'|$80
236         .byte   'T'|$80, ' '|$80, 'D'|$80, 'E'|$80, 'L'|$80, 'I'|$80
237         .byte   'A'|$80, 'F'|$80, $8D
238         .endif
239
240 msglen = * - errmsg
241
242 ; ------------------------------------------------------------------------
243
244         .segment        "LOWCODE"
245
246         ; ProDOS TechRefMan, chapter 6.2:
247         ; "Each installed routine must begin with a CLD instruction."
248 intptr: cld
249
250         ; Call interruptors and check for success
251         jsr     callirq
252         bcc     :+
253
254         ; ProDOS TechRefMan, chapter 6.2:
255         ; "When the routine that can process the interrupt is called, it
256         ;  should ... return (via an RTS) with the carry flag clear."
257         clc
258         rts
259
260         ; ProDOS TechRefMan, chapter 6.2:
261         ; "When a routine that cannot process the interrupt is called,
262         ;  it should return (via an RTS) with the cary flag set ..."
263 :       sec
264         rts
265
266         ; Setup RESET vector
267 reset:  stx     SOFTEV
268         sta     SOFTEV+1
269         eor     #$A5
270         sta     PWREDUP
271         rts
272
273         ; Quit to ProDOS dispatcher
274 quit:   jsr     $BF00           ; MLI call entry point
275         .byte   $65             ; Quit
276         .word   q_param
277
278 ; ------------------------------------------------------------------------
279
280         .rodata
281
282         ; MLI parameter list for quit        
283 q_param:.byte   $04             ; param_count
284         .byte   $00             ; quit_type
285         .word   $0000           ; reserved
286         .byte   $00             ; reserved
287         .word   $0000           ; reserved
288         
289 ; ------------------------------------------------------------------------
290
291         .data
292
293         ; MLI parameter list for (de)alloc interrupt
294 i_param:.byte   $02             ; param_count
295 int_num:.byte   $00             ; int_num
296         .addr   intptr          ; int_code
297
298         ; Location to jump to when we're done
299 done:   .addr   DOSWARM
300
301 ; ------------------------------------------------------------------------
302
303         .segment        "ZPSAVE"
304
305 zpsave: .res    zpspace
306
307 ; ------------------------------------------------------------------------
308
309         .bss
310
311 rvsave: .res    3