Jump to content
IGNORED

Guidence on improving the draw loop code


Recommended Posts

Been working on a clone of Bomberman as a project to further learn the art of coding the 2600.

I feel like I'm hitting a bit of a wall with Playfield drawing, and getting the code in shape so that there's plenty of cycles to draw multiple sprites, missiles, and the like.

 

The current Playfield + P0 sprite code I have, though it doesn't feel like is doing much, is taking up more cycles than I expect. It's also causing the P0 sprite to become corrupted depending on where the player is standing.

 

This is leaving me not feeling confident about adding P1 sprites and missiles to the draw loop, and I'm feeling at a bit of an impasse on the direction I should take. 

Is the setup I have telling me that I should be working on a 2-line kernel, or doing something smarter about the draw loop such that I'm not processing as much data per wsync?

 

A little bit about the setup:

I have a playfield setup that updates every 15 lines.

The arena only uses PF1 and PF2, so I don't update PF0 at all (I was hoping that this approach was going to save me a nice amount of cycles).

 

In the screenshot, you can see how standing in certain parts of the screen results in the sprite becoming corrupted as the PF0 is getting written too as the beam is drawing.

This tells me that I need to be writing to them earlier or later, but so far my attempts at doing so result in the problem getting pushed over to the playfield, which starts its own flickering.

image.thumb.png.dfcd36210abb2f7f3bb22862f7c1f11d.png

 

Here's the portion of code that draws the gameplay arena

 

                                ; Cycles    Total   - Comment
.SLINE_LOOP                     ;                   - Gameplay Zone Scanline Loop
    sta WSYNC                   ;     3      0/78   - Start new Scanline
.ARENA_COUNT_DOWN               ; (7/8 Cycles)      - Every 15 scanlines update PF1 and PF2
    dec ARENACOUNTER            ;     5      5      - Count down arena draw counter 
    bne .INSIDE_P0              ;     2/3    7/9    - Jump to sprite check if no arena update needed
.RST_ARENA_COUNTER              ; (5 Cycles)        - [UPDATE ARENA] Reset update counter
    lda #15                     ;     2      9      - Reset Arena map update counter
    sta ARENACOUNTER            ;     3     12      - Store the arena draw counter
.DRAW_ARENA                     ; (22 Cycles)       - 
    ldy ARENAINDEX              ;     3     15      - Get the map data offset
    lda ARENA_0_PF1,y           ;     4     19      - Load PF1
    sta PF1                     ;     3     22      - set PF1 slice
    lda ARENA_0_PF2,y           ;     4     26      - Load PF2
    sta PF2                     ;     3     29      - Set PF2 slice
    inc ARENAINDEX              ;     5     34      - Move to next line of arena map data
.INSIDE_P0:                     ; (13/14 Cycles)    - P0 Position draw check 
    txa                         ;     2     36      - Transfer X to A
    sec                         ;     2     38      - Set carry before subtraction
    sbc P0POSY                  ;     3     41      - Subtract sprite Y coord
    cmp #P0_HGT                 ;     2     43      - Current scanline inside p0 sprite bounds?
    bcc .DRAWSPRP0              ;     2/3   45      - Draw P0 sprite routine
    lda #0                      ;     2     47      - else, index to 0
.DRAWSPRP0:                     ; (23 Cycles)       - P0 Draw sprite slice
    clc                         ;     2     49      -  
    adc P0ANMSET                ;     3     52      - 
    tay                         ;     2     54      - load Y so we can work with pointer
    lda (P0SPRPTR),Y            ;     5     59      - 
    sta GRP0                    ;     3     62      - set graphics for player0
    lda (P0COLPTR),Y            ;     5     67      -
    sta COLUP0                  ;     3     70      - set color of player 0
.DECREMENT_SCANLINE             ; (5 Cycles)        - Decrement Scanline loop 
    dex                         ;     2     72      - Reduce scanline counter (x)
    bne .SLINE_LOOP             ;     3     75      - repeat until screen is drawn
                                ;-------------------- END OF GAME PLAY ZONE

 

Added sourcecode and compiled bin for full review.

BMan2600.zip bman2600.asm.bin

Link to comment
Share on other sites

 2 line kernel is an easy solution, though you will lose a little detail  on your sprites  if they are small .

Draw  the sprites on different lines right after the WSYNC in the horizontal blank period.   This should correct the shearing issue on the sprites. You can use the VDEL to line up the sprites .   

 

Similar for updates of colors, PF , etc.  You want the object updated before it needs to be drawn .

 

