Skip to content

Instantly share code, notes, and snippets.

@SaxxonPike
Last active January 20, 2017 22:29
Show Gist options
  • Save SaxxonPike/e6d3e65a212a9b2ac5c7b97558456bc9 to your computer and use it in GitHub Desktop.
Save SaxxonPike/e6d3e65a212a9b2ac5c7b97558456bc9 to your computer and use it in GitHub Desktop.
Commodore 64 EasyFlash RAM driver [CA65 assembler]
;------------------------------------------------------------------------------
; [ efram chunk ]
; I wrote this because I needed something that could bank in/out the
; EasyFlash code, perform copies and memory swaps, and not occupy RAM
; where the game exists, all while fitting in the lower half of the
; page (because EasyAPI uses the upper half.)
; This starts at $DF03 to give us XBANK address at $DF00 and two
; variables to do whatever with at $DF01 and $DF02. Plus it fits nicely
; if you think of this as a jumptable entry address or something.
; (Don't mind my rants, these are notes to help ME think.)
;------------------------------------------------------------------------------
.scope EFRAM
BASE = $DF03-START
;------------------------------------------------------------------------------
; [ rom call jump ]
; A = low address to jump. If high address is needed too, write it
; externally or something. I use $A000 because it fits my driver.
;------------------------------------------------------------------------------
START:
sta ROMADDR+1+BASE
jsr ENABLE+BASE
ROMADDR: jsr $A000
;------------------------------------------------------------------------------
; [ disable ef-rom ]
; Turn off the EasyFlash LED and banking.
; The "bne" instruction is used at the end to save space.
;------------------------------------------------------------------------------
DISABLE:
ROMCONF: lda #$F5
pha
lda #%00000100
bne SWCOMMON
;------------------------------------------------------------------------------
; [ swap ]
; note: source and destination must be written externally.
; Use SWAPSRC1, SWAPSRC2, SWAPDST1, and SWAPDST2.
; $FFFF is used as a temp address because it's all one bits and this
; somehow benefits the life of the EasyFlash.
; This only does up to one page at a time (the length is determined by
; the X register) and banks out EasyFlash during the operation. It will
; bank EasyFlash back in after it is finished.
; Also note that due to the way the indexing is done, the swapping will
; start at ADDRESS+1. Not a problem if X is zero, it will do a whole
; page. But keep this in mind for all other values.
;------------------------------------------------------------------------------
SWAP:
.scope SWAPSEC
jsr DISABLE+BASE
LOOP:
SRC1: lda $FFFF,x
DST1: ldy $FFFF,x
DST2: sta $FFFF,x
tya
SRC2: sta $FFFF,x
dex
bne LOOP
.endscope
;------------------------------------------------------------------------------
; [ enable ef-rom ]
; All other roms are enabled as a side-effect & EasyFlash LED is on.
;------------------------------------------------------------------------------
ENABLE:
lda $01
sta ROMCONF+1+BASE
ora #$07
pha
lda #%10000111
;------------------------------------------------------------------------------
; [ set ef-rom state ]
; A = EasyFlash $DE02 state
;------------------------------------------------------------------------------
SWCOMMON:
sta $DE02
pla
sta $01
;------------------------------------------------------------------------------
; [ retore bank ]
; This is the ONLY return point from the loader.
; All functions lead here. Functions may also JSR here just to set
; the EasyFlash bank.
;------------------------------------------------------------------------------
SETBANK:
BNK: lda #$01
RESTBNK:
sta $DE00
rts
;------------------------------------------------------------------------------
; [ copy ]
; note: source and destination must be written externally to
; SRC+1/SRC+2 and DST+1/DST+2.
; X = highbyte length (two's complement)
; Y = lowbyte length (two's complement)
; The "bne RESTBNK" line is the *actual* exit point in this function.
; I have tried a number of methods and this hybrid self-modifying and
; absolute-indexing method is the fastest I could get. It does require
; a bit of setup due to the file length needing to be two's complement.
; Even using zeropage was slower because of the indirect addressing.
; BIT+BVC is used to determine if we are crossing EasyFlash banks.
; Due to this, you CANNOT tell it not to use the high bank. However,
; by writing different values to COPYWRP you can at least tell it you
; don't want the low bank (the value $A0 will work OK for this.) The
; default wrap value is $80.
;------------------------------------------------------------------------------
COPY:
.scope COPYSEC
jsr SETBANK+BASE
LOOP:
SRC: lda $FFFF,y
DST: sta $FFFF,y
iny
beq BANKCHK
CTROK:
inx
bne LOOP
inc LEN+BASE
bne LOOP
lda #$01
sta BNK+1+BASE
bne RESTBNK
BANKCHK:
inc SRC+2+BASE
inc DST+2+BASE
bit SRC+2+BASE
bvc CTROK
WRP: lda #$80
sta SRC+2+BASE
inc BNK+1+BASE
jsr SETBANK+BASE
jmp CTROK+BASE
LEN: .byte $00
.endscope
;------------------------------------------------------------------------------
; [ footer ]
; I used to put padding here, but now that this routine is pretty much
; finished I removed it.
;------------------------------------------------------------------------------
END:
.endscope
EFRAMSIZE = EFRAM::END-EFRAM::START
;------------------------------------------------------------------------------
; [ exports ]
; To be used when writing internal variables externally.
;------------------------------------------------------------------------------
EFMEMCONF := EFRAM::ROMCONF+1+EFRAM::BASE
EFDISABLE := EFRAM::DISABLE+EFRAM::BASE
EFENABLE := EFRAM::ENABLE+EFRAM::BASE
EFCOPY := EFRAM::COPY+EFRAM::BASE
COPYSRC := EFRAM::COPYSEC::SRC+1+EFRAM::BASE
COPYDST := EFRAM::COPYSEC::DST+1+EFRAM::BASE
COPYBNK := EFRAM::BNK+1+EFRAM::BASE
COPYWRP := EFRAM::COPYSEC::WRP+1+EFRAM::BASE
COPYLEN := EFRAM::COPYSEC::LEN+EFRAM::BASE
EFSWAP := EFRAM::SWAP+EFRAM::BASE
SWAPSRC1 := EFRAM::SWAPSEC::SRC1+1+EFRAM::BASE
SWAPSRC2 := EFRAM::SWAPSEC::SRC2+1+EFRAM::BASE
SWAPDST1 := EFRAM::SWAPSEC::DST1+1+EFRAM::BASE
SWAPDST2 := EFRAM::SWAPSEC::DST2+1+EFRAM::BASE
@SaxxonPike
Copy link
Author

See this driver in action on the Legacy of the Ancients / Legend of Blacksilver +4 release on CSDB:
http://csdb.dk/release/?id=128480

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment