ca65 page crossing assert macro

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
rainwarrior
Posts: 7816
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

ca65 page crossing assert macro

Post by rainwarrior » Fri Aug 05, 2016 9:31 pm

I've usually just ensured that branches don't cross a page with small pieces of aligned code, but I thought it might be useful to have an assert to verify it. Here's code that should do it:

Code: Select all

.macro assert_branch_page label_
	.assert >(label_) = >(*+2), error, "Page crossing detected!"
.endmacro

test:
	assert_branch_page :+
	beq :+ ; .assert should happen if this would cross a page
		nop
	:
	rts
This seems to work fine, but I'd appreciate a second set of eyes on it, just to be sure. Does this look correct to you? Is *+2 before a branch instruction the right value to test against the label?


Edit: Later on I decided to just put the assert immediately after the branch instruction rather than before, which eliminates the need for +2.
Last edited by rainwarrior on Wed Oct 31, 2018 1:27 am, edited 1 time in total.

furrykef
Posts: 35
Joined: Fri Mar 02, 2012 11:10 pm

Re: ca65 page crossing assert macro

Post by furrykef » Sun Aug 28, 2016 9:07 pm

Just tested it myself; I believe it is correct.

6502.org says "A page boundary crossing occurs when the branch destination is on a different page than the instruction AFTER the branch instruction", which is exactly what this macro detects.

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

Re: ca65 page crossing assert macro

Post by tokumaru » Sun Aug 28, 2016 9:23 pm

If the assert comes before the instruction, then yes, I believe you have to compensate for the size of the instruction itself. I've implemented this functionality a bit differently - I have two macros, one for general page crossing checks, which can be used for data tables or anything else that isn't a branch instruction, and another one specific for branches, which outputs the branch instruction itself (so I only have to write the target label once in my source code) and then checks whether the branch will cause a page to be crossed:

Code: Select all

	;Generates a warning if two address are in different memory pages.
	.macro Assembler_TestPageCrossing _FirstAddress, _SecondAddress
		.ifblank _SecondAddress
			.assert >_FirstAddress = >*, warning, "Unintentional page crossing."
		.else
			.assert >_FirstAddress = >_SecondAddress, warning, "Unintentional page crossing."
		.endif
	.endmacro

	;Outputs a branch instruction and generates a warning if the destination is in another memory page.
	.macro Assembler_BranchToSamePage _Instruction, _DestinationAddress
		_Instruction _DestinationAddress
		Assembler_TestPageCrossing _DestinationAddress
	.endmacro

Code: Select all

Test:
	Assembler_BranchToSamePage beq, Destination
	;(more stuff)
Destination:

User avatar
rainwarrior
Posts: 7816
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 page crossing assert macro

Post by rainwarrior » Sun Aug 28, 2016 9:27 pm

Thanks for the replies.

In the weeks since I asked this question, I realized that I could just use the macro immediately after the branch instead and not have to do the +2 (and could also just use the same macro for page crossings everywhere, e.g. RAM regions to be indexed).

My question was really just "is the page crossing from PC + 2 of the branch?" Cause a lot of documentation isn't very explicit about it. Makes sense, though. PC gets adjusted by +2 by the CPU automatically, like it would for any 2 byte instruction, and then branch just optionally adds to it,

User avatar
mikejmoffitt
Posts: 1349
Joined: Sun May 27, 2012 8:43 pm

Re: ca65 page crossing assert macro

Post by mikejmoffitt » Sun Aug 28, 2016 11:05 pm

Is it possible to have every global label be automatically checked against this? I expected that ca65 would give a warning if any label crossed a page boundary, given the ruinous behavior when that happens, but if that's not the case then having an assertion for every global label that could be a routine would be nice.

jsr is not jmp
Last edited by mikejmoffitt on Mon Aug 29, 2016 1:40 am, edited 2 times in total.

User avatar
rainwarrior
Posts: 7816
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 page crossing assert macro

Post by rainwarrior » Sun Aug 28, 2016 11:11 pm

What do you mean by ruinous behaviour? The only consequence of crossing a page is it takes the CPU 2 more cycles. Unless you're working on timing critical code it doesn't make a difference.

What's a "global label that could be a routine"?

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

Re: ca65 page crossing assert macro

Post by tokumaru » Sun Aug 28, 2016 11:33 pm

The only "ruinous" thing I can think of related to page crossing is an indirect JMP that loads the destination address from the last byte of a page, because the CPU wraps around to the beginning of the page for the second byte of the address, instead of advancing to the next page.

Other than that, page crossing is hardly a big deal on the NES. Sure you have to avoid it in timed code, but that's hardly a big part of a typical NES game. A simple macro should cover these cases without problems.

User avatar
rainwarrior
Posts: 7816
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 page crossing assert macro

Post by rainwarrior » Sun Aug 28, 2016 11:43 pm

tokumaru wrote:The only "ruinous" thing I can think of related to page crossing is an indirect JMP that loads the destination address from the last byte of a page, because the CPU wraps around to the beginning of the page for the second byte of the address, instead of advancing to the next page.
That generates this warning: "jmp (abs)" across page border

User avatar
mikejmoffitt
Posts: 1349
Joined: Sun May 27, 2012 8:43 pm

Re: ca65 page crossing assert macro

Post by mikejmoffitt » Mon Aug 29, 2016 1:37 am

That's my mistake - I was confused, and thought of jsr suffering from that problem, rather than jmp.

User avatar
rainwarrior
Posts: 7816
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 page crossing assert macro

Post by rainwarrior » Mon Aug 29, 2016 2:58 am

Well, it's also only indirect jmp, which I think is exceedingly rare to fall on a page boundary (there just aren't many relevant use cases), and easily prevented. Direct jmp doesn't have this problem.

Post Reply