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

Any way to print non-constant values in ca65?

Any way to print non-constant values in ca65?
by on (#206393)
I wanted to print some debug messages using .assert, but the values being debugged aren't necessarily known at assembly time, and I really want to display exactly how off the numbers are, rather than just stating that they're off. Since .sprintf won't take non-constant numbers, I came up with this hack:

.repeat 20, i
   .assert number <> i, ldwarning, .sprintf("number is %d", i)

It looks kludgy as hell, but it works for small ranges. I'm still wondering though, is there a better way to do this? Especially if the range of values is way bigger than in the example above. Am I just being stupid?
Re: Any way to print non-constant values in ca65?
by on (#206402)
I'm not aware of a good way, but personally I've used a macro that outputs the value in a special "build log" segment, which is then directed to a separate file in the linker configuration. Note that since we don't know the value of the variable, we always have to output a constant number of characters and pad it from the left with spaces or zeros. You can of course also output explanatory strings with .byte in the build log segment.

Here's the macro: ( ...
.macro __ngin_decimalInteger integer
    .local divider
    divider .set 100000000
    .local value
    value = integer

    ; \note .charmap isn't a problem here, because string/character literals
    ;       are not used.
    ; \todo This doesn't seem to work properly for signed numbers (try
    ;       e.g. $FFFFFFFF).

    .repeat 9, i
        ; Output the corresponding digit, or a space if the digit and all the
        ; more significant digits are zeroes. Unless we're outputting the least
        ; significant digit, which is always displayed (so that 0 is displayed).
        .byte .lobyte( __ngin_choice \
            (value/divider) <> 0 .or divider = 1, \
            (value/divider) .mod 10 + ngin_Ascii::kZero, \
            ngin_Ascii::kSpace )
        divider .set divider/10

I'm not sure why I chose to output only 9 characters (this may have been what caused it not to work with $FFFFFFFF as seen in the TODO comment). Seems like it should be 10 characters to cover the full range of a 32 bit variable. It's easy to modify this to output hexadecimal, if desired (see the __ngin_hexInteger macro in the above link).

__ngin_choice is a ternary macro defined as:
.define __ngin_choice( condition, if, else ) \
    ((!!(condition)) * (if) + (!(condition)) * (else))

ngin_Ascii is defined as:
.enum ngin_Ascii
    kLf     = 10
    kCr     = 13
    kSpace  = 32
    kQuote  = 34
    kZero   = 48
    kNine   = kZero+9
    kPlus   = 43
    kMinus  = 45
    kDot    = 46
    kA      = 65

EDIT: Added __ngin_hexInteger reference.
Re: Any way to print non-constant values in ca65?
by on (#206431)
That's a pretty good idea, thanks.
Re: Any way to print non-constant values in ca65?
by on (#207792)
I ended up going with this (it writes non-constant numbers to a text file):
   .macro Assembler_WRITE_NUMBER _Number, _Base, _Places, _Divisor
      .ifblank _Divisor
         Assembler_WRITE_NUMBER _Number, _Base, _Places, 1
         .if _Places > 0
            Assembler_WRITE_NUMBER _Number, _Base, (_Places) - 1, (_Divisor) * _Base
            .local _Digit
            _Digit = ((_Number) / (_Divisor)) .mod _Base
            .lobytes _Digit + $30 + (_Digit > 9) * $07

Then I can write numbers in any base with any number of places (I don't do anything about leading zeroes). This is how I would check the value of a label in PRG-ROM (base 16, 4 places):
Assembler_WRITE_NUMBER SomeLabel, 16, 4