mirror of
https://github.com/cc65/cc65.git
synced 2024-11-12 22:07:16 +00:00
9d926289e1
git-svn-id: svn://svn.cc65.org/cc65/trunk@954 b7a2c559-68d2-44c3-8de9-860c34a00d81
517 lines
12 KiB
ArmAsm
517 lines
12 KiB
ArmAsm
;
|
|
; 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, _main
|
|
.import __CHARRAM_START__, __CHARRAM_SIZE__, __VIDRAM_START__
|
|
.import __BSS_RUN__, __BSS_SIZE__
|
|
.import irq, nmi
|
|
.import k_irq, k_nmi, k_plot, k_udtim, k_scnkey
|
|
|
|
.include "zeropage.inc"
|
|
.include "io.inc"
|
|
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Define and export the ZP variables for the CBM510 runtime
|
|
|
|
.exportzp sp, sreg, regsave
|
|
.exportzp ptr1, ptr2, ptr3, ptr4
|
|
.exportzp tmp1, tmp2, tmp3, tmp4
|
|
.exportzp regbank, zpspace
|
|
.exportzp vic, sid, cia1, cia2, acia, tpi1, tpi2
|
|
.exportzp ktab1, ktab2, ktab3, ktab4, time, RecvBuf, SendBuf
|
|
|
|
.zeropage
|
|
|
|
zpstart = *
|
|
sp: .res 2 ; Stack pointer
|
|
sreg: .res 2 ; Secondary register/high 16 bit for longs
|
|
regsave: .res 2 ; slot to save/restore (E)AX into
|
|
ptr1: .res 2
|
|
ptr2: .res 2
|
|
ptr3: .res 2
|
|
ptr4: .res 2
|
|
tmp1: .res 1
|
|
tmp2: .res 1
|
|
tmp3: .res 1
|
|
tmp4: .res 1
|
|
regbank: .res 6 ; 6 byte register bank
|
|
|
|
zpspace = * - zpstart ; Zero page space allocated
|
|
|
|
.code
|
|
|
|
; ------------------------------------------------------------------------
|
|
; 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.
|
|
;
|
|
|
|
; 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
|
|
|
|
; Since we need some vectors to access stuff in the system bank for our own,
|
|
; we will include them here, starting from $60:
|
|
|
|
.res $60-*
|
|
|
|
vic: .word $d800
|
|
sid: .word $da00
|
|
cia1: .word $db00
|
|
cia2: .word $dc00
|
|
acia: .word $dd00
|
|
tpi1: .word $de00
|
|
tpi2: .word $df00
|
|
ktab1: .word $eab1
|
|
ktab2: .word $eb11
|
|
ktab3: .word $eb71
|
|
ktab4: .word $ebd1
|
|
time: .dword $0000
|
|
RecvBuf: .word $0100 ; RS232 received buffer
|
|
SendBuf: .word $0200 ; RS232 send buffer
|
|
|
|
|
|
; 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 $FF80,y
|
|
dey
|
|
bne L0
|
|
|
|
; 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
|
|
L1: lda (ptr1),y
|
|
sta $90,y
|
|
dey
|
|
bpl L1
|
|
|
|
; Copy the page 3 vectors in place
|
|
|
|
ldy #$00
|
|
L2: lda p3vectable,y
|
|
sta $300,y
|
|
iny
|
|
cpy #p3vectable_size
|
|
bne L2
|
|
|
|
; Copy the rest of page 3 from the system bank
|
|
|
|
lda #$00
|
|
sta ptr1
|
|
lda #$03
|
|
sta ptr1+1
|
|
L3: lda (ptr1),y
|
|
sta $300,y
|
|
iny
|
|
bne L3
|
|
|
|
; 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
|
|
|
|
; Create the (empty) command line for the program
|
|
|
|
jsr push0 ; argc
|
|
jsr push0 ; argv
|
|
|
|
; Execute the program code
|
|
|
|
jmp Start
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Additional data that we need for initialization and that's overwritten
|
|
; later
|
|
|
|
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 k_scnkey ; 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 k_setlfs ; SETLFS
|
|
jmp k_setnam ; 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 k_settim ; SETTIM
|
|
jmp k_rdtim ; RDTIM
|
|
jmp $0000 ; STOP
|
|
jmp $0000 ; GETIN
|
|
jmp $0000 ; CLALL
|
|
jmp k_udtim ; UDTIM
|
|
jmp k_screen ; SCREEN
|
|
jmp k_plot ; PLOT
|
|
jmp k_iobase ; 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
|
|
|
|
; Call the user code
|
|
|
|
ldy #4 ; Argument size
|
|
jsr _main ; call the users code
|
|
|
|
; 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
|
|
|
|
k_iobase:
|
|
ldx cia2
|
|
ldy cia2+1
|
|
rts
|
|
|
|
k_screen:
|
|
ldx #40 ; Columns
|
|
ldy #25 ; Lines
|
|
rts
|
|
|
|
k_setlfs:
|
|
sta LogicalAdr
|
|
stx FirstAdr
|
|
sty SecondAdr
|
|
rts
|
|
|
|
k_setnam:
|
|
sta FileNameLen
|
|
lda $00,x
|
|
sta FileNameAdrLo
|
|
lda $01,x
|
|
sta FileNameAdrHi
|
|
lda $02,x
|
|
sta FileNameAdrSeg
|
|
rts
|
|
|
|
k_rdtim:
|
|
sei
|
|
lda time+0
|
|
ldx time+1
|
|
ldy time+2
|
|
cli
|
|
rts
|
|
|
|
k_settim:
|
|
sei
|
|
sta time+0
|
|
stx time+1
|
|
sty time+2
|
|
cli
|
|
rts
|
|
|
|
; -------------------------------------------------------------------------
|
|
; Data area - switch back to relocatable mode
|
|
|
|
.reloc
|
|
|
|
.data
|
|
spsave: .res 1
|
|
vidsave:.res 3
|
|
|
|
|