Jump to content
IGNORED

Defender on Intellivision is confusing me. Help!


SpicedUp!

Recommended Posts

I never had this game myself and watched a video of it on Intellivision for the first time this week. And I couldn't really work out how it was done. I can see the laser fire is done using background cards/tiles. But how are the pixels that represent the enemies etc on the radar done? It's hard for me to imagine they are done with the background tiles or cards since they seem to move indepently! If not, did they really use sprites for these?? Would be interested to hear. Most games I can see what parts use sprites and what parts use background cards/tiles, but this one has me confused.

 

Also it looks like a pretty good port. Surprisingly so.

Edited by SpicedUp!
Link to comment
Share on other sites

Since you can redefine cards on each frame, you can allocate a few of those as a pseudo bitmap where you plot individual pixels within the cards for the radar display. I would presume that is how it is done in this case.

 

Already back in January 2016, I toyed a little with this kind of pseudo bitmap in a program that draws a sinus curve. It doesn't redefine cards every frame, but other examples already do that.

dynagram.bas dynagram.rom

 

Here is another one that uses an array of 16-bit variables to store the definition of one GRAM that is updated routinely.

dynamicgram.bas dynamicgram.rom

 

Yes, it is a coincidence that the two programs almost are called the same thing despite doing different actions...

  • Like 1
Link to comment
Share on other sites

Thank you but i can't see those because I havent got iNTY Basic or an emulator yet. I will check them very soon.

 

I think I understand what you mean with the pseudo bitmap but surely to store every possible combination of pixels in a 8 x 8 square would need 2 to the power of 64 number of tiles? Or am I misunderstanding?

Edited by SpicedUp!
Link to comment
Share on other sites

2 hours ago, carlsson said:

You would define them on the fly in one way or another, like I do with the sinus curve. Just like with DEFINE, if you POKE directly into GRAM memory, I believe changes won't take effect until next frame.

You could poke directly into GRAM, but you would have to do it right after WAIT, during the VBLANK period; or perhaps in an ON FRAME event, which is called from the ISR context.

 

Otherwise, you can't really buffer pokes to GRAM --it is completely inaccessible outside of the VBLANK period.  You would have to use DEFINE.


One typical way to do this is to create an array in 8-bit RAM, poke into it at your leisure, and then blit it into GRAM using DEFINE.

 

    dZ.

Link to comment
Share on other sites

1 hour ago, carlsson said:

Ok, perhaps IntyBASIC shadows those POKEs until next vertical blank. Admittedly I haven't tried my program on the real hardware, but in jzintv it works like I expected it to.

IntyBASIC does not shadow GRAM, you have to do it yourself.  You will have to allocate an array in 8-bit RAM and then blast it during VBLANK.

 

However that can be slow to do in IntyBASIC.  The fastest way to copy GRAM is from 16-bit RAM, using a tight, specialized assembly language copy routine that takes advantage of byte swapping and auto-increment indexing registers.  IntyBASIC does not give you direct access to those features.


What IntyBASIC offers is buffered GRAM loading with the DEFINE statement.  These are indeed deferred until the next VBLANK, and work by copying 16-bit data with a specialized routine as described above.

 

Unfortunately, DEFINE does have limitations in that it can only copy one -- or two, with ALTERNATE -- contiguous blocks of GRAM per frame.  If you need to load multiple areas of GRAM, or source your data from non-contiguous blocks, you are out of luck.

 

    dZ.

Link to comment
Share on other sites

I tried to analyze the generated assembly code but I'm not sure what happens in this case. I only remember that blasting multiple POKEs into GRAM memory at $3800 would not work without WAIT between each. Whether or not that is intended behavior I'm not sure but "it works for me" (at least emulated) is all I can add. Please have a look at the source code above if you want to dig further into it.

Link to comment
Share on other sites

12 minutes ago, carlsson said:

I tried to analyze the generated assembly code but I'm not sure what happens in this case. I only remember that blasting multiple POKEs into GRAM memory at $3800 would not work without WAIT between each.

Yes, that's how it works because right after WAIT, depending on the load, you may still be within the VBLANK period.

 

12 minutes ago, carlsson said:

Whether or not that is intended behavior I'm not sure but "it works for me" (at least emulated) is all I can add. Please have a look at the source code above if you want to dig further into it.

Sorry, I wasn't arguing against your example.  I was actually agreeing, but explaining how it works:  if you want to poke directly into GRAM, you have to do it during the brief VBLANK period at the top of a frame.  This can be right after WAIT, or in an ON FRAME event which is closer to the start of VBLANK.

 

There is a danger, though:  depending on the load of the runtime during the ISR (music, scroll, DEFINE, etc.) you may run out of time, the poke may fail.

Link to comment
Share on other sites

53 minutes ago, carlsson said:

