2 ; Startup code for cc65 (CBM 600/700 version)
4 ; This must be the *first* file on the linker command line
9 .import condes, initlib, donelib
10 .import push0, callmain
11 .import __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
12 .import __IRQFUNC_TABLE__, __IRQFUNC_COUNT__
15 .include "zeropage.inc"
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.
28 ; Here's the basic program that's in the following lines:
35 ; 60 data 120,169,1,133,0
37 ; The machine program in the data lines is:
41 ; sta $00 <-- Switch to bank 1 after this command
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.
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
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.
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.
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:
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.
103 ; Hardware vectors, copied to $FFFA
109 .word nmi ; NMI vector
110 .word 0 ; Reset - not used
111 .word irq ; IRQ vector
114 ; Initializers for the extended zeropage. See extzp.s
132 ; The following code is part of the kernal call subroutine. It is copied
143 ; Save the old stack pointer from the system bank and setup our hw sp
146 stx spsave ; Save the system stackpointer
147 ldx #$FE ; Leave $1FF untouched for cross bank calls
148 txs ; Set up our own stack
150 ; Switch the indirect segment to the system bank
155 ; Initialize the extended zeropage
157 ldx #.sizeof(extzp)-1
163 ; Copy stuff from the system zeropage to ours
165 lda #.sizeof(transfer_table)
168 ldy transfer_table-2,x
169 lda transfer_table-1,x
177 ; Set the interrupt, NMI and other vectors
179 ldx #.sizeof(vectors)-1
181 sta $10000 - .sizeof(vectors),x
187 lda #.lobyte($FEB5 - .sizeof(callsysbank_15))
189 lda #.hibyte($FEB5 - .sizeof(callsysbank_15))
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.
197 ldy #.sizeof(callsysbank_15)-1 ; Copy the modified part
198 @L1: lda callsysbank_15,y
199 sta $FEB5 - .sizeof(callsysbank_15),y
203 lda #.lobyte($FEB5) ; Copy the ROM part
214 ; Setup the jump vector table
217 ldx #45-1 ; Number of vectors
218 @L3: lda #$20 ; JSR opcode
221 lda #.lobyte($FEB5 - .sizeof(callsysbank_15))
224 lda #.hibyte($FEB5 - .sizeof(callsysbank_15))
230 ; Copy the stack from the system bank into page 3
239 ; Set the indirect segment to bank we're executing in
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,
262 inc ptr1+1 ; Next page
266 ; Clear the remaining page
268 Z2: ldx #<__BSS_SIZE__
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.
282 ; Call module constructors, enable chained IRQs afterwards.
285 lda #.lobyte(__IRQFUNC_COUNT__*2)
292 ; Push arguments and call main()
296 ; Disable Call module destructors. This is also the _exit entry and the default entry
297 ; point for the break vector.
300 sta irqcount ; Disable custom irq handlers
301 jsr donelib ; Run module destructors
303 ; Address the system bank
308 ; Copy stuff back from our zeropage to the systems
311 lda #.sizeof(transfer_table)
314 ldy transfer_table-2,x
315 lda transfer_table-1,x
324 ; Copy back the old system bank stack contents
333 ; Setup the welcome code at the stack bottom in the system bank.
336 lda #$58 ; CLI opcode
339 lda #$60 ; RTS opcode
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).
348 ; This is the mapping of the active irq register of the 6525 (tpi1):
350 ; Bit 7 6 5 4 3 2 1 0
352 ; | | | ^ SRQ IEEE 488
365 sta IndReg ; Be sure to address our segment
367 lda $105,x ; Get the flags from the stack
368 and #$10 ; Test break flag
375 ; Call chained IRQ handlers
379 lda #<__IRQFUNC_TABLE__
380 ldx #>__IRQFUNC_TABLE__
381 jsr condes ; Call the functions
383 ; Done with chained IRQ handlers, check the TPI for IRQs and handle them
388 lda (tpi1),y ; Interrupt Register 6525
393 cmp #%00000001 ; ticker irq?
395 jsr scnkey ; Poll the keyboard
396 jsr UDTIM ; Bump the time
400 irqend: ldy #TPI::AIR
401 sta (tpi1),y ; Clear interrupt
414 ; -------------------------------------------------------------------------
419 BRKVec: .addr _exit ; BRK indirect vector
422 ; -------------------------------------------------------------------------