## 8x16 and whatever else unreg wants to know

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
If I have 00001111 in the accumulator... how could I check the bit value of the ones? Like how would I say check the value of bit #2? Is there a faster way to do this than

Code: Select all

``````    ror a
ror a
and #\$01
beq +
;code for a value of 1
+
;code for a value of 0
``````
Does that even work? I think it does.

Dwedit
Posts: 4351
Joined: Fri Nov 19, 2004 7:35 pm
Contact:
How about just "AND #\$04" to check bit 00000100?
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
Dwedit wrote:How about just "AND #\$04" to check bit 00000100?
But the result wouldn't be a 1 or a zero... But, beq only focuses on a 0. So... is that right?

edit: That's brilliant! Thanks Dwedit! Have a happy new year!

tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil
unregistered wrote:But the result wouldn't be a 1 or a zero... But, beq only focuses on a 0. So... is that right?
As you probably have realized, if the result is not 0 you know that the bit is set. If you wanted to copy any bit to the carry flag (sometimes we do that when rearranging bits) you could use an AND followed by a CMP:

Code: Select all

``````	;copy bit 2 to the carry flag
and #%00000100
cmp #%00000100``````
Then you can shift the bit into some variable using ROR or ROL, or do whatever else you want with it.

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
tokumaru[color=white],[/color] [url=http://nesdev.com/bbs/viewtopic.php?t=7451&postdays=0&postorder=asc&start=360]on page 25[/url][color=white],[/color] wrote:
unregistered wrote:How do I know if the something IS water? How do I check the tile number?
One of the ideas we had consisted in using 1 bit to select between water or air for the empty tiles, and 3 bits for the type of the solid tiles. In this case it would be easy to know if something is water, but since you decided to use the tile index...

Well, your screenArray only has the basic collision info, it doesn't say which tiles each block uses. The only way is to make screenArray hold the index of the metatiles instead of the collision info. That way, whenever you wanted to test for collisions, you'd have to get the metatile index at the position you want and use that to fetch the collision info from a table. With the same index you can easily access the tile indexes too. It's a bit more indirect, but that's not too bad.

The last part has me confused. If screenArray held "the index of the metatiles instead of the collision info" would that destroy our library and collision files? That would be terrible! My sister has done so many of them so far.

That first thing you mentioned about "using 1 bit to select between water or air for the empty tiles, and 3 bits for the type of the solid tiles." Since we have two extra bits could we set one of them to select between water or air for the empty tiles by itself (without the 3 bits for the solid type tiles)? Then wouldn't it be much easier to know if something is water?

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
tokumaru wrote:
unregistered wrote:But the result wouldn't be a 1 or a zero... But, beq only focuses on a 0. So... is that right?
As you probably have realized, if the result is not 0 you know that the bit is set. If you wanted to copy any bit to the carry flag (sometimes we do that when rearranging bits) you could use an AND followed by a CMP:

Code: Select all

``````	;copy bit 2 to the carry flag
and #%00000100
cmp #%00000100``````
Then you can shift the bit into some variable using ROR or ROL, or do whatever else you want with it.
This is good... and it works because
a)The AND puts 4 into the accumulator...
b)then... the CMP puts (Accumulator - Memory) in the Accumulator?
c)so now the Accumulator has a 0. Does that 0 clear the Carry flag?
d) ...I dont know why this works...Could you help me please?

tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil
unregistered wrote:a)The AND puts 4 into the accumulator...
No, it ANDs whatever is in the accumulator with 4, the result will only be 4 if bit 2 was set in the original value, otherwise it will be 0.
b)then... the CMP puts (Accumulator - Memory) in the Accumulator?
No, CMP does not change the value of the accumulator. It does calculate A - 4, but the result is not stored anywhere. What matters to us is the value of the carry after this subtraction: if the accumulator is 4, the subtraction will "succeed" (i.e. there will be no underflow) and the carry will be set. If the accumulator is 0, the subtraction will cause an underflow, clearing the carry. This causes the carry to become whatever bit 2 was in the first place, so we have effectively "copied" bit 2 into the carry flag.

Kasumi
Posts: 1292
Joined: Wed Apr 02, 2008 2:09 pm
Tokumaru covered CMP, but here's a little more explanation on what AND and the other bitwise operators (hey, why not?) do, and why what Dwedit suggested works:

AND (like ORA and EOR) at a basic level gives an output of 1 bit from 2 input bits. Order doesn't matter in any of them, so there are three cases.

1. Both bits are 0.
2. Both bits are 1
3. 1 bit is 1, and the other is 0.

AND gives an output of 1 if both input bits were 1. (if input bit 1 is 1 [true] AND input bit 2 is 1 [true], the result is true.) See how it got its name?

So, AND will only return 1 in case 2, otherwise it gives 0.

ORA gives an output of 1 if either input bit is one. (If input bit 1 is 1 [true] OR input bit 2 is 1[true], the result is true.) See how it got its name?

So ORA will return 1 in every case except case 1. It will return 0 in case 1.

