]> git.sur5r.net Git - cc65/blob - libsrc/c128/crt0.s
4db5e618c288b4bcd74b4e355cf2f47b3a19201e
[cc65] / libsrc / c128 / crt0.s
1 ;
2 ; Startup code for cc65 (C128 version)
3 ;
4 ; This must be the *first* file on the linker command line
5 ;
6
7         .export         _exit
8         .export         BRKStub, BRKOld, BRKInd
9         .import         condes, initlib, donelib
10         .import         initconio, doneconio, zerobss
11         .import         push0, _main
12         .import         __IRQFUNC_TABLE__, __IRQFUNC_COUNT__
13         .import         __RAM_START__, __RAM_SIZE__
14
15         .include        "c128.inc"
16         .include        "../cbm/cbm.inc"
17
18 ; ------------------------------------------------------------------------
19 ; Constants
20
21 IRQInd          = $2FD  ; JMP $0000 - used as indirect IRQ vector
22
23 ; ------------------------------------------------------------------------
24 ; Define and export the ZP variables for the C64 runtime
25
26         .exportzp       sp, sreg, regsave
27         .exportzp       ptr1, ptr2, ptr3, ptr4
28         .exportzp       tmp1, tmp2, tmp3, tmp4
29         .exportzp       regbank, zpspace
30
31 .zeropage
32
33 zpstart = *
34 sp:             .res    2       ; Stack pointer
35 sreg:           .res    2       ; Secondary register/high 16 bit for longs
36 regsave:        .res    2       ; slot to save/restore (E)AX into
37 ptr1:           .res    2
38 ptr2:           .res    2
39 ptr3:           .res    2
40 ptr4:           .res    2
41 tmp1:           .res    1
42 tmp2:           .res    1
43 tmp3:           .res    1
44 tmp4:           .res    1
45 regbank:        .res    6       ; 6 byte register bank
46
47 zpspace = * - zpstart           ; Zero page space allocated
48
49 ; Place the startup code in a special segment to cope with the quirks of
50 ; c128 banking. Do also create an empty segment named "NMI" to avoid
51 ; warnings if the rs232 routines are not used.
52
53 .segment        "NMI"
54 ; empty
55
56 .segment        "STARTUP"
57
58 ; ------------------------------------------------------------------------
59 ; BASIC header with a SYS call
60
61         .org    $1BFF
62         .word   Head            ; Load address
63 Head:   .word   @Next
64         .word   1000            ; Line number
65         .byte   $9E,"7181"      ; SYS 7181
66         .byte   $00             ; End of BASIC line
67 @Next:  .word   0               ; BASIC end marker
68         .reloc
69
70 ; ------------------------------------------------------------------------
71 ; Actual code
72
73 ; Close open files
74
75         jsr     CLRCH
76
77 ; Switch to the second charset
78
79         lda     #14
80         jsr     BSOUT
81
82 ; Before doing anything else, we have to setup our banking configuration.
83 ; Otherwise just the lowest 16K are actually RAM. Writing through the ROM
84 ; to the underlying RAM works, but it is bad style.
85
86         lda     MMU_CR          ; Get current memory configuration...
87         pha                     ; ...and save it for later
88         lda     #CC65_MMU_CFG   ; Bank0 with kernal ROM
89         sta     MMU_CR
90
91 ; Save the zero page locations we need
92
93         ldx     #zpspace-1
94 L1:     lda     sp,x
95         sta     zpsave,x
96         dex
97         bpl     L1
98
99 ; Clear the BSS data
100
101         jsr     zerobss
102
103 ; Save system stuff and setup the stack
104
105         pla                     ; Get MMU setting
106         sta     mmusave
107
108         tsx
109         stx     spsave          ; Save the system stack pointer
110
111         lda     #<(__RAM_START__ + __RAM_SIZE__)
112         sta     sp
113         lda     #>(__RAM_START__ + __RAM_SIZE__)
114         sta     sp+1            ; Set argument stack ptr
115
116 ; Call module constructors
117
118         jsr     initlib
119
120 ; Initialize conio stuff
121
122         jsr     initconio
123
124 ; If we have IRQ functions, chain our stub into the IRQ vector
125
126         lda     #<__IRQFUNC_COUNT__
127         beq     NoIRQ1
128         lda     IRQVec
129         ldx     IRQVec+1
130         sta     IRQInd+1
131         stx     IRQInd+2
132         lda     #<IRQStub
133         ldx     #>IRQStub
134         sei
135         sta     IRQVec
136         stx     IRQVec+1
137         cli
138
139 ; Pass an empty command line
140
141 NoIRQ1: jsr     push0           ; argc
142         jsr     push0           ; argv
143
144         ldy     #4              ; Argument size
145         jsr     _main           ; call the users code
146
147 ; This is also the _exit entry. Reset the IRQ vector if we chained it.
148
149 _exit:  lda     #<__IRQFUNC_COUNT__
150         beq     NoIRQ2
151         lda     IRQInd+1
152         ldx     IRQInd+2
153         sei
154         sta     IRQVec
155         stx     IRQVec+1
156         cli
157
158 ; Run module destructors
159
160 NoIRQ2: jsr     donelib
161
162 ; Reset the conio stuff
163
164         jsr     doneconio
165
166 ; Reset the stack
167
168         ldx     spsave
169         txs
170
171 ; Copy back the zero page stuff
172
173         ldx     #zpspace-1
174 L2:     lda     zpsave,x
175         sta     sp,x
176         dex
177         bpl     L2
178
179 ; Reset the memory configuration
180
181         lda     mmusave
182         sta     MMU_CR
183
184 ; Done, restore kernal vectors in an attempt to cleanup
185
186         jmp     RESTOR
187
188 ; ------------------------------------------------------------------------
189 ; The C128 has ROM parallel to the RAM starting from $4000. The startup code
190 ; above will change this setting so that we have RAM from $0000-$BFFF. This
191 ; works quite well with the exception of interrupts: The interrupt handler
192 ; is in ROM, and the ROM switches back to the ROM configuration, which means
193 ; that parts of our program may not be accessible. Since the crt0 module is
194 ; the first module in the program, it will always be below $4000 and always
195 ; in RAM. So we place several short stubs here that switch back our ROM
196 ; config before calling our user defined handlers. These stubs are only
197 ; used if any other code uses the interrupt or break vectors. They are dead
198 ; code otherwise, but since there is no other way to keep them in low memory,
199 ; they have to go here.
200
201 IRQStub:
202         cld                             ; Just to be sure
203         lda     MMU_CR                  ; Get old register value
204         pha                             ; And save on stack
205         lda     #CC65_MMU_CFG           ; Bank 0 with kernal ROM
206         sta     MMU_CR
207         ldy     #<(__IRQFUNC_COUNT__*2)
208         lda     #<__IRQFUNC_TABLE__
209         ldx     #>__IRQFUNC_TABLE__
210         jsr     condes                  ; Call the functions
211         pla                             ; Get old register value
212         sta     MMU_CR
213         jmp     IRQInd                  ; Jump to the save IRQ vector
214
215
216 BRKStub:
217         pla                             ; Get original MMU_CR value
218         sta     MMU_CR                  ; And set it
219         jmp     BRKInd                  ; Jump indirect to break
220
221
222 ; ------------------------------------------------------------------------
223 ; Data
224
225 .data
226 zpsave: .res    zpspace
227
228 ; Old break vector preceeded by a jump opcode
229 BRKOld: jmp     $0000
230
231 ; Indirect vectors preceeded by a jump opcode
232 BRKInd: jmp     $0000
233
234 .bss
235 spsave: .res    1
236 mmusave:.res    1
237
238
239