Last time I wrote code to put a hero sprite onscreen. In my first (pre-sprite) video demo, I had room changes triggered directly by player input. Today I’m going to recode the input handler to alter the hero instead, specifically the hero’s direction.

Joypad revisited

I posted my joypad reading code in a previous post, but here it is again for easy reference:

update_joypad_data: ;props to blargg for A-only joypad read.
    lda joypad1
    sta joypad1_old
    lda #%01111111
    sta joypad1
    sta $4016
    asl a
    sta $4016
@loop:
    lda $4016
    and #$03  ;props to Disch for Famicom support
    cmp #$01
    ror joypad1 ;right, left, down, up, start, select, B, A
    bcs @loop

    lda joypad1_old
    eor #$FF
    and joypad1
    sta joypad1_pressed ;this tracks off-to-on transitions.

    lda joypad1
    eor #$FF
    and joypad1_old
    sta joypad1_released ;this tracks on-to-off transitions
    rts

Before, I was checking joypad_pressed to determine whether or not to change a room. This was so that if the player held down a button, it would only change rooms once (instead of once per frame held). But now that I’m going to be using input to control the sprite, it makes more sense to check joypad1 and use every frame’s input. If the player holds down the d-pad, the sprite should keep moving.

But I’m not quite ready to move yet!

Changing Direction

I want to rewrite my input handler so that it takes d-pad input and uses it to set the hero’s direction. This sounds pretty straightforward, but there are some special cases I need to be wary of: what happens if the player presses left+right? or up+down? This isn’t likely to happen on a real controller, but most people play Nintendo games on emulators these days. If you don’t protect against these strange key combinations, you open yourself up to some pretty bizarre glitches, like the Zelda 2 acceleration glitch (commentator talks about it around the 4:20 mark):

I’m going to work around this problem by assigning a single direction to these weird combinations. But first we need to look at our input. In my joypad reading routine above, I store the button states in a variable called joypad1. The left 4 bits of joypad1 will hold the d-pad states and the right 4 bits will hold the states of the other four buttons. First I want to isolate the d-pad bits:

    lda joypad1
    and #$F0
    beq @end
    lsr
    lsr
    lsr
    lsr
    ;now the d-pad button states are in the right nibble.
    ;do change direction stuff here.
@end:
    rts

The right nibble of the A register now holds the d-pad button states. Next I will build a look-up table, assigning a direction to each possible combination of d-pad presses. Let’s look at the combinations. There are sixteen possibilities in all:

0000xxxx
    ||||
    |||+--up
    ||+---down
    |+----left
    +-----right

0000 = no arrows pressed (will never happen)
0001 = up
0010 = down
0011 = up+down
0100 = left
0101 = left+up
0110 = left+down
0111 = left+up+down
1000 = right
1001 = right+up
1010 = right+down
1011 = right+up+down
1100 = right+left
1101 = right+left+up
1110 = right+left+down
1111 = right+left+down+up

The highlighted ones are the problem combinations. I will need to assign a single, specific direction to them. For example in the case of right+left, I will just choose one: left. Let’s turn this into a lookup table. Recall that I have a set of constants assigning a value to each direction (for indexing into CHR lookup tables among other things):

;direction indexes for tables
SPRITE_UP =         $00
SPRITE_DOWN =       $01
SPRITE_LEFT =       $02
SPRITE_RIGHT =      $03
SPRITE_UP_LEFT =    $04
SPRITE_UP_RIGHT =   $05
SPRITE_DOWN_LEFT =  $06
SPRITE_DOWN_RIGHT = $07

Now I will make a lookup table assigning these constant values to the various button combinations:

