The carry flag is set for A >= Memory, because subtraction is actually treated as adding a negative value. Negative numbers are encoded using two's complement, which means that to negate a number you invert the bits and add 1. So, for example, if we want to compare $03 and $05 we negate $05 and add it to $03: $03 - $05 $03 + $fb This gives the answer $fe which is the two's complement encoding of -2. The carry flag is not set because this addition didn't overflow. If we instead compare $05 to $03 we get: $05 - $03 $05 + $fd This gives the answer $02, but also sets the carry flag since the actual result was $102. Similarly, if you compare two values that are equal, you will set the carry flag since the resulting addition will always be $100.
That means a lot! Thank you very much for the support! Also, Serbia? Nice! I’m half Hungarian, though I’ve only been to Eastern Europe once as a kid… :D
I'd say he gives some insight into assembly in general. Sure, every CPU does things differently, but I think I can assume there's enough similarities to get the gist of them all (except the PDP-8; that architecture is whack.)
So what do you all think about branching on the 6502? Stick around to the end of the episode where I present a coding challenge! *UPDATES* - There is a typo on line 20 of the example: it should read: “sta $02” (not $01). Thanks @Captain N! - Turns out there _is_ a slick math reason why the CMP instruction sets Carry Flag is set when A >= Memory. See the awesome comment by @Ben Smith below!
As an IT guy, who learned all the digital-tech stuff in school 25 years ago, and also had an NES as Kid (magical greybox) I gotta admit: I‘m eating up your 6502 content for hours straight since yesterday. Maybe it‘s time to get into programming again :)
Is this the way to do it without branching? ; Initialize Health, Damage, and the Return Value lda #25 sta $00 lda #30 sta $01 lda #0 sta $02 ; Check if Damage >= Health lda $01 cmp $00 rol A and #1 ; Set carry flag into $01 to to indicate the player has died sta $01 rts I do assembly on the Super Nintendo because I hack Super Mario World, but I think the Super Nintendo's processor is backwards compatible with the NES one.
@@NesHacker The question there is did you wanna teach cmp/bcc or and/rol and instruction cycle length. While and and rol might be good fodder for a crash course, the reason why you'd do it over just branching surely isn't. 😁 Gotta say, I'm enjoying your approach to the basics, Ryan. Watching because I need to deoxit my brain's contacts a little. It's all coming back to me, like falling off a bicycle. (Ouch.) Now what's the equivalent of applying rust converter paint to the chassis again?
Would have loved a bit of comment how/why rol & and are used here, for all us noobs not proficient in (S)nes dev (yet)... In the meantime I found a solution of "pushing the carry bit out" (ROL) - still wrapping my old head around it XD Love the videos, even if after 20years of trying to learn this it still all is just magic to me :)
Random question... what software do you use to create the stylized graphics that represent the various concepts in your videos? I think it is very well done and very clear! Thank you.
I feel like address $02 was forgotten in this subroutine. It looks like the result of the routine is supposed to be stored in $02, but it gets stored in $01 instead, overwriting the damage taken. This probably doesn't matter but I'd prefer using $02 to store results. lda #25 sta $00 lda #30 sta $01 lda #0 sta $02 lda $01 cmp $00 rol $02 rts
You’re correct. I made a mistake and accidentally used $01 instead of $02 (which is what I intended) in the example. Thanks for letting me know, another viewer had already done so, and I usually put corrections in a pinned comment on the videos. Thanks for watching and paying such close attention! :D
The 6502 processor sets the carry flag when the A reg is large that memory because when you do subtraction, the carry out will be on if the answer is positive, and off if it is negative. When it does subtraction it does a process called twos compliment in which, you flip the bits and then add on, when added to a number it subtracts if you ignore the carry out.
Oh I just hate you 😄 was about to go to bed and saw this upload. You b@stard! 😅 Thanks for making these. Could you make a review of several 6502 hobby boards? Would I be better off just using a NES without a NES10 chip than buying something modern? It would be lovely to see the NES go up against 2 to 3 modern 6502 boards.
I haven't messed with them too much but that's a great idea for a video! I'm finally getting all my electronics lab stuff setup so once I get the lab put back together I'd be more than happy to do an episode on the subject. Do you have any in particular you'd like my thoughts on? Unfortunately I can't answer your question about which to use right now, since I haven't done much with them, but you can't go wrong getting some vintage NES gear in my opinion haha.
I would avoid branching by storing the carry in the address $01: ; Initialize Health, Damage, and the Return Value lda #25 sta $00 lda #30 sta $01 lda #0 sta $02 ; Check if Damage >= Health lda $01 cmp $00 ; Set address $01 with the value of the carry to indicate the player has died or not lda #0 adc #0 sta $01 Amazing videos by the way! I hope you release more soon!
Amazing! I just didn´t understand why the result of the adc is stored on $01. I thought the return value should be stored in $02, since #0 was stored there on the beggining of the code.
@@ordozgoite honestly I don't know why the result of adc is stored on $01. Is not supposed that $01 stores the damage? should not be the result of adc stored on $02 or any new memory address since health and damage have already assigned their spaces? What about if an enemy does an incoming damage of 1 then?
Is this the solution to the "check hp without branching" puzzle? LDA $HP_LOCATION SEC SBC $DAMAGE_LOCATION ; If the HP is greater or equal to the Damage, the C flag will be set STA $HP_LOCATION LDA #$00 ADC #$00 ; If the carry is clear (Player is alive / HP is exactly 0 (Call the one extra point of HP benevolent Game Design ;)), 0 will be in A, else, the carry will factor in and A will contain 1 STA $DEAD_STATE (Hopefully this works the same as in 65816 asm. Love your videos!)
I really appreciate you doing these videos and putting so much time into making the visual representations easy to understand. I have s question or two. First, I get the N flag and the Z flag. That makes sense, but I don't really get why the C-flag would also set in if the A value is equal to the compared value... Isn't that whole point of the Z flag. And what if I only want something to happen if the compared value is higher than equal? Also for the very last part of the code that is executed if the carry flag is 0 (Lethal hit) Should it be $02 that gets a value of #1, not $01?
So you’re right! I have a typo and it should have been $02 instead of $01 there! As far as I understand it, the Carry Flag is set this way so we can check all of the inequalities () via a single flag. If the 6502 only set the Carry when A > M then I’m pretty sure we’d have to use two checks in order to handle the “>=“ and “=“ and “
The only thing constantly messing me up is not always having a solid example to base the code on. Grasping everything so far, but not the purpose behind it. For example, I learned swapping bytes back and forth but am not entirely sure why I would ever be dong that. I also don't understand the order of operations. What exactly should the very top of my code be? Initializing values, or drawing windows and borders and loading graphics? Fantastic tutorial, but I would have really liked if you took an existing ROM and walked us through how it's built, why everything is coded where it is and what each line of code is doing in the game
@ 3:58 - are absolute addresses always reversed in their 2 bytes? If I'm looking in the ROM code for a reference to RAM address $0321 then I should do a HEX string search for "2103"? Or if I know the instruction is STA, it should be found as "8D2103" somewhere in the ROM code? Right? Or does this code look different in the ROM file? Because I'm having trouble finding code for a certain "lives count" address.
Yes. The 6502 is a little-endian machine, meaning that when it stores multi-byte values it always stores the less significant byte first, so when you write $2103 the assembler will assemble it to 0321 because the processor expects you to give it the low byte first and then the high byte after
Amateur C64 programmer here. I note the absence of the Break flag on the NES. I'm assuming this is because there's simply no need for it at the user level on a system whose only function is to play cartridge games.
@@peterponomarev1622 BMI and BPL are really good for terminating loops though when iterating through a set of data, because you will also need to complete your loop when the counter is 0. So right after a decrement instruction you can BPL back to the start of the loop.
thanks for the little coding challenge; it got me to set up a dev enviroment and write some NES code for the first time! my solution: ; Initialize Health, Damage, and the Return Value lda #17 sta $00 lda #33 sta $01 lda #0 sta $02 ; Set Carry iff Damage >= Health lda $01 cmp $00 lda #0 adc #0 ; Set address $02 to death state (1=dead,0=alive) sta $02 rts
@@jakeelliott1512 So when you use ADC to add 0 to 0 it will result in a 1 if the carry flag is set (this is why it’s called Add with Carry). At this point the A register has a value of 1 if and only if the damage >= health (because the CMP instruction above only sets the carry flag in this case). So they write out the value to $02 where we want to store the result. Hope that helps :)
OMFG can you STOP that up and down voice thing. It's like a girl that keeps up pitch talking then rapidly cycling. I'm sure you are knowledgeable and have useful information to convey but oh my God it is painful to hear people talk like this. ValleyGirl would be less painful.
The carry flag is set for A >= Memory, because subtraction is actually treated as adding a negative value.
Negative numbers are encoded using two's complement, which means that to negate a number you invert the bits and add 1. So, for example, if we want to compare $03 and $05 we negate $05 and add it to $03:
$03 - $05
$03 + $fb
This gives the answer $fe which is the two's complement encoding of -2. The carry flag is not set because this addition didn't overflow.
If we instead compare $05 to $03 we get:
$05 - $03
$05 + $fd
This gives the answer $02, but also sets the carry flag since the actual result was $102.
Similarly, if you compare two values that are equal, you will set the carry flag since the resulting addition will always be $100.
Wow, that is a slick mathematic explanation, nicely stated!
Hey Ben, I love how I keep bumping into you, even on random RUclips comments :)
@@amaiorano Heh, true! Good to see you Antonio! Working on a NES emulator or just watching some good vids?
@@bensmith218 Always wanted to write an NES game myself, and now my son wants to as well! These videos are great.
Easily the most approachable 6502 assembly course on RUclips. Love the series, Ryan. You earned a sub and like on every video. Greetings from Serbia!
That means a lot! Thank you very much for the support! Also, Serbia? Nice! I’m half Hungarian, though I’ve only been to Eastern Europe once as a kid… :D
@@NesHacker Wow, that is awesome. After the pandemics end, you should definitely visit more. Budapest is my favorite destination. :D
I'd say he gives some insight into assembly in general. Sure, every CPU does things differently, but I think I can assume there's enough similarities to get the gist of them all (except the PDP-8; that architecture is whack.)
So what do you all think about branching on the 6502? Stick around to the end of the episode where I present a coding challenge!
*UPDATES*
- There is a typo on line 20 of the example: it should read: “sta $02” (not $01). Thanks @Captain N!
- Turns out there _is_ a slick math reason why the CMP instruction sets Carry Flag is set when A >= Memory. See the awesome comment by @Ben Smith below!
To make the routine branch-less I would
lda #0
rol
Which pushes the carry flag into the lowest bit of A which can be used as the output
As an IT guy, who learned all the digital-tech stuff in school 25 years ago, and also had an NES as Kid (magical greybox) I gotta admit: I‘m eating up your 6502 content for hours straight since yesterday.
Maybe it‘s time to get into programming again :)
Sweet :), glad you’re enjoying the channel!
Is this the way to do it without branching?
; Initialize Health, Damage, and the Return Value
lda #25
sta $00
lda #30
sta $01
lda #0
sta $02
; Check if Damage >= Health
lda $01
cmp $00
rol A
and #1
; Set carry flag into $01 to to indicate the player has died
sta $01
rts
I do assembly on the Super Nintendo because I hack Super Mario World, but I think the Super Nintendo's processor is backwards compatible with the NES one.
Beautiful! Yep, that was pretty much exactly what I was thinking :D
@@NesHacker The question there is did you wanna teach cmp/bcc or and/rol and instruction cycle length. While and and rol might be good fodder for a crash course, the reason why you'd do it over just branching surely isn't. 😁
Gotta say, I'm enjoying your approach to the basics, Ryan. Watching because I need to deoxit my brain's contacts a little. It's all coming back to me, like falling off a bicycle. (Ouch.) Now what's the equivalent of applying rust converter paint to the chassis again?
Would have loved a bit of comment how/why rol & and are used here, for all us noobs not proficient in (S)nes dev (yet)... In the meantime I found a solution of "pushing the carry bit out" (ROL) - still wrapping my old head around it XD
Love the videos, even if after 20years of trying to learn this it still all is just magic to me :)
Thank you for your videos. You have an awesome channel. Great explanation and production quality. Please keep them coming. Thank you
Thanks for watching them! It’s always a thrill when folks watch the new videos right after I put them out :)
Random question... what software do you use to create the stylized graphics that represent the various concepts in your videos? I think it is very well done and very clear! Thank you.
I do custom graphics using Adobe Illustrator and animate them using After Effects.
I feel like address $02 was forgotten in this subroutine. It looks like the result of the routine is supposed to be stored in $02, but it gets stored in $01 instead, overwriting the damage taken. This probably doesn't matter but I'd prefer using $02 to store results.
lda #25
sta $00
lda #30
sta $01
lda #0
sta $02
lda $01
cmp $00
rol $02
rts
You’re correct. I made a mistake and accidentally used $01 instead of $02 (which is what I intended) in the example. Thanks for letting me know, another viewer had already done so, and I usually put corrections in a pinned comment on the videos. Thanks for watching and paying such close attention! :D
The 6502 processor sets the carry flag when the A reg is large that memory because when you do subtraction, the carry out will be on if the answer is positive, and off if it is negative. When it does subtraction it does a process called twos compliment in which, you flip the bits and then add on, when added to a number it subtracts if you ignore the carry out.
Oh I just hate you 😄 was about to go to bed and saw this upload. You b@stard! 😅
Thanks for making these. Could you make a review of several 6502 hobby boards? Would I be better off just using a NES without a NES10 chip than buying something modern? It would be lovely to see the NES go up against 2 to 3 modern 6502 boards.
I haven't messed with them too much but that's a great idea for a video! I'm finally getting all my electronics lab stuff setup so once I get the lab put back together I'd be more than happy to do an episode on the subject. Do you have any in particular you'd like my thoughts on?
Unfortunately I can't answer your question about which to use right now, since I haven't done much with them, but you can't go wrong getting some vintage NES gear in my opinion haha.
I would avoid branching by storing the carry in the address $01:
; Initialize Health, Damage, and the Return Value
lda #25
sta $00
lda #30
sta $01
lda #0
sta $02
; Check if Damage >= Health
lda $01
cmp $00
; Set address $01 with the value of the carry to indicate the player has died or not
lda #0
adc #0
sta $01
Amazing videos by the way! I hope you release more soon!
Amazing! I just didn´t understand why the result of the adc is stored on $01. I thought the return value should be stored in $02, since #0 was stored there on the beggining of the code.
@@ordozgoite honestly I don't know why the result of adc is stored on $01. Is not supposed that $01 stores the damage? should not be the result of adc stored on $02 or any new memory address since health and damage have already assigned their spaces?
What about if an enemy does an incoming damage of 1 then?
@@ordozgoite In fact I still don't get it why Ryan set address $01 instead of $02 to indicate if the player has died or not.
Just copy the carry flag into the $01
No branch instruction needed
Indeed :)
Is this the solution to the "check hp without branching" puzzle?
LDA $HP_LOCATION
SEC
SBC $DAMAGE_LOCATION ; If the HP is greater or equal to the Damage, the C flag will be set
STA $HP_LOCATION
LDA #$00
ADC #$00 ; If the carry is clear (Player is alive / HP is exactly 0 (Call the one extra point of HP benevolent Game Design ;)), 0 will be in A, else, the carry will factor in and A will contain 1
STA $DEAD_STATE
(Hopefully this works the same as in 65816 asm. Love your videos!)
It’s close but it doesn’t give the *exact* same output. But bonus points for “benevolent game design,” haha. Thanks for watching!
;lets assume HP=$00 , DMG=$01
lda $01
cmp $00
bcc gameoverYEAHHHHHHHHHHHHHHHH
rts;
gameoverYEAHHHHHHHHHHHHHHHH:
;insert code
Is the above code equivalent to this?
void isAlive(uint_8 hp,uint_8 dmg)//
{
if(dmg>=hp)
gameover(); //insert code
return;
}
Most of the things are similar to Assembly Language and Architecture of 8085 Microprocessor
I really appreciate you doing these videos and putting so much time into making the visual representations easy to understand.
I have s question or two.
First, I get the N flag and the Z flag. That makes sense, but I don't really get why the C-flag would also set in if the A value is equal to the compared value... Isn't that whole point of the Z flag. And what if I only want something to happen if the compared value is higher than equal?
Also for the very last part of the code that is executed if the carry flag is 0 (Lethal hit)
Should it be $02 that gets a value of #1, not $01?
So you’re right! I have a typo and it should have been $02 instead of $01 there!
As far as I understand it, the Carry Flag is set this way so we can check all of the inequalities () via a single flag. If the 6502 only set the Carry when A > M then I’m pretty sure we’d have to use two checks in order to handle the “>=“ and “=“ and “
@@NesHacker I guess that makes sense. This is still really new to me so I'm just trying to wrap my head around the logic of all of it.
The only thing constantly messing me up is not always having a solid example to base the code on.
Grasping everything so far, but not the purpose behind it.
For example, I learned swapping bytes back and forth but am not entirely sure why I would ever be dong that.
I also don't understand the order of operations. What exactly should the very top of my code be? Initializing values, or drawing windows and borders and loading graphics?
Fantastic tutorial, but I would have really liked if you took an existing ROM and walked us through how it's built, why everything is coded where it is and what each line of code is doing in the game
Liked and subbed! I love this channel and this series
Appreciate it! There’s so much more cover, so I’ll be making a LOT more videos, haha XD
Great video Ryan! I hope to see next video soon! Keep it up.
Appreciate it!
@ 3:58 - are absolute addresses always reversed in their 2 bytes? If I'm looking in the ROM code for a reference to RAM address $0321 then I should do a HEX string search for "2103"? Or if I know the instruction is STA, it should be found as "8D2103" somewhere in the ROM code? Right? Or does this code look different in the ROM file? Because I'm having trouble finding code for a certain "lives count" address.
Yes. The 6502 is a little-endian machine, meaning that when it stores multi-byte values it always stores the less significant byte first, so when you write $2103 the assembler will assemble it to 0321 because the processor expects you to give it the low byte first and then the high byte after
Amateur C64 programmer here. I note the absence of the Break flag on the NES. I'm assuming this is because there's simply no need for it at the user level on a system whose only function is to play cartridge games.
If Damage < Health, couldn't BMI be used as well instead of BCC? Or am I not understanding? Thanks.
I think I just answered my own question ... The carry flag would be set on greater than OR equal, both of which result in Game Over
@@peterponomarev1622 BMI and BPL are really good for terminating loops though when iterating through a set of data, because you will also need to complete your loop when the counter is 0. So right after a decrement instruction you can BPL back to the start of the loop.
This was very helpful thanks
I loved this
Excellent tutorial, thanks!
Amazing course this it is!
I LOVE C64 👍🥂🎩
Simply sublime 😃
Thanks!
thanks for the little coding challenge; it got me to set up a dev enviroment and write some NES code for the first time! my solution:
; Initialize Health, Damage, and the Return Value
lda #17
sta $00
lda #33
sta $01
lda #0
sta $02
; Set Carry iff Damage >= Health
lda $01
cmp $00
lda #0
adc #0
; Set address $02 to death state (1=dead,0=alive)
sta $02
rts
Nice!
@@jakeelliott1512 So when you use ADC to add 0 to 0 it will result in a 1 if the carry flag is set (this is why it’s called Add with Carry). At this point the A register has a value of 1 if and only if the damage >= health (because the CMP instruction above only sets the carry flag in this case). So they write out the value to $02 where we want to store the result. Hope that helps :)
keep making videos.
OMFG can you STOP that up and down voice thing. It's like a girl that keeps up pitch talking then rapidly cycling. I'm sure you are knowledgeable and have useful information to convey but oh my God it is painful to hear people talk like this. ValleyGirl would be less painful.