EOR (Exclusive OR) gives an output of 1 if EXACTLY one of the input bits is 1. (If input 1 and ONLY input is 1 [true], the result is true. If input 2 and ONLY input 2 is 1[true], the result is true)

So EOR will return 1 only in case 3, otherwise it gives 0.

Now, at a basic level they work one bit at a time. The instructions work a byte at a time with corresponding bits in a byte.

Code: Select all

``````     #%00000000
^^^^^^^^
||||||||
BIT#:  76543210
#%00000000
``````
They check and return the result of bits 7 in each byte, and while that check is happening what's in bits 6-0 doesn't matter. Then it does bits 6, and while that check is happening what's in bits 7 and 5-0 doesn't matter. Etc.

AND is useful for checking the status of a specific bit, because by definition of AND, a bit with a 0 in either byte will return 0.

So if you AND with a number that has 7 bits as 0, the result of those seve bits will be 0. That means the bit you left 1 in the AND determines whether the accumulator is 0, or non-0.

Another use for AND is clearing a bit (setting it to 0). #%01111111 will clear the leftmost bit without changing the others. The 0 is guaranteed to clear the high bit because the 0 means both bits CAN'T be 1, and the 7 ones guarantee the other bits won't change.

EOR is useful for toggling a bit (0->1 or 1->0). This is because a 0 in an EOR is guaranteed to not change the bit it is being EOR'd with, while a 1 in an EOR is guaranteed TO change the bit it is being EOR'd with.

ORA is useful for setting a bit to 1. This is a because a 1 is guaranteed to give a result of 1, while a 0 will never change the bit you are ORA'ing with.

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
tokumaru, thank you so much for helping me with this!
tokumaru wrote:if the accumulator is 4, the subtraction will "succeed" (i.e. there will be no underflow) and the carry will be set.
No underflow? So water will not flow under the subtraction and that sets the carry? (trying to get you to teach me more about "underflow" - im interested)

----
tried to make this easier to read... for me at least... just added colors
Kasumi wrote:Tokumaru covered CMP, but here's a little more explanation on what AND and the other bitwise operators (hey, why not?) do, and why what Dwedit suggested works:

AND (like ORA and EOR) at a basic level gives an output of 1 bit from 2 input bits. Order doesn't matter in any of them, so there are three cases.

1. Both bits are 0.
2. Both bits are 1
3. 1 bit is 1, and the other is 0.

AND gives an output of 1 if both input bits were 1. (if input bit 1 is 1 [true] AND input bit 2 is 1 [true], the result is true.) See how it got its name?

So, AND will only return 1 in case 2, otherwise it gives 0.

ORA gives an output of 1 if either input bit is one. (If input bit 1 is 1 [true] OR input bit 2 is 1[true], the result is true.) See how it got its name?

So ORA will return 1 in every case except case 1. It will return 0 in case 1.

EOR (Exclusive OR) gives an output of 1 if EXACTLY one of the input bits is 1. (If input 1 and ONLY input is 1 [true], the result is true. If input 2 and ONLY input 2 is 1[true], the result is true)

So EOR will return 1 only in case 3, otherwise it gives 0.

Now, at a basic level they work one bit at a time. The instructions work a byte at a time with corresponding bits in a byte.

Code: Select all

``````     #%00000000
^^^^^^^^
||||||||
BIT#:  76543210
#%00000000
``````
They check and return the result of bits 7 in each byte, and while that check is happening what's in bits 6-0 doesn't matter. Then it does bits 6, and while that check is happening what's in bits 7 and 5-0 doesn't matter. Etc.

AND is useful for checking the status of a specific bit, because by definition of AND, a bit with a 0 in either byte will return 0.

So if you AND with a number that has 7 bits as 0, the result of those seve bits will be 0. That means the bit you left 1 in the AND determines whether the accumulator is 0, or non-0.

Another use for AND is clearing a bit (setting it to 0). #%01111111 will clear the leftmost bit without changing the others. The 0 is guaranteed to clear the high bit because the 0 means both bits CAN'T be 1, and the 7 ones guarantee the other bits won't change.

EOR is useful for toggling a bit (0->1 or 1->0). This is because a 0 in an EOR is guaranteed to not change the bit it is being EOR'd with, while a 1 in an EOR is guaranteed TO change the bit it is being EOR'd with.

ORA is useful for setting a bit to 1. This is a because a 1 is guaranteed to give a result of 1, while a 0 will never change the bit you are ORA'ing with.
Kasumi, thanks for helping us! The logic surrounding your cases is interesting. : ) There are shortcuts or cycles that can be saved, right? I have tried to think of one, but I can't think of anything...

Kasumi
Posts: 1292
Joined: Wed Apr 02, 2008 2:09 pm
unregistered wrote: No underflow?
You know how sbc works, right?
Let's assume the carry is set before the sbc. Let's assume we're working with unsigned numbers. (#\$FF = 255. Signed would mean #\$FF = -1)
So this

Code: Select all

``````  sbc foo
``````
will subtract foo from the accumulator. If the accumulator ends up as a larger number than what you started with (example: 0 - 1 = 255), the carry is cleared. That's underflow. If there was no underflow, the carry stays set.