direction_change_table:
    .byte $FF               ;0000 dummy value
    .byte SPRITE_UP         ;0001, up
    .byte SPRITE_DOWN       ;0010, down
    .byte SPRITE_UP         ;0011, up+down
    .byte SPRITE_LEFT       ;0100, left
    .byte SPRITE_UP_LEFT    ;0101, left+up
    .byte SPRITE_DOWN_LEFT  ;0110, left+down
    .byte SPRITE_UP_LEFT    ;0111, left+up+down
    .byte SPRITE_RIGHT      ;1000, right
    .byte SPRITE_UP_RIGHT   ;1001, right+up
    .byte SPRITE_DOWN_RIGHT ;1010, right+down
    .byte SPRITE_DOWN_RIGHT ;1011, right+up+down
    .byte SPRITE_LEFT       ;1100, left+right
    .byte SPRITE_UP_RIGHT   ;1101, left+right+up
    .byte SPRITE_DOWN_RIGHT ;1110, left+right+down
    .byte SPRITE_DOWN_LEFT  ;1111, left+right+up+down

Now all button combinations have a single direction associated with each of them. All that I have left to do is read from this table and set the hero’s direction, like so:

handle_joypad:
    lda joypad1
    and #$F0
    beq @end
    lsr
    lsr
    lsr
    lsr
    tay ;right nibble has the d-pad button states. use as index
    lda direction_change_table, y
    sta hero_direction
@end:
    rts

Changing the sprite

The rest of the work is already done for me. I already have a routine update_hero_sprite (see last post) that takes hero_direction and uses it to index into some CHR lookup tables. I don’t have to change anything to get the new sprites to display. It works! (you can see the input in the bottom left of each picture):

If I press up, the sprite faces up.

If I press up, the sprite faces up.

Pressing down+right makes her face southeast

Pressing down+right makes her face southeast

Left+Right gets mapped to left.

Left+Right gets mapped to left.

up+down+left+right

up+down+left+right

Conclusion

My hero sprite is now capable of looking in 8 directions. I use a lookup table to assign a specific direction to each possible d-pad button combination. The next step is to actually move her in the direction she is facing. And when she is moving, I will want to animate her. Stay tuned!

Now that I have some test rooms and can navigate between them, it’s time to stick a sprite in the game. I couldn’t draw a rock to save my life, much less a cool character sprite, so for now I am going to rip one from another game to use for testing. Here she is:

She's from The Guardian Legend.

She's from The Guardian Legend.

This is the sprite from The Guardian Legend. I chose her as my test sprite for a few reasons:

  1. Her dimensions. She is 16×32, which is the size I want for my game’s hero sprite.
  2. Her proportions. Specifically her feet. I have narrow 16 pixel passages that the player will need to fit through. I want to allow a small amount of left-to-right and up-to-down movement within those 16 pixel hallways. If the sprite is too fat it will look like they are stomping on the walls.
  3. Her directions. She can face 8 different directions, which is what I want for my game.
  4. Her animation. She has a 4-frame animation that looks smooth and is CHR-efficient. Something I want to imitate.

Getting a sprite onscreen

First things first I need some variables for the hero. Right off the bat I know I will need to track her coordinates and direction:

.segment "ZP": zeropage
hero_x: .res 1 ;based on top left of sprite
hero_y: .res 1
hero_direction: .res 1

Next I will need to initialize those variables. This subroutine will be called when the game engine is loaded:

;direction indexes for tables
SPRITE_UP =         $00
SPRITE_DOWN =       $01
SPRITE_LEFT =       $02
SPRITE_RIGHT =      $03
SPRITE_UP_LEFT =    $04
SPRITE_UP_RIGHT =   $05
SPRITE_DOWN_LEFT =  $06
SPRITE_DOWN_RIGHT = $07

initialize_hero:
    lda #$50    ;place her somewhere on the map
    sta hero_x
    sta hero_y

    lda #SPRITE_DOWN
    sta hero_direction

    jsr update_hero_sprite

    rts

The hero will have a different appearance based on the direction she is facing. I will have lookup tables to find which CHR tile number to use. I index into these tables with a direction index. Those constants at the top define the direction indexes (indices? whatever).

update_hero_sprite is a routine that will be called every frame. It’s job will be to calculate the sprites’ x, y, tile number and flip/palette settings and store them in RAM. Later, in the NMI we will copy those values to the PPU (Picture Processing Unit) using DMA transfer.

