Any way to print non-constant values in ca65?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Any way to print non-constant values in ca65?

Post by tokumaru »

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:

Code: Select all

.repeat 20, i
	.assert number <> i, ldwarning, .sprintf("number is %d", i)
.endrepeat
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?
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Any way to print non-constant values in ca65?

Post by thefox »

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: (https://github.com/fo-fo/ngin/blob/mast ... e.inc#L409)

Code: Select all

.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
    .endrepeat
.endmacro
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:

Code: Select all

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

Code: Select all

.enum ngin_Ascii
    kLf     = 10
    kCr     = 13
    kSpace  = 32
    kQuote  = 34
    kZero   = 48
    kNine   = kZero+9
    kPlus   = 43
    kMinus  = 45
    kDot    = 46
    kA      = 65
.endenum
EDIT: Added __ngin_hexInteger reference.
Last edited by thefox on Sat Oct 21, 2017 11:40 am, edited 1 time in total.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Any way to print non-constant values in ca65?

Post by tokumaru »

That's a pretty good idea, thanks.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Any way to print non-constant values in ca65?

Post by tokumaru »

I ended up going with this (it writes non-constant numbers to a text file):

Code: Select all

	.macro Assembler_WRITE_NUMBER _Number, _Base, _Places, _Divisor
		.ifblank _Divisor
			Assembler_WRITE_NUMBER _Number, _Base, _Places, 1
		.else
			.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
			.endif
		.endif
	.endmacro
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):

Code: Select all

Assembler_WRITE_NUMBER SomeLabel, 16, 4
Post Reply