I've been using CA65 for almost a year now. MY first game ws NROM which was failrly easy to setup in terms of the code and linker files.
Now I am checking out UNROM. I've goten things setup and working, but it just feels like a kludge.
I was thinking I would show what I have, and let people share their opinions on a cleaner approach to organizing and assembling multiple PRG banks with CA65.
Basically here's what I did. I have 8 PRG banks (its UNROM) so I setup 8 PRG memory segments in the linker file. Then to enforce that each segment would actually be $4000 in size, I declared a start and end segment for each in the SEGMENTS section. Then in my individual .asm files (one per PRG bank) the first line declared the segment start and the last the segment end. (I also added some extra segments for the fixed PRG bank related to vectors, etc..)
Here's the linker file:
And here is a sample (blank) ASM file corresponding to one of the banks (in this case bank 3)
.segment "BANK3"
; start of bank is at $8000
; insert code for PRG bank 3
; bank ends at $C000 (non inclusive)
.segment "BANK3_END"
And here is a sample makefile
So, any opinions. Am I making this an unmaintainable mess?
Should I just have separate linker files for each bank. (Thats what I did for my header file.)
EDIT - final question. How do you guys normally access addresses in your banks. What I mean is, my labels in bank1 cannot be directly accessed by the code in bank7, so I've been using constants with SEGMENTS corresponding to those address constants. Am I again making things too complicated?
Al
Now I am checking out UNROM. I've goten things setup and working, but it just feels like a kludge.
I was thinking I would show what I have, and let people share their opinions on a cleaner approach to organizing and assembling multiple PRG banks with CA65.
Basically here's what I did. I have 8 PRG banks (its UNROM) so I setup 8 PRG memory segments in the linker file. Then to enforce that each segment would actually be $4000 in size, I declared a start and end segment for each in the SEGMENTS section. Then in my individual .asm files (one per PRG bank) the first line declared the segment start and the last the segment end. (I also added some extra segments for the fixed PRG bank related to vectors, etc..)
Here's the linker file:
Code:
MEMORY {
PRG0: start = $8000, size = $4000, type = ro, file = "bank0.prg";
PRG1: start = $8000, size = $4000, type = ro, file = "bank1.prg";
PRG2: start = $8000, size = $4000, type = ro, file = "bank2.prg";
PRG3: start = $8000, size = $4000, type = ro, file = "bank3.prg";
PRG4: start = $8000, size = $4000, type = ro, file = "bank4.prg";
PRG5: start = $8000, size = $4000, type = ro, file = "bank5.prg";
PRG6: start = $8000, size = $4000, type = ro, file = "bank6.prg";
PRG7: start = $C000, size = $4000, type = ro, file = "bank7.prg";
}
SEGMENTS {
BANK0: load = PRG0, type = ro, align = $100;
BANK0_END: load = PRG0, type = ro, start= $C000;
BANK1: load = PRG1, type = ro, align = $100;
BANK1_END: load = PRG1, type = ro, start= $C000;
BANK2: load = PRG2, type = ro, align = $100;
BANK2_END: load = PRG2, type = ro, start= $C000;
BANK3: load = PRG3, type = ro, align = $100;
BANK3_END: load = PRG3, type = ro, start= $C000;
BANK4: load = PRG4, type = ro, align = $100;
BANK4_END: load = PRG4, type = ro, start= $C000;
BANK5: load = PRG5, type = ro, align = $100;
BANK5_END: load = PRG5, type = ro, start= $C000;
BANK6: load = PRG6, type = ro, align = $100;
BANK6_END: load = PRG6, type = ro, start= $C000;
BANK7: load = PRG7, type = ro, start = $C000;
BANK_SWITCHING_TABLE: load = PRG7, type = ro, start = $FFF0;
VECTORS: load = PRG7, type = ro, start = $FFFA;
}
PRG0: start = $8000, size = $4000, type = ro, file = "bank0.prg";
PRG1: start = $8000, size = $4000, type = ro, file = "bank1.prg";
PRG2: start = $8000, size = $4000, type = ro, file = "bank2.prg";
PRG3: start = $8000, size = $4000, type = ro, file = "bank3.prg";
PRG4: start = $8000, size = $4000, type = ro, file = "bank4.prg";
PRG5: start = $8000, size = $4000, type = ro, file = "bank5.prg";
PRG6: start = $8000, size = $4000, type = ro, file = "bank6.prg";
PRG7: start = $C000, size = $4000, type = ro, file = "bank7.prg";
}
SEGMENTS {
BANK0: load = PRG0, type = ro, align = $100;
BANK0_END: load = PRG0, type = ro, start= $C000;
BANK1: load = PRG1, type = ro, align = $100;
BANK1_END: load = PRG1, type = ro, start= $C000;
BANK2: load = PRG2, type = ro, align = $100;
BANK2_END: load = PRG2, type = ro, start= $C000;
BANK3: load = PRG3, type = ro, align = $100;
BANK3_END: load = PRG3, type = ro, start= $C000;
BANK4: load = PRG4, type = ro, align = $100;
BANK4_END: load = PRG4, type = ro, start= $C000;
BANK5: load = PRG5, type = ro, align = $100;
BANK5_END: load = PRG5, type = ro, start= $C000;
BANK6: load = PRG6, type = ro, align = $100;
BANK6_END: load = PRG6, type = ro, start= $C000;
BANK7: load = PRG7, type = ro, start = $C000;
BANK_SWITCHING_TABLE: load = PRG7, type = ro, start = $FFF0;
VECTORS: load = PRG7, type = ro, start = $FFFA;
}
And here is a sample (blank) ASM file corresponding to one of the banks (in this case bank 3)
Code:
.segment "BANK3"
; start of bank is at $8000
; insert code for PRG bank 3
; bank ends at $C000 (non inclusive)
.segment "BANK3_END"
And here is a sample makefile
Code:
# To build the NES ROM just type: make
# To run the NES ROM just type: make run (or: make fceu)
# Note: as with any make system, if any of the files have been updated they will
# be rebuilt along with any parts that are dependant on them
# Tools required. Update these to point to where they are installed
AS = C:\Personal\NES\Dev\Compilers\cc65\bin\ca65.exe
LD = C:\Personal\NES\Dev\Compilers\cc65\bin\ld65.exe
EMU1 = C:\Personal\NES\Dev\Emulators\nintendulator\nintendulator.exe
EMU2 = C:\Personal\NES\Dev\Emulators\fceu-0.98.15-rerecording\fceu.exe
MAIN = myGame
# intermediate files
HEADER_OBJS = header.o
OBJS = bank0.o bank1.o bank2.o bank3.o bank4.o bank5.o bank6.o bank7.o
ALL_PRG = bank0.prg bank1.prg bank2.prg bank3.prg bank4.prg bank5.prg bank6.prg bank7.prg
# the part that does the compiling, assembling, linking etc..
all: $(MAIN).nes
clean:
rm $(OBJS) $(HEADER_OBJS) $(BANK_OBJS) $(MAIN).hed $(MAIN).nes $(ALL_PRG)
nintendulator: $(MAIN).nes
$(EMU1) $(MAIN).nes
fceu: $(MAIN).nes
$(EMU2) $(MAIN).nes
run: $(MAIN).nes
$(EMU1) $(MAIN).nes
# For making the header
$(HEADER_OBJS): %.o: %.asm
$(AS) $(CFLAGS) $< -o $@
$(MAIN).hed: $(HEADER_OBJS)
$(LD) $(HEADER_OBJS) -C header.ini -o $(MAIN).hed
# For making the PRG
$(OBJS): %.o: %.asm
$(AS) $(CFLAGS) $< -o $@
$(ALL_PRG): $(OBJS)
$(LD) $(OBJS) -C nes.ini
# For making the final iNES ROM
$(MAIN).nes: $(ALL_PRG) $(MAIN).hed
cat $(MAIN).hed $(ALL_PRG) > $(MAIN).nes
# To run the NES ROM just type: make run (or: make fceu)
# Note: as with any make system, if any of the files have been updated they will
# be rebuilt along with any parts that are dependant on them
# Tools required. Update these to point to where they are installed
AS = C:\Personal\NES\Dev\Compilers\cc65\bin\ca65.exe
LD = C:\Personal\NES\Dev\Compilers\cc65\bin\ld65.exe
EMU1 = C:\Personal\NES\Dev\Emulators\nintendulator\nintendulator.exe
EMU2 = C:\Personal\NES\Dev\Emulators\fceu-0.98.15-rerecording\fceu.exe
MAIN = myGame
# intermediate files
HEADER_OBJS = header.o
OBJS = bank0.o bank1.o bank2.o bank3.o bank4.o bank5.o bank6.o bank7.o
ALL_PRG = bank0.prg bank1.prg bank2.prg bank3.prg bank4.prg bank5.prg bank6.prg bank7.prg
# the part that does the compiling, assembling, linking etc..
all: $(MAIN).nes
clean:
rm $(OBJS) $(HEADER_OBJS) $(BANK_OBJS) $(MAIN).hed $(MAIN).nes $(ALL_PRG)
nintendulator: $(MAIN).nes
$(EMU1) $(MAIN).nes
fceu: $(MAIN).nes
$(EMU2) $(MAIN).nes
run: $(MAIN).nes
$(EMU1) $(MAIN).nes
# For making the header
$(HEADER_OBJS): %.o: %.asm
$(AS) $(CFLAGS) $< -o $@
$(MAIN).hed: $(HEADER_OBJS)
$(LD) $(HEADER_OBJS) -C header.ini -o $(MAIN).hed
# For making the PRG
$(OBJS): %.o: %.asm
$(AS) $(CFLAGS) $< -o $@
$(ALL_PRG): $(OBJS)
$(LD) $(OBJS) -C nes.ini
# For making the final iNES ROM
$(MAIN).nes: $(ALL_PRG) $(MAIN).hed
cat $(MAIN).hed $(ALL_PRG) > $(MAIN).nes
So, any opinions. Am I making this an unmaintainable mess?
Should I just have separate linker files for each bank. (Thats what I did for my header file.)
EDIT - final question. How do you guys normally access addresses in your banks. What I mean is, my labels in bank1 cannot be directly accessed by the code in bank7, so I've been using constants with SEGMENTS corresponding to those address constants. Am I again making things too complicated?
Al