Aha, so it should really be WAIT:POKE in order to get the pixel through, not POKE:WAIT?

Yep.

 

But, I think you may be better off doing ...

ON FRAME GOSUB UpdateGram

That's because IntyBASIC calls out your subroutine directly from its Interrupt Service Routine (ISR) before returning control to your program, which may give you even more free-poking time. ;)

 

   dZ.

  • Like 1
Link to comment
Share on other sites

Thanks. Regardless, it is proven that GRAM is more flexible and line/pixel oriented than just chunks of entire cards as it first might look like and which raised the question in the first place. I understand that pseudo bitmapping isn't that well known as most systems that offer user definable graphics symbols also has some mode of full bitmapping. Since I have a long history from the VIC-20 scene, I am painfully aware about setting up characters to form pseudo bitmaps which is what is done here as well. I suppose the software sprites fall in the same category since you allocate a couple cards and put on the screen, and then constantly redefine those to give the impression of movement etc.

Link to comment
Share on other sites

Just for reference and context to anybody else following this thread, the reason you need to WAIT before accessing GRAM (or go through the ON FRAME event) is because the video chip (STIC) locks out the CPU from accessing any of the graphics subsystems, including GRAM and the sprite registers, among other aspects of the video chip.

 

Once per frame, while the raster resets to start drawing a new video frame, the STIC relinquishes control of the graphics bus and lets the CPU in.  This is called the "vertical blanking" period, or VBLANK, and it is the only point at which you can update the hardware sprite registers, scroll or border details, video mode, GRAM, etc.

 

60 times per second, the STIC issues an "interrupt request" to the CPU alerting it of VBLANK, and gives it a chance to execute a program-defined Interrupt Service Routine (ISR).  The ISR is intended precisely for the program to prepare video data for the next frame that the STIC will draw.

 

For the most part, IntyBASIC abstracts all this and handles it for you.  This is why you can use DEFINE and SPRITE statements without worrying about timing.  The IntyBASIC runtime will then buffer the commands internally until the next VBLANK interrupt, and execute them for you.

 

To poke directly into GRAM, you have to peek through the veil and handle the complexity itself.  You can't create your own ISR (well, you could, but I strongly recommend against it, lest you break IntyBASIC's runtime), but you can use WAIT, which will wait with a busy-loop until the next interrupt, and returns control to your program when the runtime finishes its ISR processing.

 

At that point you may or may not still be within the brief VBLANK period (depending on processing load), so you roll the dice.

 