Speaking of which, the hero sprite is actually made up of 4 sprites. On the NES, you have a choice between 8×8 sprites and 8×16 sprites. I will be using 8×16 sprites. The hero is 16×32, which is 4 8×16 sprites:

This character sprite is actually made up of 4 8x16 sprites

This character sprite is actually made up of 4 8x16 sprites

So let’s get her on the screen:

update_hero_sprite:
    lda hero_direction
    tax       ;we use this index to read from tables

    lda #hero_top_left  ;sprite number.
    asl
    asl     ;each sprite has 4 bytes of data
    tay     ;we use this index to write sprite data to RAM

    ;sprite data is stored, in order: y, tile #, attrib, x
    lda hero_y
    sta sprite_RAM, y

    lda hero_x
    sta sprite_RAM+3, y

    lda sprite_tiles_top_left, x
    sta sprite_RAM+1, y

    lda hero_sprite_flips, x  ;right, up_right and down_right 
                              ;are just flips of left, up_left
                              ;and down_left. Flip values are
                              ; stored in a table
    sta sprite_RAM+2, y

    lda #hero_top_right    ;top right, sprite #
    asl
    asl
    tay

    lda hero_y
    sta sprite_RAM, y

    lda hero_x
    clc
    adc #$08    ;8 pixels to the right of the top left sprite
    sta sprite_RAM+3, y

    lda sprite_tiles_top_right, x
    sta sprite_RAM+1, y

    lda hero_sprite_flips, x
    sta sprite_RAM+2, y

    lda #hero_bottom_left    ;bottom left, sprite #

    ....
    ;etc.  add 16 to hero_y for the bottom two sprites

    rts
    

And in the NMI:

    ;sprite DMA transfer
    bit $2002
    lda #$00
    sta $2003
    lda #>sprite_RAM
    sta $4014
    

And she’s on the screen!

She's wearing green today.

She's wearing green today.

Conclusion

Today’s post was short and sweet. Getting the hero sprite on the screen was pretty easy. My next step is to change her direction based on joypad input. See you then!

Now that I have the ability to make some rooms, and connect them together into floors, it would be nice if I had some mechanism to go from one room to another. To do this, I will need some subroutines that will change the room coordinates in RAM and then load the new room. This is really easy to code:

move_east:
    ldx floor_roomx
    inx
    ldy floor_roomy
    jsr floor_load_room ;this subroutine takes coords in x and
                        ;y and loads the room at those
                        ;coordinates (within the same floor).
    rts

move_west:
    ldx floor_roomx
    dex
    ldy floor_roomy
    jsr floor_load_room
    rts

Subroutines for moving north and south look similar. But there’s room for improvement here. Notice that I load the new room coordinates into x and y in both subroutines. I can save some bytes if I shorten it up a little bit:

move_west:
    dec floor_roomx ;current room x coord for the floor
    jmp :+
move_east:
    inc floor_roomx
:
    ldx floor_roomx
    ldy floor_roomy
    jsr floor_load_room
    rts

If I really wanted to I could probably replace that JMP instruction with a BPL (branch if positive) and save an extra byte. I’ll leave it how it is for now for readability. I’ll save optimizations for the very end if I need them.

Changing Floors

Changing floors isn’t much harder. First I’ll have to load the new floor, and then I’ll have to load a room within that floor. Good thing I have the load_floor and load_room subroutines already written. Having a good foundation to build upon makes things easy:

floor_down:
    dec map_floor ;this var holds the current floor.
    jmp :+
floor_up:
    inc map_floor
:
    lda map_floor
    jsr load_floor ;loads the floor, setting up room ptrs, etc
    ldy floor_roomy
    ldx floor_roomx
    jsr floor_load_room
    rts

Floor Boundaries

The thing I need to watch out for now is the floor boundaries. I don’t want to call move_west when I’m currently at x=0 or my game will try to load a room at x=FF, which probably isn’t there. When the game tries to move west, I will want to check if x==0 or not. If it is, I will skip the call. If the game tries to move east, I will want to check if x is equal to floor’s x-dimension – 1. If I’m in the eastern-most room, skip the call. Likewise for moving downstairs or upstairs. I will want to check the current floor number against 0 or the top floor number, respectively.

