Jump to content
IGNORED

Two player sprites without VDEL?


boutell

Recommended Posts

Is it practical to have two nonflickering, freely moving, single-height player sprites without using VDEL? My vertical positioning code takes 20-ish cycles to handle just one sprite, and that's about all the horizontal blank time there is to play with. But I get the impression there are games out there that have this problem licked, as long as I don't make other demands as well (playfield changes, etc). Any code out there that demonstrates how to nail this?

 

I need to be able to move the sprites arbitrarily, so a trick kernel that works only when the players are on the same horizontal line (for instance) won't do the job for me.

 

Does the Batari BASIC kernel do this somehow (I'm coding in assembly)? Or does it use VDEL and double-height sprites?

Thanks for any information!

 

(2600 development looks quiet since 2007 began...)

Link to comment
Share on other sites

Why don't you want to use VDEL?

 

But for non-VDELxx two-sprite code, try this:

Loop					;	 23

  SLEEP 35			 ;+35  58

  lda #PLAYER1HEIGHT
  dcp Player1Counter
  bcs DoDraw1
  lda #0
  .byte $2C
DoDraw1
  lda (Player1Ptr),Y
  sta GRP1			 ;+18  76

  lda #PLAYER0HEIGHT
  dcp Player0Counter
  bcs DoDraw0
  lda #0
  .byte $2C
DoDraw0
  lda (Player0Ptr),Y
  sta GRP0			 ;+18  18

  dey
  bpl Loop			 ;+5   23

 

Other things to consider: are your sprites positioned across the entire X-axis? If they are never at the very edge of the screen then you can get away with writing to GRPx close to, but not inside, the horizontal blank.

Link to comment
Share on other sites

But for non-VDELxx two-sprite code, try this:

 

Shouldn't it at least have WSYNC somewhere?

 

Well, in theory the desired effect is pretty simple to achieve:

; Loop overhead
; Do whatever RhabarberDraw Technique you want to get shape 1 into X
; Do whatever RhabarberDraw Technique you want to get shape 2 into A
STA WSYNC
STX GRP0
STA GRP1
; Loop underhead

 

Voilà, both sprites set in only 6 cycles of HBLANK :)

 

I always work like that or similar, I believe outside some sixchar techniques almost non of my code uses VDEL.

Link to comment
Share on other sites

But for non-VDELxx two-sprite code, try this:

 

Shouldn't it at least have WSYNC somewhere?

Well, I assume you enter the loop at the indicated cycle. :lol: Otherwise...:skull:

 

:D

 

Well, in theory the desired effect is pretty simple to achieve:

; Loop overhead
; Do whatever RhabarberDraw Technique you want to get shape 1 into X
; Do whatever RhabarberDraw Technique you want to get shape 2 into A
STA WSYNC
STX GRP0
STA GRP1
; Loop underhead

 

Voilà, both sprites set in only 6 cycles of HBLANK :)

I forgot about that - I always tend to be using X as another counter and/or don't have the extra cycles to spare for that - good suggestion.

Link to comment
Share on other sites

Is it practical to have two nonflickering, freely moving, single-height player sprites without using VDEL? My vertical positioning code takes 20-ish cycles to handle just one sprite, and that's about all the horizontal blank time there is to play with. But I get the impression there are games out there that have this problem licked, as long as I don't make other demands as well (playfield changes, etc). Any code out there that demonstrates how to nail this?

 

I need to be able to move the sprites arbitrarily, so a trick kernel that works only when the players are on the same horizontal line (for instance) won't do the job for me.

 

Does the Batari BASIC kernel do this somehow (I'm coding in assembly)? Or does it use VDEL and double-height sprites?

Thanks for any information!

 

(2600 development looks quiet since 2007 began...)

All bB kernels use VDEL and double-height sprites :) The VDEL is done not because of the 2-line kernel but because writes for one player and the ball would otherwise occur during the visible screen, which IMO is the most useful aspect of VDEL.

Link to comment
Share on other sites

I forgot about that - I always tend to be using X as another counter and/or don't have the extra cycles to spare for that

 

That's where the real fun begins - and what LAX(),Y was invented for :lol:

 

Anyway, main thing to realize and what I was trying to illustrate with my dummy code, is that _before_ the STA WSYNC you have (almost) all the time in the world to prepare stuff - and that your loop doesn't have to start with it.

 

Here Look at this:

; Loop overhead
; Prepare stack pointer
; Do whatever RhabarberDraw Technique you want to get shape 1 into X
; Do whatever RhabarberDraw Technique you want to get shape 2 into A
CPY Particle1
STA WSYNC
PHP
STX GRP0
STA GRP1
CPY Particle2
PHP
CPY Particle3
PHP
; Loop underhead

 

Both sprites, both missiles and the ball done during HBLANK :)

  • Like 1
Link to comment
Share on other sites

But for non-VDELxx two-sprite code, try this:

 

Shouldn't it at least have WSYNC somewhere?

 

