60bc009991
named EXEHDR. * Renamed BASICHDR to EXEHDR for the PET-II machines. * Moved the call to CHRCH in front of the code that saves the zero page, since open files are sometimes remembered in the zero page, so we need to close them before we grab a copy. git-svn-id: svn://svn.cc65.org/cc65/trunk@4507 b7a2c559-68d2-44c3-8de9-860c34a00d81
220 lines
5.4 KiB
ArmAsm
220 lines
5.4 KiB
ArmAsm
;
|
|
; Startup code for cc65 (Plus/4 version)
|
|
;
|
|
|
|
.export _exit
|
|
.export brk_jmp
|
|
.export __STARTUP__ : absolute = 1 ; Mark as startup
|
|
|
|
.import callirq_y, initlib, donelib
|
|
.import callmain, zerobss
|
|
.import __INTERRUPTOR_COUNT__
|
|
|
|
.include "zeropage.inc"
|
|
.include "plus4.inc"
|
|
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Constants
|
|
|
|
IRQInd = $500 ; JMP $0000 - used as indirect IRQ vector
|
|
|
|
; ------------------------------------------------------------------------
|
|
; BASIC header with a SYS call
|
|
|
|
.segment "EXEHDR"
|
|
|
|
.word Head ; Load address
|
|
Head: .word @Next
|
|
.word .version ; Line number
|
|
.byte $9E ; SYS token
|
|
.byte <(((Start / 1000) .mod 10) + $30)
|
|
.byte <(((Start / 100) .mod 10) + $30)
|
|
.byte <(((Start / 10) .mod 10) + $30)
|
|
.byte <(((Start / 1) .mod 10) + $30)
|
|
.byte $00 ; End of BASIC line
|
|
@Next: .word 0 ; BASIC end marker
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Startup code
|
|
|
|
.segment "STARTUP"
|
|
|
|
Start:
|
|
|
|
; Close open files
|
|
|
|
jsr $FFCC ; CLRCH
|
|
|
|
; Save the zero page locations we need
|
|
|
|
sei ; No interrupts since we're banking out the ROM
|
|
sta ENABLE_RAM
|
|
ldx #zpspace-1
|
|
L1: lda sp,x
|
|
sta zpsave,x
|
|
dex
|
|
bpl L1
|
|
sta ENABLE_ROM
|
|
cli
|
|
|
|
; Switch to second charset
|
|
|
|
lda #14
|
|
jsr $FFD2 ; BSOUT
|
|
|
|
; Save system stuff and setup the stack. The stack starts at the top of the
|
|
; usable RAM.
|
|
|
|
tsx
|
|
stx spsave ; save system stk ptr
|
|
|
|
lda #<$FD00
|
|
sta sp
|
|
lda #>$FD00
|
|
sta sp+1
|
|
|
|
; Setup the IRQ vector in the banked RAM and switch off the ROM
|
|
|
|
ldx #<IRQ
|
|
ldy #>IRQ
|
|
sei ; No ints, handler not yet in place
|
|
sta ENABLE_RAM
|
|
stx $FFFE ; Install interrupt handler
|
|
sty $FFFF
|
|
cli ; Allow interrupts
|
|
|
|
; Clear the BSS data
|
|
|
|
jsr zerobss
|
|
|
|
; Initialize irqcount, which means that from now own custom linked in IRQ
|
|
; handlers (via condes) will be called.
|
|
|
|
lda #.lobyte(__INTERRUPTOR_COUNT__*2)
|
|
sta irqcount
|
|
|
|
; Call module constructors
|
|
|
|
jsr initlib
|
|
|
|
; Push arguments and call main()
|
|
|
|
jsr callmain
|
|
|
|
; Back from main (this is also the _exit entry). Run module destructors.
|
|
|
|
_exit: pha ; Save the return code
|
|
jsr donelib ; Run module destructors
|
|
|
|
; Disable chained IRQ handlers
|
|
|
|
lda #0
|
|
sta irqcount ; Disable custom IRQ handlers
|
|
|
|
; Copy back the zero page stuff
|
|
|
|
ldx #zpspace-1
|
|
L2: lda zpsave,x
|
|
sta sp,x
|
|
dex
|
|
bpl L2
|
|
|
|
; Place the program return code into ST
|
|
|
|
pla
|
|
sta ST
|
|
|
|
; Restore the stack pointer
|
|
|
|
ldx spsave
|
|
txs
|
|
|
|
; Enable the ROM, reset changed vectors and return to BASIC
|
|
|
|
sta ENABLE_ROM
|
|
jmp $FF8A ; RESTOR
|
|
|
|
|
|
; ------------------------------------------------------------------------
|
|
; IRQ handler. The handler in the ROM enables the kernal and jumps to
|
|
; $CE00, where the ROM code checks for a BRK or IRQ and branches via the
|
|
; indirect vectors at $314/$316.
|
|
; To make our stub as fast as possible, we skip the whole part of the ROM
|
|
; handler and jump to the indirect vectors directly. We do also call our
|
|
; own interrupt handlers if we have any, so they need not use $314.
|
|
|
|
.segment "LOWCODE"
|
|
|
|
IRQ: cld ; Just to be sure
|
|
pha
|
|
txa
|
|
pha
|
|
tya
|
|
pha
|
|
tsx ; Get the stack pointer
|
|
lda $0104,x ; Get the saved status register
|
|
and #$10 ; Test for BRK bit
|
|
bne dobreak
|
|
|
|
; It's an IRQ and RAM is enabled. If we have handlers, call them. We will use
|
|
; a flag here instead of loading __INTERRUPTOR_COUNT__ directly, since the
|
|
; condes function is not reentrant. The irqcount flag will be set/reset from
|
|
; the main code, to avoid races.
|
|
|
|
ldy irqcount
|
|
beq @L1
|
|
jsr callirq_y ; Call the IRQ functions
|
|
|
|
; Since the ROM handler will end with an RTI, we have to fake an IRQ return
|
|
; on stack, so we get control of the CPU after the ROM handler and can switch
|
|
; back to RAM.
|
|
|
|
@L1: lda #>irq_ret ; Push new return address
|
|
pha
|
|
lda #<irq_ret
|
|
pha
|
|
php ; Push faked IRQ frame on stack
|
|
pha ; Push faked A register
|
|
pha ; Push faked X register
|
|
pha ; Push faked Y register
|
|
sta ENABLE_ROM ; Switch to ROM
|
|
jmp (IRQVec) ; Jump indirect to kernal irq handler
|
|
|
|
irq_ret:
|
|
sta ENABLE_RAM ; Switch back to RAM
|
|
pla
|
|
tay
|
|
pla
|
|
tax
|
|
pla
|
|
rti
|
|
|
|
dobreak:
|
|
lda brk_jmp+2 ; Check high byte of address
|
|
beq nohandler
|
|
jmp brk_jmp ; Jump to the handler
|
|
|
|
; No break handler installed, jump to ROM
|
|
|
|
nohandler:
|
|
sta ENABLE_ROM
|
|
jmp (BRKVec) ; Jump indirect to the break vector
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Data
|
|
|
|
.data
|
|
|
|
; BRK handling
|
|
brk_jmp: jmp $0000
|
|
|
|
spsave: .res 1
|
|
|
|
irqcount: .byte 0
|
|
|
|
.segment "ZPSAVE"
|
|
|
|
zpsave: .res zpspace
|
|
|
|
|