Alternatively, IntyBASIC offers ON FRAME, which lets you hook into the ISR itself -- IntyBASIC will do its normal thing, but call your subroutine immediately after, still within the ISR, avoiding the additional overhead incurred in context-switching back to your program.  (That cost will still be there, but you don't care if it happens after you update GRAM, as long as you get quick access within the VBLANK period.)

 

Lastly, what sort of "load" can cause you to miss the VBLANK period after WAIT?  Well, anything that IntyBASIC needs to do for you on each frame adds to that load.  That includes playing music, executing any deferred MODE, SPRITE, SCROLL, or DEFINE statements, etc.

 

    dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

3 minutes ago, carlsson said:

Thanks. Regardless, it is proven that GRAM is more flexible and line/pixel oriented than just chunks of entire cards as it first might look like and which raised the question in the first place.

Oh yes, absolutely:  you can poke directly into it for dynamic drawing.  I think this is what Oscar does in his Missile Domination game to draw the missile trace lines.

 

It depends on how much of it you need to do.  If you need to do a lot of computations in order to poke directly into GRAM, then you may be better off pre-computing it "offline," and then use DEFINE to do the rapid copy.

 

It is still dynamic in that you are computing the blocks at runtime, but it is a deferred copy of a precomputed card or block.

 

3 minutes ago, carlsson said:

I understand that pseudo bitmapping isn't that well known as most systems that offer user definable graphics symbols also has some mode of full bitmapping. Since I have a long history from the VIC-20 scene, I am painfully aware about setting up characters to form pseudo bitmaps which is what is done here as well. I suppose the software sprites fall in the same category since you allocate a couple cards and put on the screen, and then constantly redefine those to give the impression of movement etc.

Yes, using characters to create a pseudo-bitmap is definitely possible, just a bit more complex than poking pixels directly onto the screen.

 

However, it has been done to great effect, take for example the aforementioned Missile Domination.  I also think Oscar does it for the fire torches in Sydney Hunter.

 

I am not sure, but I think an old example program created by GroovyBee does it as well to  create "GRAM fonts" by dynamically transforming GROM characters into GRAM.  I'll have to check, for I do not know if he does it by poking to GRAM directly, or pre-computing in a buffer.

 

   dZ. 
 

Link to comment
Share on other sites

I believe you and me are referring to the same techniques, but use different terms. The Intellivision surely is character based (plus MOBs) so there are no individual pixels to set or unset unless you line up a number of cards from GRAM on the screen, one instance of each.

 

Once there, you can either allocate additional RAM if you have it somewhere (with JLP extension it would not be any issue) and use it for buffering and DEFINE those characters in one go, or calculate a pixel position within the GRAM according to the layout you have chosen (number of rows and columns), wait for an opportunity to access the GRAM yourself - possibly through the ON FRAME or manually waiting for vertical blank - and then plot as many pixels as the time frame allows. For a game like Defender where you perhaps have 8-10 pixels in total to indicate your and opponent positions, it might be doable either way. For something more advanced where you might want to update up to 16 cards per time, double buffering through another memory area (if you can find it) would be the preferred way.

Link to comment
Share on other sites

10 minutes ago, DZ-Jay said:

I am not sure, but I think an old example program created by GroovyBee does it as well to  create "GRAM fonts" by dynamically transforming GROM characters into GRAM.  I'll have to check, for I do not know if he does it by poking to GRAM directly, or pre-computing in a buffer.

 

   dZ. 
 

I just confirmed that it does poke directly into GRAM.  It copies the cards directly from GROM, shifts and transforms them dynamically, byte by byte, and posts them to GRAM.

 

However, it does this with assembly language in order to optimally copy the whole character set.  It also calls IntyBASIC WAIT loop directly prior to copying, taking a frame for itself.


So, perhaps that's not a good illustration of the technique since it is not a pure "IntyBASIC solution."

 

   dZ.

Link to comment
Share on other sites

8 minutes ago, carlsson said:

I believe you and me are referring to the same techniques, but use different terms. The Intellivision surely is character based (plus MOBs) so there are no individual pixels to set or unset unless you line up a number of cards from GRAM on the screen, one instance of each.

I think we are actually talking the same language, and definitely referring to the same technique.  I just meant that doing what you called pseudo-bitmaps with characters in a character-based system is more complex than doing it on a bitmap-based system, where you can poke pixels directly using screen coordinates.  

 

Perhaps that's why it is not a very common technique.

 

Sorry for not being all that clear.

 

 

8 minutes ago, carlsson said:

Once there, you can either allocate additional RAM if you have it somewhere (with JLP extension it would not be any issue) and use it for buffering and DEFINE those characters in one go, or calculate a pixel position within the GRAM according to the layout you have chosen (number of rows and columns), wait for an opportunity to access the GRAM yourself - possibly through the ON FRAME or manually waiting for vertical blank - and then plot as many pixels as the time frame allows.

Agreed.

 

8 minutes ago, carlsson said:

For a game like Defender where you perhaps have 8-10 pixels in total to indicate your and opponent positions, it might be doable either way. For something more advanced where you might want to update up to 16 cards per time, double buffering through another memory area (if you can find it) would be the preferred way.

For Defender it shouldn't be a big deal since the mini-map is not too big.


Bigger regions will require a bit more work, since you have to contend with the limitations on the size of GRAM, and how many cards you can copy in one frame, etc.  Reusing GRAM cards in BACKTAB adds another dimension to the complexity as well.


Personally, if I were to do something like Defender in IntyBASIC, I would create a 16-bit buffer representing packed cards in GRAM, update them during the main game loop, then use DEFINE to copy them over.

 

If I were not constrained to IntyBASIC, I would get fancier:  I would create a buffer in 8-bit RAM to draw the GRAM cards, then track which ones were updated at any given time, and copy only those during VBLANK in an optimized loop.

 

Depending on the cost, I could try copying only specific rows that changed in each card, rather than whole cards at a time.  I would have to experiment with that.

 

I do not think that is impossible to do in IntyBASIC, I just do not know if it can be done optimally along with all other processing; but mostly because I lack the experience in the language.  IntyBASIC is very RAM-happy, and likes to make round-trips to RAM -- often multiple times to the same addresses -- when resolving arithmetic or logical expressions.
 

    dZ.

  • Like 1
Link to comment
Share on other sites

11 minutes ago, DZ-Jay said:

I just meant that doing what you called pseudo-bitmaps with characters in a character-based system is more complex than doing it on a bitmap-based system, where you can poke pixels directly using screen coordinates.

Ok. I agree that on e.g. the C64 where you can set up the entire screen as a 320x200 bitmap that is easier if you really need that resolution, usually to display pretty images. Actually it reminds me that Intycolor essentially does the very same thing: create a pseudo bitmap using all available GRAM and then plot those to screen in desired block patterns. It has the ability to reuse the same GRAM for areas that are the same, which probably is where a pseudo bitmap differs the most from a full bitmap. In the later case you save no memory by using the same pattern on multiple places.

 

Then we have some of those systems where the entire screen is bitmapped and character modes are simulated. ZX Spectrum is the first that comes to my mind. It lets you freely draw lines on top of characters because everything is a bitmap, but also has the drawback that the small area of user definable graphics lacks functions in BASIC to recognize since the command to check which character is at a given position compares the screen bitmap with the ROM pixel by pixel and once there is something that doesn't match - could be a circle through your M - the routine would fail. Part of that can be overcome by moving the entire character generator to a different address in RAM, but it still is bitmap all the time (and a rather hairy memory map, I'd like to add).

 