Well, in theory the desired effect is pretty simple to achieve:

; Loop overhead
; Do whatever RhabarberDraw Technique you want to get shape 1 into X
; Do whatever RhabarberDraw Technique you want to get shape 2 into A
STA WSYNC
STX GRP0
STA GRP1
; Loop underhead

 

Voilà, both sprites set in only 6 cycles of HBLANK :)

 

I always work like that or similar, I believe outside some sixchar techniques almost non of my code uses VDEL.

 

Interesting - but what is the RhabarberDraw technique?

 

Thanks!

 

Edit: oh. Rhabarber = rhubarb = joke. Gotcha.

Edited by boutell
Link to comment
Share on other sites

Is it practical to have two nonflickering, freely moving, single-height player sprites without using VDEL? My vertical positioning code takes 20-ish cycles to handle just one sprite, and that's about all the horizontal blank time there is to play with. But I get the impression there are games out there that have this problem licked, as long as I don't make other demands as well (playfield changes, etc). Any code out there that demonstrates how to nail this?

 

I need to be able to move the sprites arbitrarily, so a trick kernel that works only when the players are on the same horizontal line (for instance) won't do the job for me.

 

Does the Batari BASIC kernel do this somehow (I'm coding in assembly)? Or does it use VDEL and double-height sprites?

Thanks for any information!

 

(2600 development looks quiet since 2007 began...)

All bB kernels use VDEL and double-height sprites :) The VDEL is done not because of the 2-line kernel but because writes for one player and the ball would otherwise occur during the visible screen, which IMO is the most useful aspect of VDEL.

 

Thanks for answering that one. My planned game doesn't need a ball, and it looks like I can avoid VDEL in that situation and have full-res sprites. Yay!

Link to comment
Share on other sites

Why don't you want to use VDEL?

 

With VDEL, I can only have double-height sprites. And late-era 2600 games in the same genre - Super Football, to be specific - manage not only single-height sprites, but single-height sprites that change color on every scanline!

 

So I'm trying real hard here not to write a game that looks like Gunslinger.

 

Super Football manages a LOT of impressive stuff.

Link to comment
Share on other sites

But for non-VDELxx two-sprite code, try this:

 

Shouldn't it at least have WSYNC somewhere?

 

Well, in theory the desired effect is pretty simple to achieve:

; Loop overhead
; Do whatever RhabarberDraw Technique you want to get shape 1 into X
; Do whatever RhabarberDraw Technique you want to get shape 2 into A
STA WSYNC
STX GRP0
STA GRP1
; Loop underhead

 

Voilà, both sprites set in only 6 cycles of HBLANK :)

 

I always work like that or similar, I believe outside some sixchar techniques almost non of my code uses VDEL.

 

This tactic actually works very well for me. A variation on it is doing the job nicely.

 

Is it also practical to change the player colors on each scanline? Or is that unrealistic given the time constraints we're under? I've tried alternating bitmap bytes with color bytes, placing the color bytes in a second table after the bitmap table, etc., but any way I slice it I wind up taking too long and consuming a second scanline.

 

I've been looking at Super Football and thinking "I should be able to match that!" but apparently that game uses the 6-digit score technique which is an entirely different kettle of fish and probably won't suit my plans.

Link to comment
Share on other sites

But for non-VDELxx two-sprite code, try this:

Loop				;	 23

  SLEEP 35			;+35  58

  lda #PLAYER1HEIGHT
  dcp Player1Counter
  bcs DoDraw1
  lda #0
  .byte $2C
DoDraw1
  lda (Player1Ptr),Y
  sta GRP1			;+18  76

  lda #PLAYER0HEIGHT
  dcp Player0Counter
  bcs DoDraw0
  lda #0
  .byte $2C
DoDraw0
  lda (Player0Ptr),Y
  sta GRP0			;+18  18

  dey
  bpl Loop			;+5   23

 

Other things to consider: are your sprites positioned across the entire X-axis? If they are never at the very edge of the screen then you can get away with writing to GRPx close to, but not inside, the horizontal blank.

 

This is impressive but I sure don't understand it yet. (:

 

How should the player counters be initialized? To the starting scanline of the player in question?

 

And in the spirit of actually understanding rather than blindly using stuff:

 

You load the accumulator, you use dcp which decrements a memory location without borrow (no impact on A that I can see), you branch if carry is set, if not you load zero into the accumulator. Then waht did the first load of the accumulator accomplish? And what does .byte $2C (BIT ABS?) do? I can't find an intelligible explanation of that one.

 

Thanks!

Link to comment
Share on other sites

With VDEL, I can only have double-height sprites.

 

Despite the name, VDEL does not limit you to double-height sprites. Many games with single-height sprites use VDEL a lot.

 

... Because you can write to PF0 at a "bad" time and get that magically copied to the real PF0 when you write to PF1, which you cleverly do at a "good" time.

 

