/tools/cc65-2.13.2/libsrc/cbm510/crt0.s
Assembly | 585 lines | 347 code | 122 blank | 116 comment | 0 complexity | 6ec9ae9e245b4358023d37fd06302432 MD5 | raw file
- ;
- ; Startup code for cc65 (CBM 500 version)
- ;
- .export _exit
- .export __STARTUP__ : absolute = 1 ; Mark as startup
- .import _clrscr, initlib, donelib, callirq_y
- .import push0, callmain
- .import __CHARRAM_START__, __CHARRAM_SIZE__, __VIDRAM_START__
- .import __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
- .import __INTERRUPTOR_COUNT__
- .import scnkey, UDTIM
- .include "zeropage.inc"
- .include "extzp.inc"
- .include "cbm510.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 0, 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,0,133,0
- ;
- ; The machine program in the data lines is:
- ;
- ; sei
- ; lda #$00
- ; sta $00 <-- Switch to bank 0 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,$30,$2c,$31,$33,$33,$2c,$30,$00,$00,$00
- ;------------------------------------------------------------------------------
- ; A table that contains values that must be transfered from the system zero
- ; page into out zero page. Contains pairs of bytes, first one is the address
- ; in the system ZP, second one is our ZP address. The table goes into page 2,
- ; but is declared here, because it is needed earlier.
- .SEGMENT "PAGE2"
- .proc transfer_table
- .byte $9F, DEVNUM
- .byte $CA, CURS_Y
- .byte $CB, CURS_X
- .byte $EC, CHARCOLOR
- .endproc
- ;------------------------------------------------------------------------------
- ; Page 3 data. This page contains the break vector and the bankswitch
- ; subroutine that is copied into high memory on startup. The space occupied by
- ; this routine will later be used for a copy of the bank 15 stack. It must be
- ; saved, since we're going to destroy it when calling bank 15.
- .segment "PAGE3"
- BRKVec: .addr _exit ; BRK indirect vector
- .proc callbank15
- excrts = $FEFE
- .org $FEC3
- entry: php
- pha
- lda #$0F ; Bank 15
- sta IndReg
- txa
- pha
- tya
- pha
- sei
- ldy #$FF
- lda (sysp1),y
- tay
- lda ExecReg
- sta (sysp1),y
- dey
- lda #.hibyte(excrts-1)
- sta (sysp1),y
- dey
- lda #.lobyte(excrts-1)
- sta (sysp1),y
- tya
- sec
- sbc #7
- sta $1FF ; Save new sp
- tay
- tsx
- pla
- iny
- sta (sysp1),y
- pla
- iny
- sta (sysp1),y
- pla
- iny
- sta (sysp1),y
- pla
- iny
- sta (sysp1),y
- lda $105,x
- sec
- sbc #3
- iny
- sta (sysp1),y
- lda $106,x
- sbc #0
- iny
- sta (sysp1),y
- ldy $1FF ; Restore sp in bank 15
- lda #.hibyte(expull-1)
- sta (sysp1),y
- dey
- lda #.lobyte(expull-1)
- sta (sysp1),y
- dey
- pla
- pla
- tsx
- stx $1FF
- tya
- tax
- txs
- lda IndReg
- jmp $FFF6
- expull: pla
- tay
- pla
- tax
- pla
- plp
- rts
- .if (expull <> $FF26)
- .error "Symbol expull must be aligned with kernal in bank 15"
- .endif
- .reloc
- .endproc
- ;------------------------------------------------------------------------------
- ; 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: 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 #$00
- 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 ; vic
- .word $da00 ; sid
- .word $db00 ; cia1
- .word $dc00 ; cia2
- .word $dd00 ; acia
- .word $de00 ; tpi1
- .word $df00 ; tpi2
- .word $eab1 ; ktab1
- .word $eb11 ; ktab2
- .word $eb71 ; ktab3
- .word $ebd1 ; ktab4
- .endproc
- ; Switch the indirect segment to the system bank
- Origin: lda #$0F
- sta IndReg
- ; Initialize the extended zeropage
- ldx #.sizeof(extzp)-1
- L1: lda extzp,x
- sta <__EXTZP_RUN__,x
- dex
- bpl L1
- ; Save the old stack pointer from the system bank and setup our hw sp
- tsx
- txa
- ldy #$FF
- sta (sysp1),y ; Save system stack point into $F:$1FF
- ldx #$FE ; Leave $1FF untouched for cross bank calls
- txs ; Set up our own stack
- ; Copy stuff from the system zeropage to ours
- lda #.sizeof(transfer_table)
- sta ktmp
- L2: ldx ktmp
- ldy transfer_table-2,x
- lda transfer_table-1,x
- tax
- lda (sysp0),y
- sta $00,x
- dec ktmp
- dec ktmp
- bne L2
- ; Set the interrupt, NMI and other vectors
- ldx #.sizeof(vectors)-1
- L3: lda vectors,x
- sta $10000 - .sizeof(vectors),x
- dex
- bpl L3
- ; Setup the C stack
- lda #.lobyte(callbank15::entry)
- sta sp
- lda #.hibyte(callbank15::entry)
- sta sp+1
- ; Setup the subroutine and jump vector table that redirects kernal calls to
- ; the system bank.
- ldy #.sizeof(callbank15)
- @L1: lda callbank15-1,y
- sta callbank15::entry-1,y
- dey
- bne @L1
- ; Setup the jump vector table. Y is zero on entry.
- ldx #45-1 ; Number of vectors
- @L2: lda #$20 ; JSR opcode
- sta $FF6F,y
- iny
- lda #.lobyte(callbank15::entry)
- sta $FF6F,y
- iny
- lda #.hibyte(callbank15::entry)
- sta $FF6F,y
- iny
- dex
- bpl @L2
- ; 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"
- ; Copy the character rom from the system bank into the execution bank
- Init: lda #<$C000
- sta ptr1
- lda #>$C000
- sta ptr1+1
- lda #<__CHARRAM_START__
- sta ptr2
- lda #>__CHARRAM_START__
- sta ptr2+1
- lda #>__CHARRAM_SIZE__ ; 16 * 256 bytes to copy
- sta tmp1
- ldy #$00
- ccopy: lda #$0F
- sta IndReg ; Access the system bank
- ccopy1: lda (ptr1),y
- sta __VIDRAM_START__,y
- iny
- bne ccopy1
- lda ExecReg
- sta IndReg
- ccopy2: lda __VIDRAM_START__,y
- sta (ptr2),y
- iny
- bne ccopy2
- inc ptr1+1
- inc ptr2+1 ; Bump high pointer bytes
- dec tmp1
- bne ccopy
- ; Clear the video memory. We will do this before switching the video to bank 0
- ; to avoid garbage when doing so.
- jsr _clrscr
- ; Reprogram the VIC so that the text screen and the character ROM is in the
- ; execution bank. This is done in three steps:
- lda #$0F ; We need access to the system bank
- sta IndReg
- ; Place the VIC video RAM into bank 0
- ; CA (STATVID) = 0
- ; CB (VICDOTSEL) = 0
- ldy #TPI::CR
- lda (tpi1),y
- sta vidsave+0
- and #%00001111
- ora #%10100000
- sta (tpi1),y
- ; Set bit 14/15 of the VIC address range to the high bits of __VIDRAM_START__
- ; PC6/PC7 (VICBANKSEL 0/1) = 11
- ldy #TPI::PRC
- lda (tpi2),y
- sta vidsave+1
- and #$3F
- ora #<((>__VIDRAM_START__) & $C0)
- sta (tpi2),y
- ; Set the VIC base address register to the addresses of the video and
- ; character RAM.
- ldy #VIC_VIDEO_ADR
- lda (vic),y
- sta vidsave+2
- and #$01
- ora #<(((__VIDRAM_START__ >> 6) & $F0) | ((__CHARRAM_START__ >> 10) & $0E) | $02)
- ; and #$0F
- ; ora #<(((>__VIDRAM_START__) << 2) & $F0)
- sta (vic),y
- ; Switch back to the execution bank
- lda ExecReg
- sta IndReg
- ; Activate chained interrupt handlers, then enable interrupts.
- lda #.lobyte(__INTERRUPTOR_COUNT__*2)
- sta irqcount
- cli
- ; Call module constructors.
- jsr initlib
- ; Push arguments and call main()
- jsr callmain
- ; Call module destructors. This is also the _exit entry and the default entry
- ; point for the break vector.
- _exit: pha ; Save the return code on stack
- jsr donelib ; Run module destructors
- lda #$00
- sta irqcount ; Disable custom irq handlers
- ; Address the system bank
- lda #$0F
- sta IndReg
- ; Switch back the video to the system bank
- ldy #TPI::CR
- lda vidsave+0
- sta (tpi1),y
- ldy #TPI::PRC
- lda vidsave+1
- sta (tpi2),y
- ldy #VIC_VIDEO_ADR
- lda vidsave+2
- sta (vic),y
- ; Copy stuff back from our zeropage to the systems
- .if 0
- lda #.sizeof(transfer_table)
- sta ktmp
- @L0: ldx ktmp
- ldy transfer_table-2,x
- lda transfer_table-1,x
- tax
- lda $00,x
- sta (sysp0),y
- dec ktmp
- dec ktmp
- bne @L0
- .endif
- ; Place the program return code into ST
- pla
- ldy #$9C ; ST
- sta (sysp0),y
- ; Setup the welcome code at the stack bottom in the system bank.
- ldy #$FF
- lda (sysp1),y ; Load system bank sp
- tax
- iny ; Y = 0
- lda #$58 ; CLI opcode
- sta (sysp1),y
- iny
- lda #$60 ; RTS opcode
- sta (sysp1),y
- lda IndReg
- sei
- txs
- 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
- jsr callirq_y ; 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)
- ; -------------------------------------------------------------------------
- ; Data area
- .data
- vidsave:.res 3
- .bss
- irqcount: .byte 0