Triggering a Room Change

I have everything set up to change rooms. Now I need something to trigger the room changes. Most games trigger room changes based on sprite position/collision. If the user moves their character sprite to the edge of the screen, change the room. I haven’t coded sprite-support in yet, so for testing purposes I’m going to trigger room changes directly to the input. Pressing right will move east. Pressing down will move south. Pressing left will move west. Pressing up will move north. I’ll use B and A for going downstairs and upstairs.

Reading Input

First I need a joypad reading routine. The one I’m using is based off a really cool one that blargg posted on a nesdev thread. It preserves both x and y.

update_joypad_data: ;thanks to blargg for this A-only joypad
                    ;read routine.
    lda joypad1
    sta joypad1_old  ;save previous joypad data
    lda #%01111111
    sta joypad1
    sta $4016
    asl a
    sta $4016
@loop:
    lda $4016
    and #$03  ;props to Disch for Famicon support
    cmp #$01
    ror joypad1 ;right, left, down, up, start, select, B, A
    bcs @loop

    lda joypad1_old
    eor #$FF
    and joypad1
    sta joypad1_pressed ;this tracks off-to-on transitions.

    lda joypad1
    eor #$FF
    and joypad1_old
    sta joypad1_released ;this tracks on-to-off transitions
    rts

For changing rooms I will want to check joypad1_pressed. This variable tracks off-to-on transitions for the buttons. Using joypad1_pressed instead of joypad1 ensures that I will only move once per button press.

The joypad reading routine is called once per frame. There are many frames in a second. If the player presses up for a second, the variable joypad1 will report an up press for each frame the button was held. That’s a lot of ups. joypad1_pressed will only report one up press – the first one. I only want to move once per button-press, so I’ll check my input using joypad1_pressed.

So I read the joypad, then I perform actions based on the input. If left was pressed I’ll check to make sure that our room x-coord isn’t 0, and then I’ll call move_west. If up was pressed I’ll check to make sure our room y-coord isn’t 0, then call move_north if it isn’t. ETC..

Take a look

I made a demo video showing room navigation. I hope to make several videos like this as I code the game. I think it will be cool to document the making of an NES game like this. Anyway, take a look:

Conclusion

Room navigation works great. The next step is to put a movable sprite into the game and have room changes triggered by that sprite’s coordinates. Then it will start to look like a real game!

In my first post about Explorer’s room data format, I said that I used 4 bytes to store the wall/gap information for each room. After some thinking, I realized that this was wasteful, as rooms will share wall data with their neighboring rooms. For example, look at these two rooms:

These two rooms share a wall.

These two rooms share a wall.

They share a wall. The south wall of the top room is the same as the north wall of the bottom room. With my old format, I’d be storing the data for this wall twice: once in the data for the top room and once in the data for the bottom room. Not very efficient. If you imagine a 3×3 (9-room) floor you can see that the room in the middle will share ALL of its walls! Yet here I am wasting 4 bytes of ROM space to declare them for that room.

I was going to hold off on fixing this until later in the project, but Roth left a comment with a great idea for a solution. Here’s what he said:

About the data redundancy deal, I’m not quite sure how this could be approached off the top of my head, but what about making a separate map? For instance, I would guess that you have an overall table map that describes what room is what:

.db $30,$31,$20

or whatever. What if there was a second map, but it was a mapping of the openings?

.db %00100100, %00011000, %00011000, %00011000

So when you go into whatever room, read from that offset and subtract/add to get to be able to get all four sides maybe?

This idea of separating the wall data completely from the room data was just what I was looking for! Each floor would have a lookup table of wall bytes, and I’d index into it based on my room coordinates. I worked it out on paper to see what kind of savings I could get and the difference was huge.

Yes, these are my actual notes.

These are my actual notes. Look at the savings!

