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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

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. :oops:
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

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! :D Have a happy new year! :)
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

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. :shock:

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? :oops:
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

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?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

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.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

tokumaru, thank you so much for helping me with this! :D
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! :D 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... :oops:
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

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... :oops:
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. :lol: 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.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

Thank you Kasumi and tokumaru! :D
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? :?
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

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: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Post by unregistered »

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

Post by unregistered »

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. :)
Post Reply