This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

CA65: generating debug labels for multiple banks (fceux .nl)

CA65: generating debug labels for multiple banks (fceux .nl)
by on (#227266)
I have been trying to get ca65 to generate labels that are compatible with multiple banks for use with fceux's debugger, but I don't know how to get it to export the gamename.labels.txt file with bank information on each line.

The best way I got was to edit the example_fceux_symbols.py script made by I forgot who (dougeff perhaps?) rainwarrior (thanks!) so it would at least sort the labels that have prefixes to them. For example, every bank 5 label must start with "bk5_" prefix, then it will go to the .5.nl file. All labels with no prefixes go to bank 0 by default (0x0000 - 0x7FF ones go to .ram.nl).

Is there another way to do this?

Code:
#############################################################
# CA65 labels.txt to fceux's .nl files converter script
# Original script by rainwarrior.
# Edited for multiple banks compatibility by Nesrocks 2018
# Visit nesrocks.com
# version 1.3
#############################################################


# All labels that have a bk<number>_ prefix will go to the appropriate file.
# For example "bk7_reset_handler:" will go to the gamename.nes.7.nl file.
#
# Labels that have no prefix in this format and have address 0x8000+ will go to bank 0 file
# and may be randomly overwritten if the memory address is the same.
# Ram and zeropage labels do not need prefixes, they are sorted by the address.
#
# Warning: this script will delete .nl files with the same rom name that weren't freshly created.
# Script best used in a batch sequence on game compile.
#
# Original folder structure:
# compile.bat
# src/main.asm
# src/system/example_fceux_symbols.py
#
# batch command calling this script:
# cd src
# system\example_fceux_symbols.py
#
# You may need to edit this script to adjust for paths in your project folder structure.


#################
# User parameters
#################

banks = 8 # Set your bank count here. i.e: 8 = banks 0-7. If set to 2 it will go into "old" mode and generate files based on addresses only (ram.nl, 0.nl and 1.nl)
trim_prefix = 1 # 0-1. Option to remove the label prefixes on the converted debug file. can help readability on fceux's debugger.
romname = "gamename" # Write your ROM name here, minus .nes extension. No special characters.

#################
# User params end
#################

import sys
import os
assert sys.version_info[0] >= 3, "Python 3 required."

from collections import OrderedDict


def label_to_nl(label_file):
    rawlabels = []

    try:
        of = open(label_file, "rt")
        rawlabels = of.readlines()
    except IOError:
        print("skipped: "+label_file)
        return

    labels = [] # a list of lists. finally will contain a list of labels for each bank. will overwrite labels with same address on the same bank
    ramlabels = [] # zeropage and ram labels go here
    ramstrout = ""
    str_outs = []
    nl_files = []
    prefixes = []

    for bank in range(banks):
        str_outs.append("")
        nl_files.append("..\\" + romname + ".nes." + str(bank) + ".nl")
        prefixes.append("bk" + str(bank) + "_")
        labels.append([])

    # read the raw labels string and populate the labels list of lists
    for line in rawlabels:

        # get a list of words, splitting on spaces
        words = line.split()

        if (words[0] == "al"):
            address = int(words[1], 16) # the memory address
            label = words[2] # the actual symbol
            label = label.lstrip('.') # remove everything to the left of ".", including it

            if (label[0] == '_' and label[1] == '_'): # if the first and second letters of the symbol are _ _
                continue # skip compiler internals


            hasbank = 0 # flag to know if the label on this line has a prefix. if it doesn't, throw it into bank 0

            if (address >= 0x0000 and address <= 0x7FF):
                ramlabels.append(label)
                ramstrout += ("$%04X#%s#\n" % (address, label))

            else:
                for bank in range(banks):
                    if label.startswith(prefixes[bank]):
                        if trim_prefix:
                            label = label[4:]
                        labels[bank].append(label)
                        str_outs[bank] += ("$%04X#%s#\n" % (address, label))
                        hasbank = 1 # set the flag so it skips the next part

                # no prefix detected flag
                if hasbank == 0:
                    # throw into bank 0 if generic amount of banks or if address is bank 0
                    if banks != 2 or (address >= 0x8000 and address <= 0xBFFF):
                        labels[0].append(label)
                        str_outs[0] += ("$%04X#%s#\n" % (address, label))
                    # address must be bank 1
                    elif (address >= 0xC000 and address <= 0xFFFF):
                        labels[1].append(label)
                        str_outs[1] += ("$%04X#%s#\n" % (address, label))

    # write each file that isn't empty
    for bank in range(banks):
        if str_outs[bank] != "":
            open(nl_files[bank], "wt").write(str_outs[bank])
            print("debug symbols: " + nl_files[bank])

        # delete old nl files if they exist
        elif os.path.isfile(nl_files[bank]):
            os.remove(nl_files[bank])

    if ramlabels != "":
        open("..\\" + romname + ".nes.ram.nl", "wt").write(ramstrout)
        print("debug symbols: " + "..\\" + romname + ".nes.ram.nl")


if __name__ == "__main__":
    label_to_nl("..\\" + romname + ".labels.txt")
Re: CA65: generating debug labels for multiple banks (fceux
by on (#227267)
I think the script is mine.

I think a better way is probably to parse the debug file instead of an nl file. With that you can get a list of segments/etc. and you could sort out the banks with that.

The example I gave doesn't deal with banks. With Lizard I had 32k banking, so it seemed okay just to split up each bank into a separate link (keeps the symbols separated, neatly deals with the issue of duplicate code conflicts due to there being no fixed bank, etc.). With a fixed bank I'd probably want to do them in one big build instead, and for that I'd need to parse segments.
Re: CA65: generating debug labels for multiple banks (fceux
by on (#227268)
Thanks for the original script! It's a godsend :)

Oh for some reason I had in my mind that the dbg file was binary compressed, not plain text. Interesting! Will look into that.
I also noticed that I destroyed the .ram file creation, so ranges 0x000 - 0x7FF are going to .0.nl which is no good. I'll udpate that before going the debug file route.
edit: post updated with proper .ram file sorting
Re: CA65: generating debug labels for multiple banks (fceux
by on (#227275)
If you speak C, there's an "official" library for parsing the debug files at https://github.com/cc65/cc65/tree/master/src/dbginfo
Re: CA65: generating debug labels for multiple banks (fceux
by on (#227287)
That sounds like it would be very useful. I skimmed through the three files and I have no idea what to do with them or how to use them. I'm not versed in C at all.
I had never used python before, so for this purpose I guess I'll just try to parse the debug file using python for now, since I managed to get something done.

edit: updated the original post to add the ability to use old style bank assigning by address instead of prefixes. Just set banks to 2.