God forbid I ever make a floor with 400 rooms, but if I did I’d save myself 840 bytes on the wall data! Assuming a square floor where “x” is the length of each dimension, the old method uses 4x^2 bytes to store all of the wall data. The new method uses 2x^2 – 2x. In other words, take 4x^2 and cut it in half, then subtract more! 50%+ savings. Thanks Roth!

This method saves me on bytes in two ways:

  1. redundant wall bytes – each wall is declared one time instead of two times.
  2. perimeter walls – since lookups are based on room coordinates, I can assume a solid wall if x=0 or y=0 or x=max_x or y=max_y. I don’t have to store any perimeter walls!

My test data has 3 floors. Each floor is 3×3 rooms. My wall data dropped from 108 bytes to 36 bytes! :)

Implementation

I found that it was less headache to calculate indexes if I separated East-West walls from North-South walls and made two lookup tables for each floor.

Click to enlarge.

Click to enlarge.

The cost of this approach is that I have to store pointers to two tables per floor instead of one, but I think the tradeoff is worth it at this stage.

Implementing this couldn’t be easier. First I add wall table pointers to my floor data:

;----------------
; floor data
floor_map0:
    .byte $03, $03 ;dimensions of floor
    .word floor0_ew_walls ;ptr to ew wall lookup table
    .word floor0_ns_walls ;ptr to ns wall lookup table
    .word f0_r00, f0_r10, f0_r20 ;ptrs to room data
    .word f0_r01, f0_r11, f0_r21
    .word f0_r02, f0_r12, f0_r22

Next I update my load_floor routine to read these new pointers and store them in RAM:

;----------------
; load_floor expects the floor number in A
load_floor:
    sta map_floor   ;current floor
    asl
    tay
    lda map_ptr   ;this is a pointer to the floor lookup table
    sta temp_ptr2
    lda map_ptr+1
    sta temp_ptr2+1

    lda (temp_ptr2), y   ;get the pointers to the floor data
    sta floor_ptr
    sta temp_ptr1
    iny
    lda (temp_ptr2), y
    sta floor_ptr+1
    sta temp_ptr1+1

    ldy #$00     ;now let's read and set the floor's dimensions
    lda (temp_ptr1), y
    sta floor_dim_x
    iny
    lda (temp_ptr1), y
    sta floor_dim_y
    iny

    lda (temp_ptr1), y ;store the ptrs to the wall tables
    sta ew_walls_ptr
    iny
    lda (temp_ptr1), y
    sta ew_walls_ptr+1
    iny

    lda (temp_ptr1), y
    sta ns_walls_ptr
    iny
    lda (temp_ptr1), y
    sta ns_walls_ptr+1
    iny

    tya  ;update floor_ptr to the first room after the header
    clc
    adc floor_ptr
    bcc @done
    inc floor_ptr+1
 @done:
    sta floor_ptr
    rts

Then I modify the wall-building routine to calculate indexes and read from the wall tables. The last step is to reorganize my data: remove wall bytes from the individual room data and stick them in tables. Very quick fix.

Conclusion

Separating the wall data from the room data is going to save me a lot of ROM space. It also prevents the possibility of non-matching shared walls (I’m still entering all the data by hand, and I mistype from time to time). It’s sped up the map-building process too.

Come to think of it, I might try to implement something similar for stairs. Stairs_up and stairs_down need to line up on the z-axis. In a way the ceilings and floors are like walls themselves, just with fewer openings (stairs) across the whole map. I’ll save that battle for another day.

Thanks again Roth!

Last time I talked about how I store walls in my map data for Explorer. Today I’m going to talk about how I store tiles for the room interior.

After the walls are put down, there is a 12×11 field of floor space for me to work with for the room interior. This picture should make it clear:

The room interior is 12x11 metatiles big

The room interior is 12x11 metatiles big

The basic way I place a tile on the map interior is with a coordinate byte. A coordinate byte has the x-coordinate in the left nibble and the y-coordinate in the right nibble. For example, let’s say I want to place a tile in the spot designated by the blue square in the following screenshot:

