This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

"Philosophical question": Unnamed labels

"Philosophical question": Unnamed labels
by on (#170246)
One thing that I have seen in various codes is the use of unnamed labels.

I haven't used them myself, but from what I've seen, I assume that : is an unnamed label and that JMP :+ means "go down to the next unnamed label", JMP :- means "go up to the next unnamed label" and JMP :++ means "go down until you are at the second unnamed label that you encounter".

My question is: Why do people use this? Is there any advantage over named labels?

I like the idea of local labels very much, giving me the chance to use names like @end and @loop in multiple functions.
But I've never seen the appeal of unnamed labels.

In fact, I only see disadvantages:

1. When reading the code, you cannot readily see where the jump goes to. You have to take the number of pluses or minuses and then count through all the colons to check where you end up.
With named labels, it is much clearer where the jump goes to. Especially in text editors like Notepad++ where I just need to mark the name and then the other occurences of the name get a colored background.

2. The unnamed labels are error-prone.
Remove a label and forget to remove the references: Your code has just gotten a bug.
Remove a label and forget to update the references that don't even have to do with it, but that point to other unnamed level: Your code has just gotten a bug.
Reorder code (switching around two sections that each start with a label) and forget to switch the number of pluses and minuses elsewhere in the code: Your code has just gotten a bug.
But remove a named label and forget to remove the references and the compiler will give you an error message.
And switch around two code sections that start with named labels and the rest of the code can still remain as it is.

So, where's the advantage? What's the reason to ever use unnamed labels?
Re: "Phylosophical question": Unnamed labels
by on (#170247)
The syntax of : which is referred with :- or :+ is an idiom of CA65. I actually much prefer the way XA65 does it, i.e. the label is -, --, or --- if it is only entered with backwards branches, and +, ++, +++ if forward branches, and the same label occurs in both the label and the branch instruction. They can also be nested; the longer the sequence minuses or pluses, the larger its scope is.

As for why? It allows brevity. You don't need to assign a name for every single thing you do. Sometimes a loop is just a loop. An "if" is just an "if". No need to attach sentiment and deeper meaning to it. The short label is just the means to the end; it is minimal so as not to distract the viewer from the actual topic at hand. The same reason why { and } are just single characters in C. Compared to using a goto with a label, the same potential problems with error propensity arise, although the mandatory pairing helps mitigate it a little.
Re: "Phylosophical question": Unnamed labels
by on (#170248)
In my code, I don't use more than one + or -, and the target is almost always within ten lines. It might be a really tight loop:
Code:
  ; Wait for NMI handler to signal vertical blanking
  lda nmis
  :
    cmp nmis
    beq :-

Code:
  ; Load initial palette
  ldx #$3F
  stx PPUADDR
  ldx #$00
  stx PPUADDR
  :
    lda palette,x
    sta PPUDATA
    inx
    cpx #32
    bcc :-


Or an addition of a 16-bit signed velocity to a 24-bit position, to skip over adjustment of the most significant byte if not needed:
Code:
  ; Add velocity to position
  clc
  lda actor_xvel_sub,x
  adc actor_x_sub,x
  sta actor_x_sub,x
  lda actor_xvel,x
  bpl :+
    dec actor_x_hi,x
  :
  adc actor_x,x
  bcc :+
    inc actor_x_hi,x
  :
  sta actor_x,x

For longer branches than about a half dozen lines, I come up with a name for the target that reflects what the code does. For example, a forward branch skipping over something might be called bcc not_in_wall or beq not_start. But these are cases where what the code does is self-evident from the variable names.
Re: "Phylosophical question": Unnamed labels
by on (#170250)
DRW wrote:
I assume that : is an unnamed label and that JMP :+ means "go down to the next unnamed label", JMP :- means "go up to the next unnamed label" and JMP :++ means "go down until you are at the second unnamed label that you encounter".

This is ca65's notation, yes.

Quote:
My question is: Why do people use this? Is there any advantage over named labels?

You don't have to think of names for EVERY. SINGLE. BRANCH.

Quote:
1. When reading the code, you cannot readily see where the jump goes to. You have to take the number of pluses or minuses and then count through all the colons to check where you end up.

I don't know how other people use their unnamed labels, but I only use them for really short jumps. As for having to count the number of pluses/minuses, that's the one annoying thing about ca65's approach to unnamed labels IMO. If you're editing a block of code and need to add more unnamed labels, you have to look for all nearby references to unnamed labels and fix the ones that need fixing. In these situations I tend to use unique labels (not necessarily meaningful, sometimes I just hit random keys) while editing, and only change everything to unnamed labels once that part of the logic is done. This is the reason I like ASM6's approach better.

Quote:
Remove a label and forget to remove the references: Your code has just gotten a bug.
Remove a label and forget to update the references that don't even have to do with it, but that point to other unnamed level: Your code has just gotten a bug.
Reorder code (switching around two sections that each start with a label) and forget to switch the number of pluses and minuses elsewhere in the code: Your code has just gotten a bug.
But remove a named label and forget to remove the references and the compiler will give you an error message.

True. This is the main drawback of ca65's unnamed labels.

Quote:
So, where's the advantage? What's the reason to ever use unnamed labels?

Like I said, in small sections of code or small loops where all decisions are straightforward and don't need any explanation. I find some operations more readable without a big label splitting up the code. For example, I have this code to hide all unused sprites:

Code:
   ;hide all unused sprites
   ldy Video::AvailableSprites
   beq Return
   ldx Video::NextBackSprite
   lda #Video::HIDDEN_SPRITE_Y
   bne :++
:   dex
   dex
   dex
   dex
:   sta Video::OAMBuffer+Video::SPRITE_Y, x
   dey
   bne :--

The comment at the top describes what the entire block of code does, and it's a very small and simple piece of logic that doesn't require further explanations, so I think it's more readable to have the entire operation tightly packed in a small block.
Re: "Philosophical question": Unnamed labels
by on (#170254)
I don't use them personally. I don't even use local (@) labels that often (out of habit), since I tend to wrap most of my functions in .proc blocks, and sometimes also use .scope blocks within the .proc.

Never really had a problem coming up with label names.

Personally, I would do something like this for tokumaru's snippet:
Code:
    ;hide all unused sprites
    ldy Video::AvailableSprites
    beq Return
    ldx Video::NextBackSprite
    lda #Video::HIDDEN_SPRITE_Y
    bne startLoop
    loop:
        dex
        dex
        dex
        dex
    startLoop:
        sta Video::OAMBuffer+Video::SPRITE_Y, x
        dey
    bne loop
Re: "Philosophical question": Unnamed labels
by on (#170256)
thefox wrote:
Personally, I would do something like this for tokumaru's snippet:

I kinda feel like "loop" and "startLoop" break the flow of the logic. I can visualize it better without the names. I also don't like indenting assembly code... I think it looks weird with labels, and since the flow of assembly is less restricted, sometimes the logic doesn't map well to indented blocks.

I make heavy use of .scope blocks (not .proc though - I use my own version that allows for the entry point to be in places other than the very top), so I too don't feel the need to use local (@) labels.
Re: "Philosophical question": Unnamed labels
by on (#170257)
DRW wrote:
My question is: Why do people use this? Is there any advantage over named labels?

Convenience, specifically for simple/obvious loops that tend to be small. Anything substantially complex (I would say more than two unnamed labels), IMO, warrants use of an actual label. The code snippets tepples provided (esp. the first two) are good working examples of where use of an unnamed label still makes for legible ease-of-understanding code. The bright side to unnamed labels is: you don't have to use them. :-) I try not to get hung up on things like this -- I'd rather be writing actual code than pontificating over the pros/cons of optional assembler features.
Re: "Philosophical question": Unnamed labels
by on (#170258)
I use them constantly. I like the way they look, they're quick to type. I think for simple branches they're easier to read than something like @loop.

I use :++ less often, and rarely use :+++, but I don't find them very error-prone, in my experience. Part of it is that I use a style with indentation to indicate structure, so it's clear to me where it's going. My : usage is similar to how I would use C's { and } with control structures. Another part of it is that I don't use them if I think the branching is complicated, or over a long span; this is exclusively for short blocks of code.

One thing you might not know is that ca65 will generate an error if there is no reference to an unnamed label. This catches a lot of the potential errors when making code revisions around these things.
Re: "Philosophical question": Unnamed labels
by on (#170260)
But ca65 also doesn't scope unnamed labels. I can define one in one .proc and accidentally refer to it in another. Usually the result is a "Range error", but sometimes the result is a silent successful assembly of a defective program.
Re: "Philosophical question": Unnamed labels
by on (#170262)
Yes, there are always potential errors, but I think I've only had to debug an erroneous : maybe 2 times in the past year, whereas I think the assembler has caught unreferenced unnamed label errors maybe 10 times? They way I work, the kind of mistakes I make with them tend to produce that assembly error more often than not.

Your mileage may vary. I don't find they cause me much grief, compared to a lot of other things.

I have thousands of unnamed labels in the code for my current project; that's thousands of times I didn't have to think of or read a name. ;)
Re: "Philosophical question": Unnamed labels
by on (#170275)
Yeah, not having to think names for all those dumb branches is probably the main reason why unnamed labels exist.

One other big problem I don't see mentioned is that a lot of assemblers seem to simply not support local labels properly (or at all), period, and when they do they may disagree on the syntax, while unnamed labels seem to have better support and tend to be mostly the same across tools. No idea how bad it is on the 6502, I know it can easily become hell with other architectures though.
Re: "Philosophical question": Unnamed labels
by on (#170278)
Also, for disassemby, i think the use is reasonable until you've figured out what stuff is for for sure.
Re: "Philosophical question": Unnamed labels
by on (#170282)
Instead of anonymous labels, DASM has two types of temporary labels: the traditional kind whose scope is defined by a pseudo-op, and a super-temporary kind whose scope is the next non-temporary label. The latter kind are like anonymous labels in that the scope is (meant to be) very small, and you can drop one in without disturbing the scope of anything else. It's not any better or worse than CA65's anonymous labels, just different.
Re: "Philosophical question": Unnamed labels
by on (#170284)
The author of the CA65 help file doesn't seem to like them.

Like everyone else here, I use them only for very short jumps in smaller routines. But quite rarely, and in all cases I have used them I think they could be replaced by a local label with very simple general names like @loop, @skip or @exit, and still be equally readable. Generally I prefer to have names on things, but if it's a really small routine it's fine either way.

The only advantages over local labels I can think of is that they are fast to type, fast to read and doesn't require any global labels around them.
Re: "Philosophical question": Unnamed labels
by on (#170285)
tokumaru wrote:
thefox wrote:
Personally, I would do something like this for tokumaru's snippet:

I kinda feel like "loop" and "startLoop" break the flow of the logic. I can visualize it better without the names. I also don't like indenting assembly code... I think it looks weird with labels, and since the flow of assembly is less restricted, sometimes the logic doesn't map well to indented blocks.

It probably comes down to what each of us is used to. It's true that not all assembly code can be indented properly, but most of the common structures can (loop back, branch forward on a negated condition, ...).
Re: "Philosophical question": Unnamed labels
by on (#170286)
rainwarrior wrote:
My : usage is similar to how I would use C's { and } with control structures.

Only in C you cannot jump from one } back to a { that it doesn't belong to. Unlike unnamed labels, they always come in pairs.

rainwarrior wrote:
One thing you might not know is that ca65 will generate an error if there is no reference to an unnamed label. This catches a lot of the potential errors when making code revisions around these things.

I pretty much assumed that the compiler will catch a jump to an unnamed label that doesn't exist (i.e. using :+++ even though there are only two : after it). I mean, what else should the compiler do but give an error? I was exclusively talking about situations where the code is still syntactically correct.

rainwarrior wrote:
Yes, there are always potential errors, but I think I've only had to debug an erroneous : maybe 2 times in the past year, whereas I think the assembler has caught unreferenced unnamed label errors maybe 10 times?

O.k., sorry, but that comparison is beyond faulty. Really. That's so absolutely apples vs. oranges:

"I had 10 compiler errors with named labels and 2 runtime errors with unnamed labels. 2 errors < 10 errors. Therefore unnabed labels win."

You cannot do this because having a compiler error is a good thing. It means the compiler caught something before it slipped into your program.
So, instead the calculation goes:

"I had 2 runtime errors with unnamed labels. And I was prevented 10 times from having a runtime error with named labels. So, the score is 10 points for named labels. And 2 minus points for unnamed labels. The end result is 12:0 for named labels."

Pokun wrote:
The only advantages over local labels I can think of is that they are fast to type, fast to read and doesn't require any global labels around them.

Fast to type: Yes.

Fast to read (i.e. faster than named labels): Definitely no. It is much faster to read @end and to find the other @end in the code than it is to read :+++ and to count three : until you're at the destination location.

And do you really have those many labels of the same type (i.e. where the names would clash) within the same function (i.e. within the same global label)?
Re: "Philosophical question": Unnamed labels
by on (#170290)
Multiple loop: in one .proc? Certainly, and it causes an assembly error until I rename at least all but one.
Re: "Philosophical question": Unnamed labels
by on (#170291)
Yes, it's quite common to need several "loop" and "skip" labels, in which case local labels won't be of any help. Like Bisqwit said, sometimes a loop if just a loop, and an if is just an if. If you can get by without naming every if and every loop in high-level languages, you can do the same in assembly.
Re: "Philosophical question": Unnamed labels
by on (#170303)
As for documentation: I usually explain in comments what the code does*, even going as far as writing the task out in pseudo code (or even real C). This comes naturally, as I usually prototype the difficult parts in C++ or PHP before writing them out in assembler.
Example:
Code:
        ; To save RAM, the second plane was put into stack, in reverse order.
        ; We can't just use PLA+STA in a loop, or we'll reverse the data.
        tsx
        ; OldStackPointer     = X
        ; CorrectStackPointer = X+8
        ; for(Y=CorrectStackPointer; Y!=OldStackPointer; Y--) Send($100[Y]);
        @old = RowsRemaining ; reuse as temp
        txa
        sta @old
        adc #(8-1) ; C was set. Clears C.
        tax
:       ldy $100,x
        sty $2007
        dex
        cpx @old
        bne :-
        ; Restore stack pointer _after_ reading $100, so that
        ; a possible interrupt happening in the middle of the
        ; loop will not clobber our stack data.
        tax
        txs

*) This principle may slip if I have spent so much time with the code that the code seems blatantly obvious.
Re: "Philosophical question": Unnamed labels
by on (#170305)
DRW wrote:
rainwarrior wrote:
[...]I've only had to debug an erroneous : maybe 2 times in the past year, whereas I think the assembler has caught unreferenced unnamed label errors maybe 10 times?

[...]
"I had 10 compiler errors with named labels and 2 runtime errors with unnamed labels. 2 errors < 10 errors. Therefore unnabed labels win."

Pardon the interruption, but there's some confusion here. Rainwarrior was talking about unnamed labels only, he wasn't comparing unnamed and named.

@Bisqwit: I do the exact same thing for the same reasons. I also leave comments when I try an optimization that doesn't work, so I don't see that optimization again two years later and try it out again.

Named labels help out with documentation, unless it really is just a supershort loop or a single-instruction skip, but even if you prefer using unnamed labels as much as you can, you could also leave a comment explaining the branch, and that'd be just the same.
Re: "Philosophical question": Unnamed labels
by on (#170307)
Drag wrote:
Pardon the interruption, but there's some confusion here. Rainwarrior was talking about unnamed labels only, he wasn't comparing unnamed and named.

In a way, he was. He said this:
rainwarrior wrote:
Yes, there are always potential errors, but I think I've only had to debug an erroneous : maybe 2 times in the past year, whereas I think the assembler has caught unreferenced unnamed label errors maybe 10 times?

The way he says it, it sounds like he compares the times that named labels bothered him with the time that unnamed labels bothered him and comes to the conclusion that it's 10 vs. 2.
Which is not a fair comparison because the two errors were actual bugs while the 10 errors were prevented bugs.
The unnamed labels probably wouldn't have prevented the problems in those 10 instances. Instead, they would have shifted the error from compile time to runtime.
Re: "Philosophical question": Unnamed labels
by on (#170308)
DRW wrote:
rainwarrior wrote:
My : usage is similar to how I would use C's { and } with control structures.

Only in C you cannot jump from one } back to a { that it doesn't belong to. Unlike unnamed labels, they always come in pairs.

No, I'm not talking literally about the characters { and } but the scoped structure they represent. There is a clear start and end (which I tend to indicate with indentation), structures like: for, while, if. The cases where I use : are very similar to those.

DRW wrote:
rainwarrior wrote:
One thing you might not know is that ca65 will generate an error if there is no reference to an unnamed label. This catches a lot of the potential errors when making code revisions around these things.

I pretty much assumed that the compiler will catch a jump to an unnamed label that doesn't exist (i.e. using :+++ even though there are only two : after it). I mean, what else should the compiler do but give an error? I was exclusively talking about situations where the code is still syntactically correct.

No, it gives errors in the opposite way from that.

The assembler will catch an unnamed label with no references to it. If you type : and then nothing branches to it, this indicates that you've made a logical error, so it fails for you.

The case you just brought up is actually a problem, because unnamed labels are unscoped. A :+++ to nowhere will reach down through your code until it finds a suitable unnamed label to attach to. Because the common use case for this is with branches often this will produce a range error, but not if it's with a jump instead.

The other thing is, if you meant to type :++ and accidentally typed :+++, often this makes an orphan of the : you were actually intending to jump to, thankfully creating the unreferenced unnamed label error. If there's other references to it, though, you're out of luck.

Here's how this error usually manifests for me:
Code:
; original code with a simple loop

   ldx #10
   :
      stx $2007
      dex
      bne :-
   lda #11
   sta $2007

; later I revise this code to add a special case,
; and I didn't think about the enclosing unnamed label loop

   ldx #10
   : ; this now has no references, assembler error
      cpx #3
      bne :+
         lda #11
         sta $2007
      :
      stx $2007
      dex
      bne :- ; this now points to the wrong one (no direct error)
   lda #11
   sta $2007


DRW wrote:
rainwarrior wrote:
Yes, there are always potential errors, but I think I've only had to debug an erroneous : maybe 2 times in the past year, whereas I think the assembler has caught unreferenced unnamed label errors maybe 10 times?

O.k., sorry, but that comparison is beyond faulty. Really. That's so absolutely apples vs. oranges:
"I had 10 compiler errors with named labels and 2 runtime errors with unnamed labels. 2 errors < 10 errors. Therefore unnabed labels win."

No. I was comparing two different types of errors with unnamed labels, to give you an idea of the practical problems they do and do not create, in my own experience. There is no comparison against named labels here.

DRW wrote:
Fast to read (i.e. faster than named labels): Definitely no. It is much faster to read @end and to find the other @end in the code than it is to read :+++ and to count three : until you're at the destination location.

This is why I favour indentation. If the control structure is clear, you can just look to the end of the indent. If you're using :+++ with spaghetti then you deserve the problems you get.


You seem to be arguing that things you don't use are error prone. I don't see the point of this. I use them constantly, and I don't find them a significant source of programming problems for me. Any programming construct is subject to errors if you make a mistake, the question is how often you make these mistakes, and how much time they consume for you. I make lots of mistakes while programming, but unnamed label mistakes barely register on the scale of time wasted.

You asked why people use them, and those of us who answered seem to be in consensus: less typing, less thinking about names, they look nice to us.

If you wanna tell me you used them a bunch and had a lot of problems with errors, great, that's a reason for you not to use them, and I'd love to hear about your experiences, but if you're gonna just tell me they're useless because they're error prone from some detached theoretical position, you can stuff it. If you wanna know what kinda problems they create, try them out. If you're afraid, leave them alone. I think they're well worth using.
Re: "Philosophical question": Unnamed labels
by on (#170309)
DRW wrote:
Pokun wrote:
The only advantages over local labels I can think of is that they are fast to type, fast to read and doesn't require any global labels around them.

Fast to type: Yes.

Fast to read (i.e. faster than named labels): Definitely no. It is much faster to read @end and to find the other @end in the code than it is to read :+++ and to count three : until you're at the destination location.

And do you really have those many labels of the same type (i.e. where the names would clash) within the same function (i.e. within the same global label)?

Yeah well more than two plus-/minus-signs and the readability is starting to decline. I'd probably never use more than three or four (if the assembler even allows that many?). But I think one or two pluses in a short jump is pretty much instantly read.

If I need several @loop or @skip labels within the same "function", I usually just number them (@loop1, @loop2 etc), or think up a more unique name for the situation (like @loop_outer, @loop_nested or something).

Edit: Fixed quote errors.
Re: "Philosophical question": Unnamed labels
by on (#170310)
You've inserted me into that quote erroneously.
Re: "Philosophical question": Unnamed labels
by on (#170311)
rainwarrior wrote:
No. I was comparing two different types of errors with unnamed labels, to give you an idea of the practical problems they do and do not create, in my own experience. There is no comparison against named labels here.

Oops. Right, I read that wrong.

Yeah, for me, I'll indeed leave unnamed labels alone. You might find them practical, but I'm much better when I have actual names to read for labels.
Re: "Philosophical question": Unnamed labels
by on (#170313)
I actually like the way WLA does it:

Code:
   ldx var
---
   iny
   dex
   bne ---     ;this will loop to the label above
   jmp +

++
   rts    ;this line will be ignored
+
   lda #3


The number of + or - doesn't need to correlate to how many nameless labels are in front of or behind your branch statement. It always just looks for the closest occurrence of what you type (for instance, with the "jmp +", it skips right past the "++" because it's simply looking for the next occurrence of just "+").

I would probably die without nameless labels. I have so many little branches in my code that the thought of coming up with a name for each one is just horrible. I make up for it though by defining the exact location in RAM for every single variable. I can't stand that .DS 1 crap, not knowing where the hell anything is when you're trying to debug.
Re: "Philosophical question": Unnamed labels
by on (#170314)
Celius wrote:
I actually like the way WLA does it

But as far as I see, these aren't unnamed labels. It's just that the names don't consist of letters. If you want that and you don't have it, you could easily workaround it by giving the labels names like @a, @aa, @bbb.
Re: "Philosophical question": Unnamed labels
by on (#170315)
Well it's not exactly the same as a named label. Something like this works:

Code:
   ldx var
---
   iny
   dex
   bne ---     ;this will loop to the label above
   jmp +

++
   rts    ;this line will be ignored
+
   lda #3
   jmp +  ;this will go to the NEXT occurrence of +

++
   rts    ;this line will be ignored
+
  lda #4
  ....


You can reuse the + and - label names as many times as you want. The key is that when it is a + label, it can only be referenced by instructions behind it. When it is a - label, it can only be referenced by instructions ahead of it. So if you have a label you want to jump ahead to, but later you also want to jump back to that label, you'd need to do it like this:

Code:
   jmp +
   ...
+
-
   lda #4
   sta var
   ..blah code
   dex
   bne -


It's not as clear as a named label, but it definitely saves time once you're comfortable using them.
Re: "Philosophical question": Unnamed labels
by on (#170316)
Celius, that's how ASM6 works too, which's the way I prefer.

DRW wrote:
It's just that the names don't consist of letters.

That and the labels are completely reusable, even within the same scope.

Quote:
you could easily workaround it by giving the labels names like @a, @aa, @bbb.

Not really, because you can only have one "@a" label in the same scope (or between non-local labels). The code would look much messier as you used random letters for the labels.
Re: "Philosophical question": Unnamed labels
by on (#170332)
tokumaru wrote:
Celius, that's how ASM6 works too, which's the way I prefer.


And X816, where I stole the idea from ;)
Re: "Philosophical question": Unnamed labels
by on (#170347)
And xa65, from which I stole the idea to nescom and snescom (which I made, aiming feature and format compatibility, because I was unsatisfied with something or other in xa65; I can't remember anymore what).
However, the scope works the opposite way to Celius's example.

This won't compile:
Code:
    bne ++
    beq +   ; Error: undefined label

++  lda #1
    jmp +++
+   lda #2  ; Warning: unused label
+++ rts


But this will:
Code:
    bne +
    beq ++

+   lda #1
    jmp +++
++  lda #2
+++ rts


A larger number of pluses invalidates all shorter-length +labels so far. With minus-labels it works the same, but backwards.
Thus the length of the plus or minus sequence is indicative of how far the label is from its use. I totally love this syntax (and hate the way ca65 does it, but I work with it).

I'm not sure whether xa65 allows it, but more short labels can also be defined in a single line. Example (from my Simon's Quest automatic disassembly):
Code:
PlotAction01_Continues:
        $8336  A4 19:       ldy CurrentPlotAction_StateWithin
        $8338  D0 07:       bne ++              ; $8341
        $833A  C6 2A:       dec TimeRelated2A
        $833C  D0 02:       bne +               ; $8340 -> rts
        $833E  E6 19:       inc CurrentPlotAction_StateWithin
+ -     $8340  60:          rts

++      $8341  E6 FD:       inc PPUscrollingPositionLo
        $8343  A5 FD:       lda PPUscrollingPositionLo
        $8345  D0 F9:       bne -               ; $8340 -> rts
        $8347  A5 FF:       lda PPUdesiredRegister2000
        $8349  49 01:       eor #$01
        $834B  85 FF:       sta PPUdesiredRegister2000
        $834D  A9 B4:       lda #$B4
        $834F  85 2A:       sta TimeRelated2A
        $8351  A9 00:       lda #$00
        $8353  85 19:       sta CurrentPlotAction_StateWithin
        $8355  60:          rts

Which, if the offset and hex bytes were removed, would be reassemblable assuming the RAM labels are defined appropriately. (Can you guess what this code does?)
Re: "Philosophical question": Unnamed labels
by on (#170350)
Here's a laugh, this is an example of an actual label in one of my projects:

sprmanager_render_metasprite_loop_priority_samebyte

"samebyte" is a branch within the "priority" section of the "loop" section of the "render_metasprite" subroutine which is part of the "sprmanager" group of stuff within the system library. This label could easily be replaced with an anonymous label because it's a fairly short jump, but my preference would be to replace it with a temporary named label like "samebyte$" (in DASM) since it's a little more descriptive without needing to read the comments.
Re: "Philosophical question": Unnamed labels
by on (#170352)
Drag wrote:
sprmanager_render_metasprite_loop_priority_samebyte

I don't know why you bothered shortening sprite into spr if you were gonna have labels this long!
Re: "Philosophical question": Unnamed labels
by on (#170359)
It was a diet that didn't quite work out.
Re: "Philosophical question": Unnamed labels
by on (#170375)
OK so random thought #1: with unnamed labels, while small snippets are easy to move as-is, in case of larger ones if you ever insert in the middle any other unnamed label for whatever reason you need to adjust all surrounding code that branches across that point. Ouch.

Random thought #2: named labels have a similar issue, ironically! Copy some piece of code (modified or not) and you get an error if there's a conflict, yes. But then when you fix it, you need to remember to fix not just the label, but also the instruction that calls it. I lost the count of times I caused a program to get stuck because it tried to loop to the wrong label.

And before somebody calls me out on copying code: try to do something like IF PRESSED_RIGHT THEN X += 2 without writing nearly identical code for every direction. Whatever you attempt, that'd be overkill.

Celius wrote:
I make up for it though by defining the exact location in RAM for every single variable. I can't stand that .DS 1 crap, not knowing where the hell anything is when you're trying to debug.

Generate a listing if the assembler provides the option. This will tell you the exact address of every line (useful not just for telling where each variable is, but also to tell precisely where the code gets stuck).
Re: "Philosophical question": Unnamed labels
by on (#170379)
Sik wrote:
Celius wrote:
I make up for it though by defining the exact location in RAM for every single variable. I can't stand that .DS 1 crap, not knowing where the hell anything is when you're trying to debug.

Generate a listing if the assembler provides the option. This will tell you the exact address of every line (useful not just for telling where each variable is, but also to tell precisely where the code gets stuck).

To deal with the problem of moving variables, I wrote a program that automatically generates an FCEUX watch list every time I rebuild. I don't find they move around much, though; I don't need to make new RAM allocations often, but it was easy enough to automate and not have to fret with it.

Sik wrote:
OK so random thought #1: with unnamed labels, while small snippets are easy to move as-is, in case of larger ones if you ever insert in the middle any other unnamed label for whatever reason you need to adjust all surrounding code that branches across that point. Ouch.

Yep, that is the main source of errors with it, and also why I try to keep unnamed label branching small and simple (and indented).


I do like the +/- thing WLA does, that seems very sensible to me, better than ca65's, though I wouldn't switch assemblers over such a small feature. ;)
Re: "Philosophical question": Unnamed labels
by on (#170381)
Sik wrote:
Generate a listing if the assembler provides the option. This will tell you the exact address of every line (useful not just for telling where each variable is, but also to tell precisely where the code gets stuck).


I'm sure WLA could generate this, but for me, it's not that much more work to do it manually anyways. The time spent doing it manually probably adds up, but I just feel like it's easier for me to have immediate access to see what goes where.

As for debugging program crashes, it would probably be very helpful to have a file that shows you exactly what address each line is at. I usually just use FCEUXD with breakpoints, but depending on the break criteria it can be challenging sometimes to figure out what code is being executed. I can't tell you how many times I've been scrolling around that little debug window trying to look at surrounding code to figure out what subroutine I'm in, getting thrown off every time the disassembly gets misaligned (when it can't distinguish code from data, or it starts reading in the middle of an instruction).

Quote:
OK so random thought #1: with unnamed labels, while small snippets are easy to move as-is, in case of larger ones if you ever insert in the middle any other unnamed label for whatever reason you need to adjust all surrounding code that branches across that point. Ouch.


I have actually had this problem, too. I've gone to the extreme with adjusting nameless labels to avoid confusion by naming one label "++" and the other one that would create a conflict "++++++++++" or something ridiculous. That way it stands out more. Then again, this might not be as easy for assemblers that force you to use real nameless labels (":") and refer to them relatively (like ":++" for "two nameless labels ahead of me").
Re: "Philosophical question": Unnamed labels
by on (#170382)
Celius wrote:
I make up for it though by defining the exact location in RAM for every single variable. I can't stand that .DS 1 crap, not knowing where the hell anything is when you're trying to debug.

What happens when you need to resize an array, then? You have to manually move every variable that comes after it! Not to mention that you have to calculate the address of each variable/array in your head according to the size of the one that came before (i.e. the length of a variable is unknown until you write/read the NEXT line!), which is error-prone.

You shouldn't have to memorize memory locations anyway, that's one of the reasons assemblers are able to generate listings, so you know where everything end up.

Quote:
I wrote a program that automatically generates an FCEUX watch list every time I rebuild.

I did that once, but since I reuse RAM a lot, the result wasn't particularly useful. Specially considering that ca65 doesn't include scope names in its symbols list, so some symbols appear to be repeated while in fact they belong to different scopes.
Re: "Philosophical question": Unnamed labels
by on (#170383)
Sik wrote:
And before somebody calls me out on copying code: try to do something like IF PRESSED_RIGHT THEN X += 2 without writing nearly identical code for every direction. Whatever you attempt, that'd be overkill.

A lot of games with Left+Right or Up+Down errors make me think the Left+Right and Up+Down cases are don't cares in a lookup table. Or is a lookup table the kind of "overkill" you were talking about?
Code:
.code
; 8< 8< 8<
  ldx cur_turn
  lda cur_keys,x
  and #KEY_UP|KEY_DOWN|KEY_LEFT|KEY_RIGHT  ; 8|4|2|1 = $0F

  ; Don't accelerate at all if Up and Down are pressed at once.
  ; (The same behavior for Left and Right is baked into the tables.)
  cmp #KEY_UP|KEY_DOWN
  bcs is_up_down
 
  ; Add X acceleration to velocity
  tay
  ; clc  ; always clear due to previous BCS
  lda dir_to_xaccel,y
  bpl :+
    dec xvel,x
  :
  adc xvel_sub,x
  sta xvel_sub,x
  bcc :+
    inc xvel,x
    clc
  :

  ; Add Y acceleration to velocity
  lda dir_to_yaccel,y
  bpl :+
    dec yvel,x
  :
  adc yvel_sub
  sta yvel_sub,x
  bcc :+
    inc yvel,x
  :
  is_up_down:

; 8< 8< 8<

.rodata
; Apply same magnitude of acceleration to orthogonal and diagonal
; Values in each row is X neutral, right, left, left+right
; Rows are Y neutral, down, up (up+down rejected by other code)
dir_to_xaccel:
  .byte    0,  30,<-30,   0
  .byte    0,  21,<-21,   0  ; diagonals are 30% less: 1 vs. sqrt(.5)
  .byte    0,  21,<-21,   0

dir_to_yaccel:
  .byte    0,   0,   0,   0
  .byte   30,  21,  21,   0  ; down
  .byte <-30,<-21,<-21,   0  ; up

In this fragment, is_up_down is an example of a label that gets a name because of an intervening anonymous label. A lot of such labels in my code begin with is_ or not_ to denote the condition that caused a branch to that label, or have_ to denote a value that has been calculated and is ready to use (such as have_new_frame if the ID of the next sprite cel has been calculated).
Re: "Philosophical question": Unnamed labels
by on (#170386)
Code:
 lda controller
 and #%00000011        ;Are either left or right pressed?
 beq horizontal_end
 cmp #%00000011        ;Are they both pressed?
 beq horizontal_end
 and #%00000001        ;Is right pressed?
 beq left
 ; Right code here
 jmp horizontal_end
left
 ; Left code here
horizontal_end

You can remove the CMP/BEQ for the left+right check, and when both are pressed, right is given priority. Oh, and another example of named labels where unnamed labels may also work, but the named labels here indicate the effect of the branch, instead of the condition of the branch.
Re: "Philosophical question": Unnamed labels
by on (#170417)
Or depending on the situation, you can just let both directions get executed and negate each other (doesn't work in all cases but for some games it happens to work).

tepples wrote:
A lot of games with Left+Right or Up+Down errors make me think the Left+Right and Up+Down cases are don't cares in a lookup table. Or is a lookup table the kind of "overkill" you were talking about?

Nah, by overkill I meant something like turning the IF into a subroutine so you call the subroutine instead of implementing separate branches. That kind of dumb stuff. Look-up tables are quite common, especially when more complex processing is needed (e.g. to ensure diagonals move at the correct speed), although they're normally not used for 2-way movement like in platformers or the like.

What you mention though really annoys me. Pretty much all systems are known to be at risk of that kind of invalid input from wearing, and a look-up table is a good chance to tackle it by simply treating opposite directions to be the same as neither being pressed (getting to even avoid accidental side effects!), yet in practice they get usually assigned something like no buttons pressed at all or something stupid like that. Argh, why? >_< (once I was looking at the code of a game for some Atari system that used a look-up table, all those "invalid" values got assigned the same as going right instead - like, um, what?)
Re: "Philosophical question": Unnamed labels
by on (#170444)
Sik wrote:
...in practice they get usually assigned something like no buttons pressed at all or something stupid like that. Argh, why? >_< (once I was looking at the code of a game for some Atari system that used a look-up table, all those "invalid" values got assigned the same as going right instead - like, um, what?)

When you're talking about a part of the code that was never tested during development, it's hard to argue about intent in the finished product. When something gets revised multiple times, it's very easy to introduce a benign bug in code that doesn't get run. Nobody would have noticed that it was broken. I wouldn't necessarily expect it to have a well formed logical reasoning.

Some companies do test for that kind of thing though. In a QA pit, I've seen a gamepad with the d-pad sawed in half diagonally to permit this kind of test. Companies that do this have a fighting chance at doing something more reasonable for these unusual input cases.
Re: "Philosophical question": Unnamed labels
by on (#170445)
rainwarrior wrote:
Sik wrote:
...in practice [impossible Control Pad directions] get usually assigned something like no buttons pressed at all or something stupid like that

When you're talking about a part of the code that was never tested during development, it's hard to argue about intent in the finished product. When something gets revised multiple times, it's very easy to introduce a benign bug in code that doesn't get run. Nobody would have noticed that it was broken.

Which leads to accusations of programmers and QA being terrible. But I know the controller code in some of the Mega Man games zeroes out all direction bits if it detects an impossible direction. I forget the exact code, but the following is equivalent:
Code:
  lda cur_keys
  and #KEY_UP|KEY_LEFT
  lsr a  ; shift to overlap with KEY_DOWN and KEY_RIGHT respectively
  and cur_keys
  beq not_opposing_dir
    lda cur_keys
    and #KEY_A|KEY_B|KEY_SELECT|KEY_START
    sta cur_keys
  not_opposing_dir:

(Here, I named the label instead of using an unnamed label in order to make the code fragment's meaning self-evident so that it doesn't need a comment.)

Perhaps code like this became more common as the Control Pads in the Famicom consoles used for testing games became worn, thereby exposing this possibility of this to developers.

In one of my older game projects, I would treat an impossible direction the same way that my present NES library treats a DPCM glitch, using the previous frame's data instead. This game was often played on a keyboard, where it is much easier to press Left and Right or Up and Down. This made circular motions more practical: Right-UpRight-UpRightLeft-UpLeft was recognized as Right-UpRight-(UpRight)-UpLeft. There was an additional "4-way" option to forbid diagonal presses. A lot of directional pads on USB HID gamepads, particularly the Gravis GamePad Pro and the Jess Tech GGE909, make accidental diagonal presses too easy. I first tried zeroing the direction, but that caused Up-UpRight-Up to be treated as Up-center-Up, causing two movements instead of one. So I instead switched to using the direction from the previous frame, resulting in Up-(Up)-Up.
Re: "Philosophical question": Unnamed labels
by on (#170489)
You can go with the most recently pressed direction too. If right is held and left gets pressed, left takes priority. If left is held and right gets pressed, right takes priority. That's a strategy used in computer games using keyboard input.
Re: "Philosophical question": Unnamed labels
by on (#170600)
tokumaru wrote:
What happens when you need to resize an array, then? You have to manually move every variable that comes after it! Not to mention that you have to calculate the address of each variable/array in your head according to the size of the one that came before (i.e. the length of a variable is unknown until you write/read the NEXT line!), which is error-prone.

You shouldn't have to memorize memory locations anyway, that's one of the reasons assemblers are able to generate listings, so you know where everything end up.


It's a lot of work, being lazy :). Honestly, I know it's a better practice to declare variables with .DS statements so you don't have to choose an exact address. I'd probably find it easy enough to work with if I actually tried it once. Maybe I'll give it a shot on my next project.