you also could do away with the arena counter and just update PFs every other line after one of the 2 line kernel WSYNCS.

With a 2 line kernel you could put in an entire table for the arena and it will be half as many bytes using 2 LK

 

 

 

Edited by easmith
  • Like 2
Link to comment
Share on other sites

I second the 2 Line Kernel suggestion, commonly referred to as 2LK.

 

You might like to check out Collect, my tutorial for writing a 2K game from scratch. It uses a 2LK so it can draw both players, both missiles, the ball, as well as the playfield using PF0, PF1, and PF2.

 

Collect in action:

collect_20140714.thumb.png.2f83daab5b2063138f9e77ef0afe5a93.png

 

In Stella's debug color mode, which makes it clear which red block is the ball:

 

collect_20140714_1.thumb.png.021b5fb1397faa8f3a19bff7d3a4b064.png

 

These are the colors Stella uses for each object in Debug Color mode:

 

Screenshot2024-02-20at1_30_03PM.png.f9e94fc95385b946db87bd98eb4c01d8.png

 

Note: there are 3 shades of purple to clearly show the 3 PF registers. Same occurs with the other objects, as seen by the 3 shades of yellow and green in Combat to show the 3 copies of player 1 and missile 1:

 

Combat(1977)(Atari)_dbg_c803f2c5.thumb.png.6556a062aad3cfb18746359619454c47.png

  • Like 4
Link to comment
Share on other sites

  • 4 weeks later...

Hi, I don't know if you're still looking for advice for this, but I've rewritten your scanline loop so that it now redraws the screen without errors/tearing... mostly. I changed the arena rows to be 16 scanlines high rather than 15, but I didn't change any of the other code, so the player character no longer lines up with the visible playfield. Despite that, what I've written might give you some ideas. The parts that I've changed are marked with the text "*ALTERATION".

 

Even though this code now (theoretically) draws the player sprite and the background without issue, there aren't many cycles left for anything else, like placing enemies or boxes. I think some sort of solution based on switching between pre-selected "chunks", rather than using a single scanline counter for the whole screen, might open things up a bit, since you wouldn't need to do as much branching.

 

The real issue is to change visual items at a point on each scanline when you are certain they won't be mid-draw. I started by storing the sprite data for the next line in RAM, and then pulling these right after the HBlank. Then I update the playfield right at the end of the scanline, just after PF1 has finished drawing. I also pre-filled a buffer for the arena data in RAM, and used stack instructions to load it for drawing. Finally, I changed the height of the rows to 16 so that a simple AND with the scanline counter would tell when the playfield needs to be updated.

 

I hope it helps!

 

bman2600_alt01.asm

Edited by Verdant
  • Like 1
Link to comment
Share on other sites

On 3/16/2024 at 5:44 AM, Verdant said:

Hi, I don't know if you're still looking for advice for this, but I've rewritten your scanline loop so that it now redraws the screen without errors/tearing... mostly. I changed the arena rows to be 16 scanlines high rather than 15, but I didn't change any of the other code, so the player character no longer lines up with the visible playfield. Despite that, what I've written might give you some ideas. The parts that I've changed are marked with the text "*ALTERATION".

 

Even though this code now (theoretically) draws the player sprite and the background without issue, there aren't many cycles left for anything else, like placing enemies or boxes. I think some sort of solution based on switching between pre-selected "chunks", rather than using a single scanline counter for the whole screen, might open things up a bit, since you wouldn't need to do as much branching.

 

The real issue is to change visual items at a point on each scanline when you are certain they won't be mid-draw. I started by storing the sprite data for the next line in RAM, and then pulling these right after the HBlank. Then I update the playfield right at the end of the scanline, just after PF1 has finished drawing. I also pre-filled a buffer for the arena data in RAM, and used stack instructions to load it for drawing. Finally, I changed the height of the rows to 16 so that a simple AND with the scanline counter would tell when the playfield needs to be updated.

 

I hope it helps!

 

bman2600_alt01.asm 38.72 kB · 1 download

 

 

Thanks for taking a look Verdant. I'll have a read through.

 

Based on Darrell's feedback, I did update the code and aggressively cut things down. Just as you mentioned, I could see how few cycles I had before even beginning to look at things like enemy placement so just wanted to focus on getting the arena and basic movement done as tight as possible before getting into the fancier stuff like animation and color.

 

I'm sure I'll learn some tricks from your changes.

 

image.thumb.png.0457770c7332d388f3d3697f53b5ee33.png

bman2600.asm bman2600.asm.bin

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...