;
; Startup code for cc65 (CBM 500 version)
;
; This must be the *first* file on the linker command line
;

   	.export	     	_exit

     	.import	   	_clrscr, initlib, donelib
     	.import	     	push0, callmain
	.import	   	__CHARRAM_START__, __CHARRAM_SIZE__, __VIDRAM_START__
        .import         __EXTZP_RUN__, __EXTZP_SIZE__
	.import	       	__BSS_RUN__, __BSS_SIZE__
     	.import		irq, nmi
       	.import	       	k_irq, k_nmi, PLOT, UDTIM, SCNKEY

     	.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.
;

.code

; To make things more simple, make the code of this module absolute.

   	.org	$0001
Head:  	.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

; 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.

      	.res	$F8-*
Back:	ldx	spsave
   	txs
   	lda    	IndReg
   	sta	ExecReg

; The following code 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)!

      	tsx
       	stx	spsave 		; Save the system stackpointer
	ldx	#$FF
	txs	       		; Set up our own stack

; Set the interrupt, NMI and other vectors

      	ldy	#vectable_size
L0:   	lda	vectable-1,y
      	sta	$FF81-1,y
      	dey
       	bne    	L0

; Initialize the extended zero page variables

        ldx     #zptable_size
L1:     lda     zptable-1,x
        sta     <(__EXTZP_RUN__-1),x
        dex
        bne     L1

; Switch the indirect segment to the system bank

      	lda	#$0F
      	sta	IndReg

; Copy the kernal zero page ($90-$F2) from the system bank

   	lda  	#$90
   	sta	ptr1
	lda	#$00
   	sta	ptr1+1
   	ldy	#$62-1
L2:	lda	(ptr1),y
   	sta	$90,y
   	dey
   	bpl 	L2

; Copy the page 3 vectors in place

	ldy	#$00
L3:	lda	p3vectable,y
  	sta	$300,y
   	iny
  	cpy	#p3vectable_size
       	bne	L3

; Copy the rest of page 3 from the system bank

      	lda	#$00
      	sta	ptr1
	lda	#$03
	sta	ptr1+1
L4:	lda	(ptr1),y
   	sta	$300,y
   	iny
   	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:

; Setup the C stack

	lda   	#<$FF81
	sta 	sp
       	lda 	#>$FF81
	sta 	sp+1

; We expect to be in page 2 now

.if 	(* < $1FD)
  	jmp	$200
   	.res	$200-*
.endif
.if	(* < $200)
     	.res	$200-*,$EA
.endif
.if    	(* >= $2F0)
.error	"Code range invalid"
.endif

; This code is in page 2, so we may now start calling subroutines safely,
; since the code we execute is no longer in the stack page.

; Copy the character rom from the system bank into the execution bank

        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	#tpiCtrlReg
       	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    	#tpiPortC
       	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

; Call module constructors

       	jsr	initlib

; Execute the program code

	jmp	Start

; ------------------------------------------------------------------------
; Additional data that we need for initialization and that's overwritten
; later

zptable:
        .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
  	.dword 	$0000           ; time
        .word	$0100		; RecvBuf
        .word  	$0200  	       	; SendBuf
zptable_size    = * - zptable