... Right? My early results there are encouraging though my code is definitely still sloppy. Thanks for the hint!

Link to comment
Share on other sites

With VDEL, I can only have double-height sprites.

 

Despite the name, VDEL does not limit you to double-height sprites. Many games with single-height sprites use VDEL a lot.

 

... Because you can write to PF0 at a "bad" time and get that magically copied to the real PF0 when you write to PF1, which you cleverly do at a "good" time.

 

... Right? My early results there are encouraging though my code is definitely still sloppy. Thanks for the hint!

That's right. "VDEL" is usually said to mean "Vertical DELay," but I like "Video DELay" better, since that's a more accurate description of what it does. Likewise with another "V" register-- "VBLANK," or "Vertical BLANKing," but it's really "Video BLANKing," since it can be used anywhere, even in the middle of drawing an active scan line.

 

Michael

Link to comment
Share on other sites

   lda #PLAYER1HEIGHT
  dcp Player1Counter
  bcs DoDraw1
  lda #0
  .byte $2C
DoDraw1
  lda (Player1Ptr),Y
  sta GRP1	;+18  76

 

This is impressive but I sure don't understand it yet. (:

 

How should the player counters be initialized? To the starting scanline of the player in question?

 

And in the spirit of actually understanding rather than blindly using stuff:

 

You load the accumulator, you use dcp which decrements a memory location without borrow (no impact on A that I can see), you branch if carry is set, if not you load zero into the accumulator. Then waht did the first load of the accumulator accomplish? And what does .byte $2C (BIT ABS?) do? I can't find an intelligible explanation of that one.

DCP decrements the memory location *and* then compares the result with A, setting flags appropriately. If you don't want to use illegal (or bonus, or...) operations, then the same effect can be achieved this way:

   tya
  sec
  sbc Player1Counter
  adc #PLAYER1HEIGHT
  bcs DoDraw1
  lda #0
  .byte $2C
DoDraw1
  lda (Player1Ptr),Y
  sta GRP1	;+20

 

I can never set Player1Counter correctly on my first try, but it should be set to (I think) the height of your display minus the starting scanline of the player in question plus the height of the player. The idea is that you are decrementing that value every scanline and when it is equal to the height of the player (#PLAYER1HEIGHT) it will start drawing.

 

The .byte $2C trick is just that, a nifty little trick to skip the next two bytes (i.e., skip LDA (),Y). You can also use branching (BEQ, JMP, etc.), but it saves a byte over a conditional branch and the timing works out perfectly, which is important if you don't want to have to hit WSYNC every line. It screws up the flags but doesn't change any registers or RAM.

 

That make sense?

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

Is it also practical to change the player colors on each scanline? Or is that unrealistic given the time constraints we're under?

 

Depends a bit on where you're objects are allowed to be horizontally. If there's no restriction at all, you're completely limited to what you get done during the couple of HBLANK cycles.

Link to comment
Share on other sites

Depends a bit on where you're objects are allowed to be horizontally. If there's no restriction at all, you're completely limited to what you get done during the couple of HBLANK cycles.

 

At some expense in code size, you could use two kernels: the first when the object is nearer the left half of the screen, and the second when the object is nearer the right. Toyshop Trouble has two kernel routines for the conveyor sections based upon whether they are near the left half of the range or the right half, and it switches between the two invisibly. That particular switch was done because of different masking requirements rather than write timing, though write timing played a role as well. The key point is that it demonstrates that it's possible to switch kernels completely invisibly to the player.

 

Incidentally, a couple of other points about Toyshop Trouble:

 

-1- The player is seventeen scan-lines high, and the screen is divided into sixteen-line zones which are displayed with Y counting from 15 to 0. The top and bottom colors of the player are always the same color. Consequently, to color the player I have two copies of the 16-byte color in ROM consecutively, and I can set the player color with "lda (clptr0),y / sta COLUP0" without having to do any decision making.

 

-2- If there weren't so many animation frames for the player, I would store its shape in memory preceded and followed by 15 zeroes (this is what I did in an earlier version). Zones alternate between what I call "a" and "b" zones, and so within a zone I'll use either "lda (shptr0a),y / sta GRP0" or else I'll use shptr0b; the "a" zones have enough spare time to determine whether the player should appear in the next "a" and "b" zones, so I set the pointers as appropriate. The "b" zones have no time for decision making, but can just blindly use the "shptr0b" pointer set up during the "a" zones.

 

-3- After Nathan came up with 28 animation frames, it became clear I couldn't afford to store all of them in ROM preceded and followed by 15 zeroes. So I use 32 bytes of RAM, split into 16 bytes for the "a" zone and 16 bytes for the "b" zone. If a player occupies part of a zone, shptr0a or shptr0b will point to the appropriate RAM buffer; otherwise it will point to 16 bytes of zeroes. Note that storing the player shapes in RAM allows for a little bit of extra logic to assemble player shapes out of 16 bodies and 7 hats.

Edited by supercat
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...