Simply, not two retro video game systems are equal or even operate on the same principles.

Link to comment
Share on other sites

27 minutes ago, carlsson said:

Simply, not two retro video game systems are equal or even operate on the same principles.

That is a very interesting and astute observation.  I guess they were all trying to solve the same problem, at a time when there were no established rules or solutions.


I imagine that the same applies to the myriad color palettes created for each platform.

 

    dZ.

Link to comment
Share on other sites

Another thing to consider since we're touching upon non-Intellivision systems is that the memory footprint for a bitmapped mode is rather expensive. For instance many C64 games that have fast, smooth scrolling use text based modes with custom characters. The hardware scroll is not an issue, but once you need to shift all the data that is 40x25 = 1000 bytes (plus possibly another 1000 for colours). If you work with bitmapped mode, multiply the first figure by 8 for number of lines per character, and possibly add another 1000 if you're working in 160x200 mode with added colour resolution.

 

Thus a game like Defender would even on the C64 use text mode and create the radar display with custom characters just like here, despite the system has "better" graphics modes available.  Technically the VIC-II allows addressing on the fly so you could change graphics mode for a split screen effect but usually it would add very little, of course depending on which game you are working on.

 

Edit: Well, technically you could reuse sprites on the C64 so the radar screen is generated by sprites instead of characters. Unfortunately multiplexing sprites on the Intellivision is not done as easily (without flicker).

Edited by carlsson
Link to comment
Share on other sites

1 hour ago, carlsson said:

Another thing to consider since we're touching upon non-Intellivision systems is that the memory footprint for a bitmapped mode is rather expensive. For instance many C64 games that have fast, smooth scrolling use text based modes with custom characters. The hardware scroll is not an issue, but once you need to shift all the data that is 40x25 = 1000 bytes (plus possibly another 1000 for colours). If you work with bitmapped mode, multiply the first figure by 8 for number of lines per character, and possibly add another 1000 if you're working in 160x200 mode with added colour resolution.

 

Thus a game like Defender would even on the C64 use text mode and create the radar display with custom characters just like here, despite the system has "better" graphics modes available.  Technically the VIC-II allows addressing on the fly so you could change graphics mode for a split screen effect but usually it would add very little, of course depending on which game you are working on.

That is a good point.

 

I think the added problem with the Intellivision is the limited GRAM space, which does not allow for a full screen buffer.  What's more, it needs to be shared with characters for MOBs and other visual elements not directly associated with a pseudo-bitmapped region.

 

1 hour ago, carlsson said:

Edit: Well, technically you could reuse sprites on the C64 so the radar screen is generated by sprites instead of characters. Unfortunately multiplexing sprites on the Intellivision is not done as easily (without flicker).

To be brutally realistic, it cannot be done at all without flicker, since there is no access to the horizontal blanking interrupt within a frame, like in the C=64 and other platforms.

 

     -dZ.

Link to comment
Share on other sites

18 hours ago, carlsson said:

You would define them on the fly in one way or another, like I do with the sinus curve. Just like with DEFINE, if you POKE directly into GRAM memory, I believe changes won't take effect until next frame.

Yes sorry. I realized what you actually meant last night after thinking about it. I get it now. That's pretty cool you can make a small bitmap like that! I was stuck in the frame of mind where all the tiles/cards were just pre-stored on cartridge. Are there any other official games that use pseudo-bitmaps like that?

Link to comment
Share on other sites

1 hour ago, DZ-Jay said:

To be brutally realistic, it cannot be done at all without flicker, since there is no access to the horizontal blanking interrupt within a frame, like in the C=64 and other platforms.

 

     -dZ.

I have to say I know very little about this type of technical matter but is there any way they could have added a simple chip to the game cartridges to allow sprite multiplexing? If it was a cheap chip they could have put it in all carts maybe for a low cost.

Edited by SpicedUp!
Link to comment
Share on other sites

Actually the STIC allows an external video overlay, either with the internal screen enabled or disabled. I think that is how the Keyboard Component works, and also is what JohnPCAE is working on in the main forum, see the "Bird's nest" thread. Thus you would not improve the STIC capacities, but have a parallel video generating source. There is also the Tutorvision etc which are rare variants with extra GRAM onboard. It does not add any more sprites or multiplexing, but simply lets you define more symbols e.g. for a pseudo bitmapped display. Those are non-standard though so more of an anomaly than anything to put a lot of effort in if you're learning the system.

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