vectable:
      	jmp	$0000		; CINT
      	jmp	$0000		; IOINIT
      	jmp	$0000		; RAMTAS
      	jmp	$0000	      	; RESTOR
      	jmp	$0000	      	; VECTOR
      	jmp	$0000	      	; SETMSG
      	jmp	$0000  	      	; SECOND
      	jmp	$0000	      	; TKSA
      	jmp	$0000	      	; MEMTOP
      	jmp	$0000	      	; MEMBOT
       	jmp    	SCNKEY
      	jmp	$0000	      	; SETTMO
      	jmp	$0000	      	; ACPTR
      	jmp	$0000	      	; CIOUT
      	jmp	$0000  	      	; UNTLK
      	jmp	$0000	      	; UNLSN
      	jmp	$0000	      	; LISTEN
      	jmp	$0000	      	; TALK
   	jmp	$0000	      	; READST
       	jmp    	SETLFS
       	jmp    	SETNAM
     	jmp	$0000	      	; OPEN
	jmp	$0000 	      	; CLOSE
     	jmp	$0000	      	; CHKIN
	jmp	$0000	      	; CKOUT
	jmp	$0000	      	; CLRCH
	jmp	$0000	      	; BASIN
      	jmp	$0000	      	; BSOUT
	jmp	$0000	      	; LOAD
  	jmp	$0000	      	; SAVE
	jmp	SETTIM
       	jmp    	RDTIM
	jmp	$0000	      	; STOP
   	jmp	$0000	      	; GETIN
	jmp	$0000	      	; CLALL
	jmp	UDTIM
   	jmp	SCREEN
	jmp	PLOT
	jmp	IOBASE
   	sta	ExecReg
	rts
	.byte  	$01 	      	; Filler
       	.word	nmi
       	.word	0     	      	; Reset - not used
       	.word	irq
vectable_size	= * - vectable

p3vectable:
	.word	k_irq		; IRQ user vector
	.word	k_brk		; BRK user vector
	.word	k_nmi		; NMI user vector
p3vectable_size	= * - p3vectable


; ------------------------------------------------------------------------
; This is the program code after setup. It starts at $400

   	.res	$400-*

Start:

; Enable interrupts

      	cli

; Push arguments and call main()

       	jsr    	callmain

; Call module destructors. This is also the _exit entry.

_exit:	jsr	donelib		; Run module destructors

; We need access to the system bank now

      	lda	#$0F
      	sta	IndReg

; Switch back the video to the system bank

  	ldy	#tpiCtrlReg
	lda	vidsave+0
  	sta	(tpi1),y

	ldy    	#tpiPortC
   	lda	vidsave+1
   	sta	(tpi2),y

        ldy	#VIC_VIDEO_ADR
	lda	vidsave+2
	sta	(vic),y

; Clear the start of the zero page, since it will be interpreted as a
; (garbage) BASIC program otherwise. This is also the default entry for
; the break vector.

k_brk:	sei
      	lda	#$00
      	ldx	#$3E
Clear:	sta	$02,x
      	dex
      	bne	Clear

; Setup the welcome code at the stack bottom in the system bank. Use
; the F4/F5 vector to access the system bank

      	ldy    	#$00
       	sty	$F4
      	iny
      	sty	$F5
   	ldy	#reset_size-1
@L1:	lda	reset,y
   	sta	($F4),y
   	dey
   	bne	@L1
   	jmp	Back

; ------------------------------------------------------------------------
; Code that is copied into the system bank at $100 when switching back

reset:	cli
	jmp	$8000		   	; BASIC cold start
reset_size = * - reset

; ------------------------------------------------------------------------
; Code for a few simpler kernal calls goes here

.export IOBASE
.proc   IOBASE
        ldx	cia2
  	ldy	cia2+1
	rts
.endproc

.export SCREEN
.proc   SCREEN
       	ldx    	#40	   	; Columns
	ldy	#25	   	; Lines
	rts
.endproc

.export SETLFS
.proc   SETLFS
        sta     LogicalAdr
        stx     FirstAdr
        sty     SecondAdr
        rts
.endproc

.export SETNAM
.proc   SETNAM
        sta     FileNameLen
        lda     $00,x
        sta     FileNameAdrLo
        lda     $01,x
        sta     FileNameAdrHi
        lda     $02,x
        sta     FileNameAdrSeg
        rts
.endproc

.export RDTIM
.proc   RDTIM
     	sei
     	lda   	time+0
     	ldx   	time+1
     	ldy   	time+2
     	cli
     	rts
.endproc

.export SETTIM
.proc   SETTIM
     	sei
     	sta	time+0
     	stx	time+1
     	sty	time+2
     	cli
     	rts
.endproc

; -------------------------------------------------------------------------
; Data area - switch back to relocatable mode

	.reloc

.data
spsave:	.res	1
vidsave:.res	3


