Jump to content
IGNORED

Scrolling with the 'Under the Hood' method


mksmith

Recommended Posts

 Hey everyone,

 

Mike and I were discussing a project a while ago and the main point was scrolling the screen.  Mike came up with the 'under the hood' process for Arkanoid and which I adapted for Millie & Molly and other projects.  Essentially you directly access the DL to update images, palettes etc without having to fully redraw the screen each frame.

 

I've put together some examples for both 8 and 16 pixel zone heights. 

scrollingzone.zip

 

The key thing is knowing the number of objects in your zone and how many you need to process. I would suggest doing the background first with your initial known number of objects then as you say overlay other sprites (ie ships, bullets) they will then be added to the end and not affected.

 

Base Code (thanks to @RevEng for he's always valuable assistance!)

; ShiftBlockTo - A=blockX Y=blockY temp3=location
ShiftBlockTo
 jsr SetBlockPointer
 ldy #4 ; object x-coordinate is the 5th byte in the dl object
 lda temp3
 sta (temp1),y
 rts

; ShiftBlockLeft - A=blockX Y=blockY temp3=distance
ShiftBlockLeft
 jsr SetBlockPointer
 ldy #4                        ; object x-coordinate is the 5th byte in the dl object
 lda (temp1),y
 sec
 sbc temp3
 sta (temp1),y
 rts

; ShiftBlockRight - A=blockX Y=blockY temp3=distance
ShiftBlockRight
 jsr SetBlockPointer
 ldy #4                       ; object x-coordinate is the 5th byte in the dl object
 lda (temp1),y
 ;clc
 adc temp3
 sta (temp1),y
 rts

; called with A=maxobjects Y=blockY temp3=distance
; left 0-127 right 255-128?
ShiftZone
 sta temp4                    ;store maxobjects (A)
 sty temp5                    ;store blockY (Y)
ShiftZoneLoop
 jsr SetBlockPointer
 ldy #4                       ;object x-coordinate is the 5th byte in the dl object
 lda (temp1),y              
 sec                          ;increases scroll speed+1 unless we set carry
 sbc temp3                    ;reduce x by required distance
 sta (temp1),y                ;and store
 
 dec temp4                    ;decrement to next object in zone
 bmi ShiftZoneExit            ;reached end of objects in zone?

 lda temp4                    ;load A & Y for block pointer
 ldy temp5                    

 jmp ShiftZoneLoop            ;continue
ShiftZoneExit
 rts

 

  • Like 5
  • Thanks 2
Link to comment
Share on other sites

  • 4 weeks later...

I'm investigating this method for an upcoming game as well - my assembly skills are wanting, but I'm tinkering around trying to see if this approach would make more sense than my current scrolling system.

 

My current scrolling technique (see fig.1) involves storing a screen's worth of "tiles", 22x10 blocks (or an 11x10 grid of 16x16 "sprites," that last column being used for the edge of the screen), that I then divide into 2 parts and draw as two plotmaps.  Moving where the division is effectively scrolls the whole screen.  The beauty here is that all I need to do is update the column that's on the division, via a few pokes, and even that column only needs updating when I've moved a tile's worth horizontally.  I'm still not entirely sure how efficient plotmap is versus a "sprites as tiles" approach, especially at the scale of a full screen, but it served me well in Dragon's Havoc.  I did make use of the double buffer - even if I could draw a scrolling screen without it, there would be no time to do anything else like game logic.  

 

I guess my question would be what (if any) performance gain I would get from switching to a variant of the "under the hood" manipulation of DLLs.  Tentative tests suggest I'd still want a double buffer, but I think I could pull that off with the ROM/RAM setup I'm looking at.  I'd modify the ShiftZone to allow a "starting x" instead of assuming a line of graphics starting from the edge (which is my current stumbling block - my assembly skills, as I said, are...developing) so I'd be calling it twice per row, and changing where that "starting x" is as the screen scrolls.  I could then change the updated column of DLL elements to be the appropriate new tiles as the level scrolls.  

ScrollingDiagram.thumb.png.a804f01967ed66e46b46c2f257048235.png

Fig. 1 - "split screen" scrolling, or a type of circular buffer

 

I guess my main question is, in the opinion/gut check of folks who know a bit more about DLLs, how much of a performance gain would this be, if any?  Switching to an "under the hood" direct DLL manipulation could certainly be more versatile (I'm particularly looking at the ability to store color palettes), but my implementing such a system is going to be a bit of a work, and at this point something of a gamble if it ends up being slower or harder to maintain than my current approach. 

 

Even if it's just a *little* faster, it's a "devil I don't know."  That is, it's a new technique to me, versus an older, more familiar one, so I wouldn't implement it for this project for just a slight boost.  I'd likely keep it in the back of my head for when I want to do that 4-directional scrolling RPG I've also been thinking about.  But, if it's a case of "What?  Why are you doing it your current way?  Direct DLL manipulation would be *much* faster!"  then I think I'd take a more involved attempt at implementing it for my current game in development.

 

Regardless, thank you for sharing this!  This examples have been very informative, and a great tool to have in one's toolbox!

 

EDIT: I might be overthinking this - I might just be able to move the rightmost object back to the left and vice versa, depending on which direction I'm scrolling, like a treadmill for each row.  I think that would be fewer updates than some other approaches.  I'd also need to figure out a scheme for collision detection if I go this route...

My initial question still stands, however - 2 big plotmaps versus a full screen's worth of DL manipulations, how does each approach compare?

Edited by Revontuli
Link to comment
Share on other sites

Hi Todd,

 

It's an interesting concept this one. The 'Under the Hood' (UTH) stuff gives us the ability to plot once and then manipulate directly whereas using plotmap/plotsprite is done each frame.  The UTH process does have some limitations such as no DoubleBuffer and you still are limited to how much you can draw.  I reached various limits doing Millie & Molly & Arkanoid where I just ran out of time to do more plotting of sprites during the redraw (this was before I understood this process further).  I haven't done any investigations around moving object between zones as yet or how that might even be tracked.

 

One area where is does excel is the draw once and move within it's zone though and really that's where it can shine.  If you have a bunch of items that doesn't need tracking position wise and is just moved together is easy enough using a little bit of assembly.  Or you can also update the sprite and parent palette without having to redraw it it you know it's zone and draw order just as easily. 

 

Scrolling wise and updating tiles might require tracking of the active tile column sliding off the screen so you can address it correctly.  I'd like to spend some time having a play with that side of it for sure.

 

I've found the plotmap is very fast and easy to manipulate but with the limitations of 1 palette and a 256 width or height.  If we had just a little more render time so you can throw a few more sprites at it (i found a max of about 8-9 24 height sprites in the GnG demo but maybe some excellent optimisations might help increase that) it would fantastic if you can fit within those use cases.  

  

  • Thanks 1
Link to comment
Share on other sites

Plotmap, for its limitations, is fast and relatively easy manage.  I found the limits to be similar to what you found - honestly, a full scrolling screen + 8-9 sprites is roughly what a stock NES can do (palette limitations notwithstanding), and was sufficient for Dragon's Havoc, and I've been adapting it for Harpy's Curse.

 

The "Under the Hood" approach does seem like a more sophisticated way to do things, and feels like what an in-house expert would use - and seems to play to the 7800s strengths and the way it manages graphics.  I imagine something like this was used for Scrapyard Dog.  Some of my initial tests suggest it isn't quite fast enough for me to do the 11x10 16x16 tile screen setup I had before, although a better coder or some optimizations could certainly make it work.  That said, if I were starting another game from scratch that required scrolling, UTH seems to be a good way to go - like I said earlier, I think some adjustments and strategic design could make it work well for a 4-way scrolling RPG along the lines of Dragon Warrior or Final Fantasy.  

 

Again, an excellent technique, thank you for sharing!

  • Like 1
Link to comment
Share on other sites

I am trying to learn 7800 as well. Is the idea of these demos to manipulate the offset fields in the DL's for horisontal scrolling?

 

For vertical scrolling you obviously need to modify two DL's with a holey. Right?

 

  • Like 1
Link to comment
Share on other sites

On 2/5/2022 at 9:08 PM, mksmith said:

 Base Code

I'm not totally sure how all the underlying data structures are organized in this (i.e. the entry management), but here's some cycle saving ideas at the expense of wasting RAM and ROM. Or at least the ones I was using...

 

Usually Display Lists are managed incrementally - so they start at a fixed base address and new draw operations are added in order (i.e. $2400, $2404, $2408, ...). Another method is to start at the center and draw your background downward in memory while the movable objects are drawn upward. This simplifies caching and can (if you're using only 4-Byte Display List entries) make nudging large sets of draw commands far quicker.

 

What I mean is to arrange each zone's Display List like this...

        | $xx00        | $xx40        | $xx80        | $xxC0
 -------|-----------------------------|-------------------------------------
  $24xx |       <--- Background Tiles | Movable Objects --->

...maybe 256 Bytes per zone is wasteful, but let's stick with that for now. The background is stored downward from $2400 - $247F while any "sprites" on top of it are added at $2480 - $24FF.

 

Now if we're using 4-Byte entries the offsets are all fixed, and we can do huge unrolled segments like...

rsc_1_e00
	INC $2403
rsc_1_e01
	INC $2407
rsc_1_e02
	INC $240B
rsc_1_e03
	INC $240F
rsc_1_e04
	...

 

Or (when moving more than one pixel)...

rsc_3_e00
	clc
	LDA $2403
	ADC #3
	STA $2403
rsc_3_e01
	clc
	LDA $2407
	ADC #3
	STA $2407
rsc_3_e02
	clc
	LDA $240B
	ADC #3
	STA $240B
rsc_3_e03
	clc
	LDA $240F
	ADC #3
	STA $240F
rsc_3_e04
	...

...with a big jump table to land at the start of where our background tiles were drawn in the Display List - so we don't waste time nudging any unused slots or parsing the list contents.

 

 

9 hours ago, karri said:

For vertical scrolling you obviously need to modify two DL's with a holey. Right?

You specifically modify the DL's height in its corresponding DLL, yes - but keep in mind this will also mess up the fetch offsets.

  • Like 1
  • Thanks 1
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...