1
0
mirror of https://github.com/cc65/cc65.git synced 2024-07-05 06:28:57 +00:00
cc65/libsrc/cbm610/crt0.s
cuz a7042ddbe4 Complete redesign of the CBM610 support.
Use wrappers to call the kernal in bank 15 instead of implementing kernal
functionality within the cc65 libs (eats performance but is much smaller and
simpler).
Improved startup/shutdown code allows a return to the system bank without
calling the BASIC cold start vector.


git-svn-id: svn://svn.cc65.org/cc65/trunk@2793 b7a2c559-68d2-44c3-8de9-860c34a00d81
2003-12-20 23:13:26 +00:00

402 lines
9.9 KiB
ArmAsm

;
; Startup code for cc65 (CBM 600/700 version)
;
; This must be the *first* file on the linker command line
;
.export _exit, BRKVec, UDTIM
.import condes, initlib, donelib
.import push0, callmain
.import __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
.import __IRQFUNC_TABLE__, __IRQFUNC_COUNT__
.import SCNKEY
.include "zeropage.inc"
.include "extzp.inc"
.include "cbm610.inc"
; ------------------------------------------------------------------------
; BASIC header and a small BASIC program. Since it is not possible to start
; programs in other banks using SYS, the BASIC program will write a small
; machine code program into memory at $100 and start that machine code
; program. The machine code program will then start the machine language
; code in bank 1, which will initialize the system by copying stuff from
; the system bank, and start the application.
;
; Here's the basic program that's in the following lines:
;
; 10 for i=0 to 4
; 20 read j
; 30 poke 256+i,j
; 40 next i
; 50 sys 256
; 60 data 120,169,1,133,0
;
; The machine program in the data lines is:
;
; sei
; lda #$01
; sta $00 <-- Switch to bank 1 after this command
;
; Initialization is not only complex because of the jumping from one bank
; into another. but also because we want to save memory, and because of
; this, we will use the system memory ($00-$3FF) for initialization stuff
; that is overwritten later.
;
.segment "BASICHDR"
.byte $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00
.byte $19,$00,$14,$00,$87,$20,$4a,$00,$27,$00,$1e,$00,$97,$20,$32,$35
.byte $36,$aa,$49,$2c,$4a,$00,$2f,$00,$28,$00,$82,$20,$49,$00,$39,$00
.byte $32,$00,$9e,$20,$32,$35,$36,$00,$4f,$00,$3c,$00,$83,$20,$31,$32
.byte $30,$2c,$31,$36,$39,$2c,$31,$2c,$31,$33,$33,$2c,$30,$00,$00,$00
;------------------------------------------------------------------------------
; The code in the target bank when switching back will be put at the bottom
; of the stack. We will jump here to switch segments. The range $F2..$FF is
; not used by any kernal routine.
.segment "STARTUP"
Back: sei
ldx spsave
txs
lda IndReg
sta ExecReg
;------------------------------------------------------------------------------
; We are at $100 now. The following snippet is a copy of the code that is poked
; in the system bank memory by the basic header program, it's only for
; documentation and not actually used here:
sei
lda #$01
sta ExecReg
; This is the actual starting point of our code after switching banks for
; startup. Beware: The following code will get overwritten as soon as we
; use the stack (since it's in page 1)! We jump to another location, since
; we need some space for subroutines that aren't used later.
jmp Origin
; Hardware vectors, copied to $FFFA
.proc vectors
sta ExecReg
rts
nop
.word nmi ; NMI vector
.word 0 ; Reset - not used
.word irq ; IRQ vector
.endproc
; Initializers for the extended zeropage. See extzp.s
.proc extzp
.word $0100 ; sysp1
.word $0300 ; sysp3
.word $d800 ; crtc
.word $da00 ; sid
.word $db00 ; ipccia
.word $dc00 ; cia
.word $dd00 ; acia
.word $de00 ; tpi1
.word $df00 ; tpi2
.word $ea29 ; ktab1
.word $ea89 ; ktab2
.word $eae9 ; ktab3
.word $eb49 ; ktab4
.endproc
; The following code is part of the kernal call subroutine. It is copied
; to $FFAE
.proc callsysbank_15
php
pha
lda #$0F ; Bank 15
sta IndReg
sei
.endproc
; Save the old stack pointer from the system bank and setup our hw sp
Origin: tsx
stx spsave ; Save the system stackpointer
ldx #$FE ; Leave $1FF untouched for cross bank calls
txs ; Set up our own stack
; Initialize the extended zeropage
ldx #.sizeof(extzp)-1
L1: lda extzp,x
sta <__EXTZP_RUN__,x
dex
bpl L1
; Set the interrupt, NMI and other vectors
ldy #.sizeof(vectors)-1
L2: lda vectors,y
sta $10000 - .sizeof(vectors),y
dey
bpl L2
; Switch the indirect segment to the system bank
lda #$0F
sta IndReg
; Setup the C stack
lda #.lobyte($FEB5 - .sizeof(callsysbank_15))
sta sp
lda #.hibyte($FEB5 - .sizeof(callsysbank_15))
sta sp+1
; Setup the subroutine and jump vector table that redirects kernal calls to
; the system bank. Copy the bank switch routines starting at $FEB5 from the
; system bank into the current bank.
ldy #.sizeof(callsysbank_15)-1 ; Copy the modified part
@L1: lda callsysbank_15,y
sta $FEB5 - .sizeof(callsysbank_15),y
dey
bpl @L1
lda #.lobyte($FEB5) ; Copy the ROM part
sta ptr1
lda #.hibyte($FEB5)
sta ptr1+1
ldy #$00
@L2: lda (ptr1),y
sta $FEB5,y
iny
cpy #<($FF6F-$FEB5)
bne @L2
; Setup the jump vector table
ldy #$00
ldx #45-1 ; Number of vectors
@L3: lda #$20 ; JSR opcode
sta $FF6F,y
iny
lda #.lobyte($FEB5 - .sizeof(callsysbank_15))
sta $FF6F,y
iny
lda #.hibyte($FEB5 - .sizeof(callsysbank_15))
sta $FF6F,y
iny
dex
bpl @L3
; Copy the stack from the system bank into page 3
ldy #$FF
L4: lda (sysp1),y
sta $300,y
dey
cpy spsave
bne L4
; Set the indirect segment to bank we're executing in
lda ExecReg
sta IndReg
; Zero the BSS segment. We will do that here instead calling the routine
; in the common library, since we have the memory anyway, and this way,
; it's reused later.
lda #<__BSS_RUN__
sta ptr1
lda #>__BSS_RUN__
sta ptr1+1
lda #0
tay
; Clear full pages
ldx #>__BSS_SIZE__
beq Z2
Z1: sta (ptr1),y
iny
bne Z1
inc ptr1+1 ; Next page
dex
bne Z1
; Clear the remaining page
Z2: ldx #<__BSS_SIZE__
beq Z4
Z3: sta (ptr1),y
iny
dex
bne Z3
Z4: jmp Init
; ------------------------------------------------------------------------
; We are at $200 now. We may now start calling subroutines safely, since
; the code we execute is no longer in the stack page.
.segment "PAGE2"
; Call module constructors, enable chained IRQs afterwards.
Init: jsr initlib
lda #.lobyte(__IRQFUNC_COUNT__*2)
sta irqcount
; Enable interrupts
cli
; Push arguments and call main()
jsr callmain
; Disable Call module destructors. This is also the _exit entry and the default entry
; point for the break vector.
_exit: lda #$00
sta irqcount ; Disable custom irq handlers
jsr donelib ; Run module destructors
; Adress the system bank
lda #$0F
sta IndReg
; Copy back the old system bank stack contents
ldy #$FF
@L1: lda $300,y
sta (sysp1),y
dey
cpy spsave
bne @L1
; Setup the welcome code at the stack bottom in the system bank.
ldy #$00
lda #$58 ; CLI opcode
sta (sysp1),y
iny
lda #$60 ; RTS opcode
sta (sysp1),y
jmp Back
; -------------------------------------------------------------------------
; The IRQ handler goes into PAGE2. For performance reasons, and to allow
; easier chaining, we do handle the IRQs in the execution bank (instead of
; passing them to the system bank).
; This is the mapping of the active irq register of the 6525 (tpi1):
;
; Bit 7 6 5 4 3 2 1 0
; | | | | ^ 50 Hz
; | | | ^ SRQ IEEE 488
; | | ^ cia
; | ^ IRQB ext. Port
; ^ acia
irq: pha
txa
pha
tya
pha
lda IndReg
pha
lda ExecReg
sta IndReg ; Be sure to address our segment
tsx
lda $105,x ; Get the flags from the stack
and #$10 ; Test break flag
bne dobrk
; It's an IRQ
cld
; Call chained IRQ handlers
ldy irqcount
beq irqskip
lda #<__IRQFUNC_TABLE__
ldx #>__IRQFUNC_TABLE__
jsr condes ; Call the functions
; Done with chained IRQ handlers, check the TPI for IRQs and handle them
irqskip:lda #$0F
sta IndReg
ldy #TPI::AIR
lda (tpi1),y ; Interrupt Register 6525
beq noirq
; 50/60Hz interrupt
cmp #%00000001 ; ticker irq?
bne irqend
jsr SCNKEY ; Poll the keyboard
jsr UDTIM ; Bump the time
; Done
irqend: ldy #TPI::AIR
sta (tpi1),y ; Clear interrupt
noirq: pla
sta IndReg
pla
tay
pla
tax
pla
nmi: rti
dobrk: jmp (BRKVec)
; -------------------------------------------------------------------------
; udtim routine for the 610. We will not check for the stop key here, since
; C programs will not use it.
;
.proc UDTIM
inc time
bne L9
inc time+1
bne L9
inc time+2
bne L9
inc time+3
L9: rts
.endproc
; -------------------------------------------------------------------------
; Page 3
.segment "PAGE3"
BRKVec: .addr _exit ; BRK indirect vector
; -------------------------------------------------------------------------
; Data area.
.data
spsave: .res 1
.bss
irqcount: .byte 0