Skip to content

Faxanadu collision detection - Part III (Sprite behaviour III)



After going through 6 of the 7 main rules from the sprite description table last time it's now time to turn to the last and most complex rule, the main rule with ID $02. What makes this rule more complicated than most others is that it has it's own set of sub-rules. Depending on a value from the sprite description table one of eight sub-rules is chosen once main rule $02 is executed.

The main part of main rule $02 is the code from $A772 to $A791. In this chunk of code the ID of the sub-rule is loaded from the sprite description table and used to look up the offset of the sub-rule code from an array of offsets I called the sub-rule table. Note that these sub-rules have absolutely nothing to do with the sub-rules mentioned in earlier updates which were stored at $2D4 - $2DB. The sub-rules of main rule $02 are local to that main rule.

CODE:
<br />$A772->    BD D4 2:  LDA $2D4,X  ; Get (non-local) sub-rule <br />$A775->    10 FA:    BPL $A771   ; Return if there's one <br />$A777->    A0 1:     LDY #$1 <br />$A779->    B1 CA:    LDA ($CA),Y ; (Local) Sub-rule ID <br />$A77B->    A:        ASL A <br />$A77C->    A8:       TAY <br />$A77D->    A9 A7:    LDA #$A7    ; $A78B is the return ... <br />$A77F->    48:       PHA         ; address the execution ... <br />$A780->    A9 8B:    LDA #$8B    ; returns to after ... <br />$A782->    48:       PHA         ; executing the sub-rule <br />$A783->    B9 95 A7: LDA $A795,Y ; Look up code offset ... <br />$A786->    48:       PHA         ; of sub-rule from ... <br />$A787->    B9 94 A7: LDA $A794,Y ; sub-rule table <br />$A78A->    48:       PHA <br />$A78B->    60:       RTS <br />$A78C->    A9 2:     LDA #$2 <br />$A78E->    20 79 A8: JSR $A879   ; Move rule pointer <br />$A791->    4C BC A6: JMP $A6BC   ; Load next main rule <br />


Nearly all of the eight sub-rules of main rule $02 are used to move the sprite in a certain way, only the last rule is used for something else. Here's a detailed overview of each of the eight rules.

Sub-rule $00: Sprite moves towards the player (X-Axis)

The sprite data array $2DC - $2E3 is a collection of flags that give information about the current status of the sprite. In the following code snippet the least significant bit of bytes from that array is used. The meaning of this bit is sprite is moving to the left (Bit is 0) or sprite is moving to the right (Bit is 1). Depending on the sprite's position relative to the player's position that bit is set or reset.

CODE:
<br />$867B->    A5 9E:   LDA $9E      ; X position of player <br />$867D->    D5 BA:   CMP $BA,X    ; X position of sprite <br />$867F->    F0 F:    BEQ $8690 <br />$8681->    2A:      ROL A <br />$8682->    29 1:    AND #$1      ; Isolate direction bit <br />$8684->    85 0:    STA $0 <br />$8686->    BD DC 2: LDA $2DC,X <br />$8689->    29 FE:   AND #$FE     ; Clear old direction bit <br />$868B->    5 0:     ORA $0       ; Set new direction bit <br />$868D->    9D DC 2: STA $2DC,X <br />$8690->    60:      RTS <br />


Sub-rule $01: Change direction (X-Axis)

This sub-rule is very short. Just like in sub-rule $00 the LSB of $2DC,X plays is changed by this rule. The difference is that the direction of the sprite is changed from left to right or from right to left without any further conditions.

CODE:
<br />$8407->    BD DC 2: LDA $2DC,X <br />$840A->    49 1:    EOR #$1 <br />$840C->    9D DC 2: STA $2DC,X <br />$840F->    60:      RTS <br />


Sub-rule $02: Sprite moves towards the player (Y-Axis)

This sub-rule equals sub-rule $00, the only difference is that this sub-rule deals with the vertical position of the sprite while the one with ID $00 deals with the horizontal position of it. Instead of the LSB the MSB is used this time. If the bit is set the sprite is moving down, if it's not set the sprite is moving up.

CODE:
<br />$8691->    A5 A1:   LDA $A1     ; Y position of player <br />$8693->    D5 C2:   CMP $C2,X   ; Y position of sprite <br />$8695->    F0 F:    BEQ $86A6 <br />$8697->    6A:      ROR A <br />$8698->    29 80:   AND #$80    ; Isolate direction bit <br />$869A->    85 0:    STA $0 <br />$869C->    BD DC 2: LDA $2DC,X <br />$869F->    29 7F:   AND #$7F    ; Clear old direction bit <br />$86A1->    5 0:     ORA $0      ; Set new direction bit <br />$86A3->    9D DC 2: STA $2DC,X <br />$86A6->    60:      RTS <br />


Sub-rule $03: Change direction (Y-Axis)

Exactly the same as sub-rule $01 but for the Y-Axis instead of the X-Axis.

CODE:
<br />$8410->    BD DC 2: LDA $2DC,X <br />$8413->    49 80:   EOR #$80 <br />$8415->    9D DC 2: STA $2DC,X <br />$8418->    60:      RTS <br />


Sub-rule $04: Set random direction (X-Axis)

Sets the X direction of a sprite to random value. The odds are 50:50 whether that value is right or left.