The blue square is at coordinate (8, 7)

The blue square is at coordinate (8, 7)

This spot is 8 to the right and 7 down, so the coordinates would be (8,7). The byte I use to represent this position is $87. If I wanted to place a tile in the very top-left square, I’d use the byte $00. The bottom-right-most square is $BA. So using the values $00-$BA, I can represent any single position on the map interior.

It would be wasteful if I declared each and every tile one byte at a time. I should try to save bytes by somehow compressing repeating data. I currently do this by drawing lines. After a coordinate byte in the room data, there can be an optional run byte. A run byte tells the map engine which direction to draw a line and how many tiles long that line should be. The left nibble represents the direction:

C=Right
D=Down
E=Left
F=Up. (That’s F’ed up.)

The right nibble tells how long the line should be.

For example, let’s say we are drawing block tiles. In our room data we encounter the bytes: $11, $CA. The first byte says “place a tile at (1,1).” The second byte says “draw a line to the right, 10 tiles long.” This screenshot shows the result of these two bytes:

2 bytes make a line of tiles

2 bytes make a line of tiles

Run bytes can be tagged onto the end of other run bytes. In this case, the starting point of the new line is the ending point of the previous line. Check out these five bytes: $11, $CA, $D9, $EA, $F8. If you were to translate these bytes into English, they’d say:

“Place a tile at (1,1), then draw a line to the right, 10 tiles long. Then draw a line down, 9 tiles long. Then go left 10 tiles, and finally up 8 tiles.”

The result:

5 bytes makes a box

5 bytes makes a box

Implementation

My test data currently has 7 possible tiles it can use in the room interior. They are:

.enum
    floor
    wall
    block
    water
    block_breakable
    stairs_up
    stairs_down
.endenum

I store my room data as a series of strings. The first byte in the string tells which tile to use. Next comes a series of coordinate bytes and run bytes. Finally, a string is terminated with $FF. A list of strings is also terminated with $FF. Here’s are some example strings:

    .byte block, $11, $CA, $D9, $EA, $F8, $FF
    .byte water, $22, $C8, $D7, $E8, $F6, $C7, $D5, $E6, $F4
        .byte $C5, $D3, $FF
    .byte stairs_up, $00, $45, $FF
    .byte block_breakable, $02, $20, $FF
    .byte stairs_down, $BA, $66, $FF
    .byte $FF  ;no more strings
    

That data will generate this room interior:

A full room.  Careful, those steps might be slippery.

A full room. Careful, those steps might be slippery.

Here is my code that parses the inner room data.

    ldy #$ff
@mainloop:
    iny
    lda (temp_ptr1), y  ;which tile?
    cmp #$FF
    beq @end            ;FF terminates the room
    sta map_temp1     ;store tile number in a temp variable
    iny
    lda (temp_ptr1), y ;coordinate byte
@innerloop:
    iny
    jsr calc_room_position ;will take the coords in A and 
                           ;calculate an offset, output in X
    lda map_temp1    ;our tile number
    sta room, x

@run_check_loop:
    lda (temp_ptr1), y  ;peek at the next byte
    cmp #$FF
    beq @mainloop   ;FF terminates a tile string, so loop to 
                    ;the next tile string
    cmp #$C0
    bcc @innerloop  ;if less than $C0, there is no tile run, 
                    ;so loop back to get a new coordinate byte
    jsr tile_run  ;else we have a run.  This will take the 
                  ;run byte in A and draw a line in our RAM 
                  ;copy of the room.  We still have x loaded
                  ;with our offset.
    iny

    jmp @run_check_loop  ;check for another run byte

@end:
    rts

Conclusion

I don’t know if this is the best way to do it, but it works! I’m always looking to improve, so if you have any better ideas for compressing the room data, let me know in the comments!

Speaking of better ideas, Roth left a comment on my last post with a cool idea for saving bytes on my wall data. I’m talking hundreds of bytes. I’ve already implemented it and I’ll talk about it next time. In the meantime, feel free to subscribe to the RSS feed.