I'm developing a game for C64 using CC65 to compile my C code to ASM. To get started quickly, I have been defining the data for my sprites in C arrays and then copying the data in those arrays to the correct location in memory for my sprites to point to. I have pointed the VIC II to Bank 3 and have my sprite data starting at 0xE000. This has been working fine but obviously that means my sprite data is stored in two places and that is not ideal.
So I want to load the data directly to 0xE000 at compile time using a segment in an ASM file like this:
.segment "GFXDATA"
.byte $7f,$ff,$fe,$e0,$00,$01,$e0,$00
.byte $01,$e0,$00,$01,$e0,$00,$01,$e0
.byte $00,$01,$e0,$c0,$c1,$e1,$21,$21
.byte $e0,$00,$01,$e0,$00,$01,$e0,$00
.byte $01,$e0,$3f,$01,$e0,$40,$81,$e0
.byte $80,$41,$e1,$00,$21,$e0,$00,$01
.byte $e0,$00,$01,$e0,$00,$01,$ff,$ff
.byte $ff,$ff,$ff,$ff,$7f,$ff,$fe,$01
To start with I created a simple test program that doesn't switch VIC Banks and loading the sprite data at 0x0840. This works fine after I modified the CC65 config file to look like this:
FEATURES {
STARTADDRESS: default = $0801;
}
SYMBOLS {
__LOADADDR__: type = import;
__EXEHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__HIMEM__: type = weak, value = $D000;
}
MEMORY {
ZP: file = "", define = yes, start = $0002, size = $001A;
LOADADDR: file = %O, start = %S - 2, size = $0002;
HEADER: file = %O, define = yes, start = %S, size = $000D;
MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__;
BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
LOADADDR: load = LOADADDR, type = ro;
EXEHDR: load = HEADER, type = ro;
STARTUP: load = MAIN, type = ro;
GFXDATA: load = MAIN, type = ro;
LOWCODE: load = MAIN, type = ro, optional = yes;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
INIT: load = MAIN, type = rw;
ONCE: load = MAIN, type = ro, define = yes;
BSS: load = BSS, type = bss, define = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}
The only line I added was this one:
GFXDATA: load = MAIN, type = ro;
That all works as expected.
So my next test was to try and get the sprite data in Bank 3 like in my game. This is where it has come unstuck. I successfully point the VIC to Bank 3, but my sprites are completely blank. The data doesn't seem to be loaded properly now. Here is the config file I have created to try and get this to work:
FEATURES {
STARTADDRESS: default = $0801;
}
SYMBOLS {
__LOADADDR__: type = import;
__EXEHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__HIMEM__: type = weak, value = $D000;
}
MEMORY {
ZP: file = "", define = yes, start = $0002, size = $001A;
LOADADDR: file = %O, start = %S - 2, size = $0002;
HEADER: file = %O, define = yes, start = %S, size = $000D;
MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __HIMEM__ - __HEADER_LAST__;
BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__;
GFXDATA: start = $E000, size = $0040, file = %O, define = yes;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
LOADADDR: load = LOADADDR, type = ro;
EXEHDR: load = HEADER, type = ro;
STARTUP: load = MAIN, type = ro;
LOWCODE: load = MAIN, type = ro, optional = yes;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
INIT: load = MAIN, type = rw;
ONCE: load = MAIN, type = ro, define = yes;
BSS: load = BSS, type = bss, define = yes;
GFXDATA: load = GFXDATA, type = ro;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}
The main different here is that I am trying to define a new memory area outside of MAIN. One called GFXDATA that starts where my sprites would start at 0xE000. Then I am loading the segment into that.
I must admit I am a pixel artist and game designer first, and a coder second, so I may be missing some really important concepts here.
Any help is much appreciated!