(Overflow is the term for addition when you end up with a number lower than what you started with [example: 255+1 = 0])

CMP does nearly the exact same thing as SBC, except for two things:
1. CMP doesn't care about the carry flag's status before it's run. It could be cleared and an extra one would not be subtracted.
2. CMP doesn't store the result of the subtraction in the accumulator. A never changes.

CMP (or even SBC) works to compare numbers because cmping a larger number guarantees the carry will be clear, and vice versa. Think: If you subtract a larger number from a smaller number you will always pass 0. (And wrap to #\$FF on the 6502.)
If the number you subtract is the same, the carry stays sets and the zero flag is set because the result is 0.

What tokumaru was suggesting works because anything cmp'd with 0 that is not 0 gives a result that is not 0. And anything that is not zero clears the 0 flag.

Code: Select all

``````lda byte
and #%00010000;This guarantees the accumulator is either
;#%00000000 (beq would branch)
;or
;#%00010000 (bne would branch)

;If you cmp #%00010000, it actually reverses that for reasons that should be clear given the above explanations.
cmp #%00010000
;if the accumulator has #%00000000 bne would branch. #%00000000 - #%00010000 is not 0.
if the accumulator has #%00010000 beq would branch. #%00010000 - #%00010000 is 0
``````
unregistered wrote:There are shortcuts or cycles that can be saved, right? I have tried to think of one, but I can't think of anything...
Optimization is my favorite business, but it's a tricky, possibly dangerous one that's always specific to what you want to do. Get your code to work first, then make it fast.

Quick tips:
Even though I just typed a book describing it, avoid cmping to reverse the 0 flag. If you just use the other branch, you can avoid the cmp which always makes sense to me.

You shouldn't ever have to and #%10000000 to check that bit because bpl and bmi already check that highest bit when it's loaded

BIT puts the highest two bits (marked X) #%XXYYYYYY into flags you can branch on, but only for variables stored in a static place.

Because of that I always place the two things the need to be checked most often in those bits when I write data formats.

tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil
I know Kasumi already explained a lot but I still want to say a few things.
unregistered wrote:No underflow? So water will not flow under the subtraction and that sets the carry? (trying to get you to teach me more about "underflow" - im interested)
An overflow is when the result is too large to be represented by the accumulator, and an underflow is when it's too small.

For unsigned numbers, an overflow is when an addition results in a number larger than 255, and an underflow is when the result of a subtraction is less than 0. For signed numbers, an overflow means an addition with a result larger than 127 and an underflow is a subtraction with a result of less than -128.
Kasumi wrote:Because of that I always place the two things the need to be checked most often in those bits when I write data formats.
I often put my flags in the top 2 bits as well.

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
Thank you Kasumi and tokumaru!
Kasumi wrote:First disable rendering... by writing the relevant bits to \$2000
How do I disable rendering? The 7th bit of \$2000 starts or stops an NMI from running at the start of vertical blanking. Does that somehow disable rendering?

tokumaru
Posts: 11858
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil
unregistered wrote:
Kasumi wrote:First disable rendering... by writing the relevant bits to \$2000
How do I disable rendering? The 7th bit of \$2000 starts or stops an NMI from running at the start of vertical blanking. Does that somehow disable rendering?
Kasumi probably meant \$2001, where bits 3 and 4 enable or disable background and sprite rendering. To disable rendering you have to clear both bits.

Bit 7 of \$2000 controls whether NMIs fire or not when VBlank starts, but the rendering process isn't affected at all.

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
tokumaru, thank you so much!

unregistered
Posts: 1075
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas
tokumaru[color=orange], from [url=http://nesdev.com/bbs/viewtopic.php?p=85297#85297]page 27[/url],[/color] wrote:Do you want to use different data for each level or something like that? If that's the case, then the answer is the indirect indexed addressing mode (i.e. LDA (\$XX), Y). With that addressing mode you use pointers to define the tables that will be read, and you can alter the pointers as much as you want.

For example, you could have a different name for each collision table (or whatever table you want), and then make a table with all the addresses:

Code: Select all

``````MetatileCollisionAdresses:
.dw MetatileCollisionLevel1, MetatileCollisionLevel2, MetatileCollisionLevel3``````
Then you can read the address for the current level and put it in a pointer using the level's index
When you say level's index that could be 1 for level1 and 2 for level2 right?
:

Code: Select all

``````	lda LevelIndex ;get the level's index
asl ;multiply by 2 because each address is 2 bytes
tax ;use that as an index into the table of addresses
lda MetatileCollisionAdresses+0, x ;copy the low byte
sta MetatileCollision+0
lda MetatileCollisionAdresses+1, x ;copy the high byte
sta MetatileCollision+1``````
MetatileCollision isnt set up for an address of 2 bytes...
Then you can use indirect indexed addressing to read the data, instead of what we had before. The only real difference is that now you'll have to use Y as your index register, because this addressing mode doesn't work with X:

Code: Select all

``````	;get collision information
lda (MetatileCollision), y``````
I'm attempting to use this, for the first time, it's kind of confusing and kind of something to learn from. Hope it becomes less of the former and more of the latter; it will.