I've been thinking, AND ... here's what happened (or not)

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
User avatar
Ramsis
Posts: 341
Joined: Sun Jul 01, 2012 6:44 am
Location: Lion's den :3
Contact:

I've been thinking, AND ... here's what happened (or not)

Post by Ramsis »

Okay, so this has been driving me nuts for days. :?

Though I'm still convinced it's just me, myself and/or I (it always has, so it's got to be this time, too!), I thought I'd seek professional help before I involuntarily zero out, you know, the zero page. :mrgreen:

Consider this bit of SNES-65816, native-mode code (Accu is 8 bits, X/Y are 16 bits):

Code: Select all

CardCheckError:
	lda	CARDSTATUS						; get card status, check for general error
	sta	errorCode
	and	#%00000001
	cmp	#%00000001
	beq	CardError
	rts

CardError:
	ClearLine 21
	ClearLine 22
	ClearLine 23
	SetCursorPos 21, 1

	ldy	#errorCode

	PrintString "Error $%x - CF card status"
FYI, CARDSTATUS is a 24-bit address, errorCode is a direct page variable, and ClearLine, SetCursorPos and PrintString are all macros, none of which messes with the contents of errorCode.

Those who know me might recognize this as a code snippet from my unofficial SNES PowerPak firmware. What you might not know is that I'm currently working on v3.01, which will feature a lot of improvements over v3.00. :D

So, here's the deal: As CardCheckError gets called very often (i. e., more than every 512 bytes of data transferred to or from the CF card), I thought it would be a good idea to optimize this subroutine a tiny little bit.

Just like so:

Code: Select all

CardCheckError:
	lda	CARDSTATUS						; get card status, check for general error
	sta	errorCode
	and	#%00000001
	bne	CardError
	rts
As there's only the LSB, aka one single bit being tested, I thought it'd be nothing to write home about.

We-heh-heh-hell. Yes it is. :!:

The routine being shortened like this, the PowerPak would give me a card error message right upon power-up.

Strange? Yes, but wait 'til you hear this ...

It would even laugh me in the face by telling me
Error $50 - CF card status
Can you believe it? Bit 0 of errorCode is clear/zero, as it apparently has been all the time, and still, the branch was taken regardless whenever I went from AND #1/CMP #1/BEQ SOMEWHERE to AND #1/BNE SOMEWHERE.

WTF's happening here?? :?:

I spent 3+ hours googling for a 65X(X)X AND quirk, but didn't come up with anything.

Please help me, as I'm beginning to lose my sanity over this (but luckily not my mane, which is hairdcoded). :lol:

Thanks!!
Some of my projects:
Furry RPG!
Unofficial SNES PowerPak firmware
(See my GitHub profile for more)
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: I've been thinking, AND ... here's what happened (or not

Post by koitsu »

There's nothing wrong with your understanding the opcodes. But I'll go over them anyway:

lda modifies N/Z flags, as you know.
sta does not modify N/Z flags, as you know.
and modifies N/Z flags appropriately -- if MSB of result is set, N=1 else N=0. If result is zero, Z=1 else Z=0.
cmp modifies N/Z/C -- it operates similar to sbc but there's no saving of the result, instead only flags get changed. If MSB of result set, N=1 else N=0. If result is zero, Z=1 else Z=0. If borrow is required (accumulator value >= result), C=1, else C=0.
bne checks against Z flag; if Z=1, branch is taken.

You don't state where CARDSTATUS lives, but you imply it's in direct page because you propose "zeroing out zero page" as a solution. Keep reading.

My gut feeling is that your lda CARDSTATUS isn't using 24-bit addressing but instead absolute or direct page addressing, resulting in a value read from wherever B is at the time. It varies per assembler, but I would expect to see lda.l CARDSTATUS or some assembler equivalent. Generate an assembly listing if you can and verify it's using 24-bit addressing; if you can't generate a listing, stick some identifier bytes near the routine (like .db $ca,$fe,$ba,$be, open up the resulting ROM in a hex editor, search for $cafebabe, then manually check each opcode of the routine in question to see which is being used.

opcode $ad = lda absolute (16-bit)
opcode $af = lda absolute long (24-bit)
opcode $a5 = lda direct page

And as a general mode of operation -- if applicable, of course! -- your program should zero direct page / RAM anyway. I won't harp on that though, everyone's needs/situations are different.

Other weird things that might be be happening, but I have no way to confirm this (only you would know):

* Possibly where CARDSTATUS is resides somewhere around where the stack pointer might be (or might overflow or underflow into)
* Possibly NMI routine is modifying some part of memory and changing values to something you don't expect
* Possibly there's a general bug in your program elsewhere writing a value to where CARDSTATUS lives without you realising it. A common situation is a program that intends to write an 8-bit value to direct page (say address $0123), but the accumulator is 16-bit, so it writes to addresses $0123 and $0124 instead; if CARDSTATUS was at $0124... you get the idea

If this is a program you can run on an emulator, or "simulate" on an emulator through some predetermined ways (i.e. "emulate" the environment/thing you're working with by pre-initialising RAM/etc. with some values), then you could debug it easily (break on reads or writes exactly where CARDSTATUS lives, and/or break on execute on the questionable piece of code). But on actual hardware, you'd need an ICE.

Another option would be to write a small little test program of your own, run it in an emulator with debugging support (ex. bsnes-plus), and step through the code and look at behaviour/registers/bits yourself. Change the value in RAM before the routine starts then see how it changes. You get the idea.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: I've been thinking, AND ... here's what happened (or not

Post by koitsu »

BTW, if you don't believe me, go to Easy 6502 and throw in this code (note the first few commented-out lines):

Code: Select all

; pick one of the below 2:
; lda #$ff   ; this should cause brk to happen (bit 0 = 1)
; lda #$00   ; this should cause a pixel to show up (bit 0 = 0)
  sta $0f

  lda #$11   ; just a bogus value in the accumulator

CardCheckError:
  lda $0f
  sta $10
  and #1
  bne CardError

noerror:
  lda #5       ; green colour
  sta $330     ; graphical RAM
  jmp noerror

CardError:
  brk
Then try changing and #1 / bne CardError to and #1 / cmp #1 / beq CardError and note that there's no difference in behaviour. You can use the disassembler and/or the debugger (see checkbox under little graphical window) + step through your code and see what happens. The assembler does not support binary immediate syntax (i.e. #%).

This is a simple 6502 emulator, but the 65816 is identical in opcode behaviour in this case, so it's a valid test.

Edit: fix URL to Easy 6502.
Last edited by koitsu on Wed Mar 21, 2018 10:46 am, edited 1 time in total.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: I've been thinking, AND ... here's what happened (or not

Post by Oziphantom »

Code: Select all

   ldy   #errorCode
         ^
ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by ReaperSMS »

That is probably just loading a pointer to errorCode into Y, that gets parsed out by the Printf-like macro afterwards.

A disassembly of the two cases, with the actual instruction bytes interspersed, might indeed shed some light on things.

Is this function anywhere near a 64K boundary?
User avatar
Ramsis
Posts: 341
Joined: Sun Jul 01, 2012 6:44 am
Location: Lion's den :3
Contact:

Re: I've been thinking, AND ... here's what happened (or not

Post by Ramsis »

Thank you all, especially koitsu, for your invaluable feedback! :)

But, according to the Furry saying™, "Sometimes an octopus sees more than a primate's visual field can cover," after days and days of reflecting the matter I still don't get what's happening, and why AND #1/CMP #1/BEQ SOMEWHERE in this case yields a different result (i. e., carry bit) than AND #1/BNE SOMEWHERE does.
koitsu wrote:You don't state where CARDSTATUS lives
Yes I did. Please re-read my original post.
koitsu wrote:but you imply it's in direct page
No.
koitsu wrote:because you propose "zeroing out zero page" as a solution.
*Sigh* That was a silly joke. Sorry about confusing the confu page out of you. :wink:
koitsu wrote:My gut feeling is that your lda CARDSTATUS isn't using 24-bit addressing
But it duuuuzz (see above) :mrgreen:
koitsu wrote:Other weird things that might be be happening, but I have no way to confirm this (only you would know):
No, please. No. The mothf** eh code has been on GitHub for quite some time now, so no, not just me but hole wurlde, you n0.
koitsu wrote:* Possibly where CARDSTATUS is resides somewhere around where the stack pointer might be (or might overflow or underflow into)
* Possibly NMI routine is modifying some part of memory and changing values to something you don't expect
Checked it all, but unfortunately, no.
koitsu wrote:* Possibly there's a general bug in your program elsewhere writing a value to where CARDSTATUS lives without you realising it.
Have thought of this before, but like it or not ... no.
koitsu wrote:A common situation is a program that intends to write an 8-bit value to direct page (say address $0123), but the accumulator is 16-bit, so it writes to addresses $0123 and $0124 instead; if CARDSTATUS was at $0124... you get the idea
Exactly. But ... no. :(
koitsu wrote:If this is a program you can run on an emulator, or "simulate" on an emulator through some predetermined ways (i.e. "emulate" the environment/thing you're working with by pre-initialising RAM/etc. with some values), then you could debug it easily (break on reads or writes exactly where CARDSTATUS lives, and/or break on execute on the questionable piece of code). But on actual hardware, you'd need an ICE.
You sh***ing me? I've been running this on actual SNES PowerPak hardware ever since.

@Oziphantom No, it's fine (even though the comments in lib_strings.inc.asm are completely obsolete, and have been ever since ...)

@ReaperSMS
ReaperSMS wrote:That is probably just loading a pointer to errorCode into Y, that gets parsed out by the Printf-like macro afterwards.
This.
ReaperSMS wrote:Is this function anywhere near a 64K boundary?
Good idea, will check on that. (It's probably not though ...)
Some of my projects:
Furry RPG!
Unofficial SNES PowerPak firmware
(See my GitHub profile for more)
ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by ReaperSMS »

I don't have a great feel for '816 accum/index bit shenanigans, but what I see from poking at github:

https://github.com/Ramsis-SNES/snes-pow ... iables.asm has most of the relevant definitions

CARDSTATUS is defined as $A0800E
errorcode looks to be defined as $0002

https://github.com/Ramsis-SNES/snes-pow ... ce.inc.asm has the suspicious code

accumulator appears to be assumed to be 8-bit here
index looks like it should be 16-bit for the whole file

a quick look didn't reveal any spot that might have made it to CardCheckError with a 16-bit accumulator, but if it did it would probably be doing much stranger things.

a dump of the bytes starting at CardCheckError would still probably be useful
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: I've been thinking, AND ... here's what happened (or not

Post by nocash »

If you are sure that "Accu is 8 bits, X/Y are 16 bits" then you can sefely initialize it as so at the begin of the function. If you are still seeing the same error then you were probably right about it being initialized as so.

Another test would be to replace the "bne CardError" opcode by two dummy bytes (eg. two NOPs, or better: some 2-byte dummy instruction that has the same value as "bne CardError" in the second byte. If you still get the error then you know that something is wrong.

As already suggested by 1-2 people: You should really disassemble the binary code instead of just relying on your source code.
User avatar
Ramsis
Posts: 341
Joined: Sun Jul 01, 2012 6:44 am
Location: Lion's den :3
Contact:

Re: I've been thinking, AND ... here's what happened (or not

Post by Ramsis »

Thank you all (again, esp. koitsu).

What you guys all don't seem to realize though (good on you though, as working my way through the hell of a mess of the original SNES PowerPak firmware's code has been, and still is, a PITA, as opposed to my little thread here) is that CardCheckError isn't just any kind of subroutine with a presumed A8/16 issue. Instead, it's called (not only, but especially) every f**king 512 bytes (i. e., every sector) read from or written to the CF card.

So we know it's good in one case, but it immediately fails in the other.

IOW: I change

Code: Select all

AND #1/CMP #1/BEQ SOMEWHERE
to

Code: Select all

AND #1/BNE SOMEWHERE
, and I'm f***ed.

To rephrase this: Although the SNES PowerPak is known to work fine with

Code: Select all

AND #1/CMP #1/BEQ SOMEWHERE
called EVERY 512 BYTES read from the card, it refuses to even boot with

Code: Select all

AND #1/BNE SOMEWHERE
.

Magic!

Or (hopefully not, but) just me (again). :cry:
Some of my projects:
Furry RPG!
Unofficial SNES PowerPak firmware
(See my GitHub profile for more)
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: I've been thinking, AND ... here's what happened (or not

Post by lidnariq »

When I'm debugging embedded situations, the order I do things, from "easier to figure out what's going on" to "more guaranteed to work", is:
* Use a debugging simulator
* Use a captive debugger
* LED debugging
* LED debugging and a logic analyzer

The SNES does have a very small number of GPIO pins, and one's even present on the controller ports.
niconii
Posts: 219
Joined: Sun Mar 27, 2016 7:56 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by niconii »

I can think of a couple things to try.

1. Try replacing

Code: Select all

   and   #%00000001
   cmp   #%00000001
   beq   CardError
with

Code: Select all

   and   #%00000001
   nop
   nop
   bne   CardError
just to make certain there's nothing weird relying on how the code is positioned or its size.

2. Instead of lda CARDSTATUS, try some constant numbers to see how it behaves. What determines which branch it takes? Does it print the correct number?

If those fail, my general advice for debugging is this: Tear out every single bit of unrelated code until you have the absolute minimum needed to recreate the bug. Do not trust a single thing, even your print macros. Don't ignore things that "should" work, because the fact is your code doesn't work, or you wouldn't be here.
ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by ReaperSMS »

I'm feeling generous, so I pulled down the various tools from github and such.

wla-65816/wlalink's listing format is rather odd and suspect to the point of being nonsensical
main_rommapping.inc.asm produced some link errors, lines 44/46 referenced gameName.gCluster, which didn't exist. changing those to gameName.Cluster got it to get past that.

it does seem to generate the correct-looking bytes.

I might be hoping for too much, since your debugging approach so far has been you: "LOL THIS DOESN'T WORK" reasonable people: "Have you looked at they bytes?" you: "LOL THIS DOESN'T WORK", but...

does it die in the same fashion if you do

Code: Select all

CardCheckError:
   lda CARDSTATUS
   sta errorCode
   and #%00000001
   bne CardError
   rts

CardError:
   nop
   nop
   ClearLine 21 ...
instead? That keeps the code after aligned, which spits out a rom that only differs in about 8 bytes -- a couple in the timestamp, the new cardcheckerror, and the checksum bytes. If that works, then something is expecting some alignment elsewhere, that is getting screwed up by chopping 2 bytes out of the middle.
User avatar
Ramsis
Posts: 341
Joined: Sun Jul 01, 2012 6:44 am
Location: Lion's den :3
Contact:

Re: I've been thinking, AND ... here's what happened (or not

Post by Ramsis »

lidnariq wrote:When I'm debugging embedded situations, the order I do things, from "easier to figure out what's going on" to "more guaranteed to work", is:
* Use a debugging simulator
* Use a captive debugger
* LED debugging
* LED debugging and a logic analyzer

The SNES does have a very small number of GPIO pins, and one's even present on the controller ports.
Thanks, but unfortunately I don't have access to hardware-level based debugging tools. :-(
Nicole wrote:I can think of a couple things to try.

1. Try replacing

Code: Select all

   and   #%00000001
   cmp   #%00000001
   beq   CardError
with

Code: Select all

   and   #%00000001
   nop
   nop
   bne   CardError
just to make certain there's nothing weird relying on how the code is positioned or its size.

2. Instead of lda CARDSTATUS, try some constant numbers to see how it behaves. What determines which branch it takes? Does it print the correct number?

If those fail, my general advice for debugging is this: Tear out every single bit of unrelated code until you have the absolute minimum needed to recreate the bug. Do not trust a single thing, even your print macros. Don't ignore things that "should" work, because the fact is your code doesn't work, or you wouldn't be here.
Thanks indeed, Nicole, for your valuable input. I'll see how things turn out with a few NOPs here and there. :)
ReaperSMS wrote:I'm feeling generous, so I pulled down the various tools from github and such.
In case of trouble assembling The Source™, I'm usually very responsive, and willing to point out to you what you're doing wrong. You'd have to contact and ask me in the first place though ... :P
ReaperSMS wrote:wla-65816/wlalink's listing format is rather odd and suspect to the point of being nonsensical
Not my fault. Feel free to open an issue wherever if it bothers you that much.
ReaperSMS wrote:main_rommapping.inc.asm produced some link errors, lines 44/46 referenced gameName.gCluster, which didn't exist. changing those to gameName.Cluster got it to get past that.
*sigh* The current master branch is WIP, so sorry that I forgot to commit some variable renaming stuff. ;-)
ReaperSMS wrote:it does seem to generate the correct-looking bytes.
[irony]Ah. Really. That's so good to know indeed.[/irony]
ReaperSMS wrote:I might be hoping for too much, since your debugging approach so far has been you: "LOL THIS DOESN'T WORK" reasonable people: "Have you looked at they bytes?" you: "LOL THIS DOESN'T WORK", but...
Right. :lol:

But let's not forget:
ReaperSMS wrote:it does seem to generate the correct-looking bytes.
*plonk*
Some of my projects:
Furry RPG!
Unofficial SNES PowerPak firmware
(See my GitHub profile for more)
ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by ReaperSMS »

Did you try it with the extra nops, as suggested, or not?
93143
Posts: 1715
Joined: Fri Jul 04, 2014 9:31 pm

Re: I've been thinking, AND ... here's what happened (or not

Post by 93143 »

He just said he was going to.

This issue reminds me of an HDMA table I made once, using .db to write it out in code. It was only a few bytes long, and it would not work unless I added a dummy byte (can't remember the value) right after the very first byte. To this day I have no idea why, and every other HDMA table I've made has worked great. Programming can be tricky...
Post Reply