It is currently Tue Oct 17, 2017 10:13 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 45 posts ]  Go to page Previous  1, 2, 3
Author Message
PostPosted: Thu May 05, 2016 7:30 pm 
Offline
User avatar

Joined: Fri Oct 14, 2011 1:09 am
Posts: 248
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?)


Top
 Profile  
 
PostPosted: Thu May 05, 2016 8:43 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
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.


Top
 Profile  
 
PostPosted: Thu May 05, 2016 9:10 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
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!


Top
 Profile  
 
PostPosted: Thu May 05, 2016 11:48 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
It was a diet that didn't quite work out.


Top
 Profile  
 
PostPosted: Fri May 06, 2016 9:32 am 
Offline

Joined: Thu Aug 12, 2010 3:43 am
Posts: 1589
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).


Top
 Profile  
 
PostPosted: Fri May 06, 2016 10:00 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5709
Location: Canada
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. ;)


Top
 Profile  
 
PostPosted: Fri May 06, 2016 10:24 am 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2126
Location: Minneapolis, Minnesota, United States
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").


Top
 Profile  
 
PostPosted: Fri May 06, 2016 10:33 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10046
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Fri May 06, 2016 10:55 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19086
Location: NE Indiana, USA (NTSC)
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).


Top
 Profile  
 
PostPosted: Fri May 06, 2016 12:39 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
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.


Top
 Profile  
 
PostPosted: Sat May 07, 2016 12:28 am 
Offline

Joined: Thu Aug 12, 2010 3:43 am
Posts: 1589
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?)


Top
 Profile  
 
PostPosted: Sat May 07, 2016 9:02 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5709
Location: Canada
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.


Top
 Profile  
 
PostPosted: Sat May 07, 2016 9:16 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19086
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
PostPosted: Sat May 07, 2016 8:45 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1248
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.


Top
 Profile  
 
PostPosted: Mon May 09, 2016 9:22 am 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2126
Location: Minneapolis, Minnesota, United States
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 45 posts ]  Go to page Previous  1, 2, 3

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 12 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group