2000-05-28 13:40:48 +00:00
|
|
|
;
|
2001-09-13 18:55:43 +00:00
|
|
|
; Startup code for cc65 (CBM 600/700 version)
|
2000-05-28 13:40:48 +00:00
|
|
|
;
|
|
|
|
; This must be the *first* file on the linker command line
|
|
|
|
;
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.export _exit, BRKVec, UDTIM
|
2002-10-03 19:18:06 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.import condes, initlib, donelib
|
2003-03-10 21:21:46 +00:00
|
|
|
.import push0, callmain
|
2003-12-20 23:13:26 +00:00
|
|
|
.import __BSS_RUN__, __BSS_SIZE__, __EXTZP_RUN__
|
|
|
|
.import __IRQFUNC_TABLE__, __IRQFUNC_COUNT__
|
|
|
|
.import SCNKEY
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
.include "zeropage.inc"
|
2003-12-20 23:13:26 +00:00
|
|
|
.include "extzp.inc"
|
2002-05-26 09:08:52 +00:00
|
|
|
.include "cbm610.inc"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
; ------------------------------------------------------------------------
|
|
|
|
; 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.
|
|
|
|
;
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.segment "BASICHDR"
|
2002-05-26 09:08:52 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.byte $03,$00,$11,$00,$0a,$00,$81,$20,$49,$b2,$30,$20,$a4,$20,$34,$00
|
2000-05-28 13:40:48 +00: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
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
;------------------------------------------------------------------------------
|
2000-05-28 13:40:48 +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.
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.segment "STARTUP"
|
|
|
|
|
|
|
|
Back: sei
|
|
|
|
ldx spsave
|
2000-05-28 13:40:48 +00:00
|
|
|
txs
|
|
|
|
lda IndReg
|
|
|
|
sta ExecReg
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
;------------------------------------------------------------------------------
|
|
|
|
; 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:
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
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
|
2003-12-20 23:13:26 +00:00
|
|
|
; 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.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
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
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
; Set the interrupt, NMI and other vectors
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
ldy #.sizeof(vectors)-1
|
|
|
|
L2: lda vectors,y
|
|
|
|
sta $10000 - .sizeof(vectors),y
|
2000-05-28 13:40:48 +00:00
|
|
|
dey
|
2003-12-20 23:13:26 +00:00
|
|
|
bpl L2
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
; Switch the indirect segment to the system bank
|
|
|
|
|
|
|
|
lda #$0F
|
|
|
|
sta IndReg
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Setup the C stack
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
lda #.lobyte($FEB5 - .sizeof(callsysbank_15))
|
|
|
|
sta sp
|
|
|
|
lda #.hibyte($FEB5 - .sizeof(callsysbank_15))
|
|
|
|
sta sp+1
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; 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
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
; Set the indirect segment to bank we're executing in
|
|
|
|
|
2003-12-19 00:12:08 +00:00
|
|
|
lda ExecReg
|
|
|
|
sta IndReg
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
; 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
|
2003-12-20 23:13:26 +00:00
|
|
|
Z4: jmp Init
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; ------------------------------------------------------------------------
|
|
|
|
; We are at $200 now. We may now start calling subroutines safely, since
|
|
|
|
; the code we execute is no longer in the stack page.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.segment "PAGE2"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Call module constructors, enable chained IRQs afterwards.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
Init: jsr initlib
|
|
|
|
lda #.lobyte(__IRQFUNC_COUNT__*2)
|
|
|
|
sta irqcount
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Enable interrupts
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
cli
|
2000-11-22 22:19:09 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Push arguments and call main()
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
jsr callmain
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Disable Call module destructors. This is also the _exit entry and the default entry
|
|
|
|
; point for the break vector.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
_exit: lda #$00
|
|
|
|
sta irqcount ; Disable custom irq handlers
|
|
|
|
jsr donelib ; Run module destructors
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Adress the system bank
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
lda #$0F
|
|
|
|
sta IndReg
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Copy back the old system bank stack contents
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
ldy #$FF
|
|
|
|
@L1: lda $300,y
|
|
|
|
sta (sysp1),y
|
|
|
|
dey
|
|
|
|
cpy spsave
|
|
|
|
bne @L1
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; Setup the welcome code at the stack bottom in the system bank.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
ldy #$00
|
|
|
|
lda #$58 ; CLI opcode
|
|
|
|
sta (sysp1),y
|
|
|
|
iny
|
|
|
|
lda #$60 ; RTS opcode
|
|
|
|
sta (sysp1),y
|
|
|
|
jmp Back
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
; 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).
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; 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)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
; udtim routine for the 610. We will not check for the stop key here, since
|
|
|
|
; C programs will not use it.
|
|
|
|
;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.proc UDTIM
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
inc time
|
|
|
|
bne L9
|
|
|
|
inc time+1
|
|
|
|
bne L9
|
|
|
|
inc time+2
|
|
|
|
bne L9
|
|
|
|
inc time+3
|
|
|
|
L9: rts
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.endproc
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
; -------------------------------------------------------------------------
|
|
|
|
; Page 3
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.segment "PAGE3"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
BRKVec: .addr _exit ; BRK indirect vector
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
; -------------------------------------------------------------------------
|
2003-12-20 23:13:26 +00:00
|
|
|
; Data area.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
.data
|
|
|
|
spsave: .res 1
|
|
|
|
|
2003-12-20 23:13:26 +00:00
|
|
|
.bss
|
|
|
|
irqcount: .byte 0
|
2002-05-26 09:08:52 +00:00
|
|
|
|