CODE:
<br />$A7AD->    20 6E CA: JSR $CA6E   ; Get a random value <br />$A7B0->    AE 78 3:  LDX $378    ; Current sprite number <br />$A7B3->    C9 80:    CMP #$80    ; Odds are 50:50 <br />$A7B5->    B0 9:     BCS $A7C0 <br />$A7B7->    BD DC 2:  LDA $2DC,X <br />$A7BA->    29 FE:    AND #$FE    ; Move to the left <br />$A7BC->    9D DC 2:  STA $2DC,X <br />$A7BF->    60:       RTS <br />$A7C0->    BD DC 2:  LDA $2DC,X <br />$A7C3->    9 1:      ORA #$1     ; Move to the right <br />$A7C5->    9D DC 2:  STA $2DC,X <br />$A7C8->    60:       RTS <br />


Sub-rule $05: Set random direction (Y-Axis)

Exactly the same as the sub-rule before but for the Y-Axis instead of the X-Axis.

CODE:
<br />$A7C9->    20 6E CA: JSR $CA6E    ; Get a random value <br />$A7CC->    AE 78 3:  LDX $378     ; Current sprite number <br />$A7CF->    C9 80:    CMP #$80     ; Odds are 50:50 <br />$A7D1->    B0 9:     BCS $A7DC <br />$A7D3->    BD DC 2:  LDA $2DC,X <br />$A7D6->    29 7F:    AND #$7F     ; Move up <br />$A7D8->    9D DC 2:  STA $2DC,X <br />$A7DB->    60:       RTS <br />$A7DC->    BD DC 2:  LDA $2DC,X <br />$A7DF->    9 80:     ORA #$80     ; Move down <br />$A7E1->    9D DC 2:  STA $2DC,X <br />$A7E4->    60:       RTS <br />


Sub-rule $06: Sprite moves up

A very simple rule that makes the sprite move up.

CODE:
<br />$A7A4->    BD DC 2:  LDA $2DC,X <br />$A7A7->    29 7F:    AND #$7F     ; Move up <br />$A7A9->    9D DC 2:  STA $2DC,X <br />$A7AC->    60:       RTS <br />


Sub-rule $07: Shoot magic

The last rule is the most complex of all sub-rules and the only one that doesn't have anything to do with moving the sprite. This rule is used by sprite $21 to shoot his magical attack.

The code of that sub-rule is split into two major parts. The first one is specific to that sub-rule, the other one is called from other code too.

Here's the first part. At first there's a check that makes sure there are not already 8 sprites on the screen, after all the sprite data arrays can only hold 8 values. Afterwards the X and Y position of the new sprite and it's movement direction is set.
That sprite data array at $32C - $333 generally contains the PPU offset of the sprite image data of that sprite. In that concrete example it's set to 0 though. I'm not totally sure why that's the case but I guess it's because sprite $54 is the magic named Deluge which is always present in the PPU because the player can use that kind of magic too.

CODE:
<br />$9239->    20 36 A2: JSR $A236   ; Check number of active sprites <br />$923C->    B0 22:    BCS $9260   ; Jump if already 8 sprites active <br />$923E->    BD DC 2:  LDA $2DC,X <br />$9241->    29 1:     AND #$1 <br />$9243->    99 DC 2:  STA $2DC,Y  ; Magic gets same direction as sprite <br />$9246->    B5 BA:    LDA $BA,X <br />$9248->    99 BA 0:  STA $BA,Y   ; Magic gets same X position as sprite <br />$924B->    B5 C2:    LDA $C2,X   ; Take Y position of sprite <br />$924D->    18:       CLC <br />$924E->    69 8:     ADC #$8     ; Add 8 to Y position of magic <br />$9250->    99 C2 0:  STA $C2,Y <br />$9253->    A9 54:    LDA #$54    ; Sprite ID of magic <br />$9255->    99 CC 2:  STA $2CC,Y <br />$9258->    A9 0:     LDA #$0 <br />$925A->    99 2C 3:  STA $32C,Y  ; PPU offset of sprite <br />$925D->    4C 2 A2:  JMP $A202 <br />$9260->    60:       RTS <br />


The second part of the sub-rule initializes various sprite data arrays. It is invoked by many other functions too.

CODE:
<br />$A202->    8A:       TXA <br />$A203->    48:       PHA <br />$A204->    B9 CC 2:  LDA $2CC,Y    ; Sprite ID <br />$A207->    A:        ASL A <br />$A208->    AA:       TAX <br />$A209->    BD 2D AD: LDA $AD2D,X   ; Look up the offset ... <br />$A20C->    99 54 3:  STA $354,Y    ; of the sprite ... <br />$A20F->    BD 2E AD: LDA $AD2E,X   ; description meta-data <br />$A212->    99 5C 3:  STA $35C,Y <br />$A215->    A9 FF:    LDA #$FF <br />$A217->    99 D4 2:  STA $2D4,Y    ; Active sub-rule <br />$A21A->    99 34 3:  STA $334,Y    ; Sprite hit by magic ID <br />$A21D->    BE CC 2:  LDX $2CC,Y <br />$A220->    BD DF B4: LDA $B4DF,X   ; Sprite size table <br />$A223->    99 4 3:   STA $304,Y    ; Sprite size <br />$A226->    A9 0:     LDA #$0 <br />$A228->    99 E4 2:  STA $2E4,Y    ; Sprite phase <br />$A22B->    B9 DC 2:  LDA $2DC,Y    ; Sprite flags <br />$A22E->    29 BF:    AND #$BF <br />$A230->    99 DC 2:  STA $2DC,Y <br />$A233->    68:       PLA <br />$A234->    AA:       TAX <br />$A235->    60:       RTS <br />

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

sc on :

Very good version,it can be better if memory view ,memory dump ,etc add.

Barry on :

Im intrested in all of your Faxanadu for NES information, Please help me as I have been wanting to try and recreate this well loved classic.

Add Comment

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
BBCode format allowed
Form options

Submitted comments will be subject to moderation before being displayed.