21

I would like to know how sprites in DOS games for vintage video cards like Hercules, CGA, EGA and VGA were programmed.

I mean how was the duration of each sprite timed and how they were blitted on screen. Is there any source code I can view to understand this? (I havent been able to find any.)

I have always wanted to understand how sprites were rendered on screen in games like Prince of Persia for example.

7

6 Answers 6

24

Hardware sprites as they exist in a lot of arcade games and consoles are just graphical rectangular objects with a given number of colors.

One of this color is special and is transparent, which allows the sprite to draw onto the background without drawing a big rectangle around it. It just draws the non-transparent pixel.

Then when moved, it's just displayed somewhere else.

But PC graphical cards didn't have any. So how did they draw those "sprites" ?

Well, by using the image data, and a mask.

By using software logic operations (AND, OR, XOR) on data, background and mask, it is possible to emulate hardware sprites. It's just slower and more tedious. Let me use an example:

Image data: image data Mask data: mask

To draw (applies to planar or chunky displays):

  • erase previous position if any by copying the original background at the previous location
  • take the address of the background rectangle where the "sprite" must be displayed
  • apply AND with a negative mask on it to "cut" data behind the mask. Now you have a hole in the background where the mask is.
  • apply OR with the real data to insert the data in the background. OR will only draw inside the cut mask.

On planar displays, this is the only efficient way. On chunky (one byte per pixel) displays, one could also read the input and draw only if not "black". This can be done too, but you deprive yourself of the genericity and must choose a transparent color that is not in any object.

For instance inside the character above, the hair and the belt of the karateka MUST be black, not transparent. The mask removes the background below the hair & belt so the black color is materialized.

7
  • This brought back memories of generating sprite sheets and masks. In the absence of double buffering of the display, this AND with the mask, XOR with the sprite procedure caused a lot of flickering. I remember this flickering in some TRS-80 games. Mar 25, 2022 at 15:41
  • 1
    yes, if you can't know when the VDU is drawing you can't escape flickering. Mar 25, 2022 at 16:10
  • Why doing that instead of looping through sprite pixels and copying only non-transparent ones?
    – Amessihel
    Mar 25, 2022 at 23:06
  • 2
    By using bit operations, multiple pixels can be processed simultaneously. A 16-color pixel uses 4 bits, for example, so a 16-bit operations can process 4 pixels at once. Bit planed modes are a bit different, but you might end up handling 16 pixels with a 16-bit operation but you need to do it once for each of 4 planes, so it averages out about the same.
    – Extrarius
    Mar 26, 2022 at 1:24
  • 1
    This is also exactly what the Amiga blitter does in hardware, and why it has three inputs and one output: background, mask, object -> display. Something in between hardware and software sprites.
    – pipe
    Mar 26, 2022 at 21:00
19

PC graphic cards do not have sprites (*1). They only feature a graphics buffer. All graphics, including 'sprites', have to be

  • drawn by software,
  • undrawn by software and
  • redrawn by software,

onto this 'canvas'. Doing such is basic bitmap/pixelmap manipulation, Graphics 101.

Some cards and modes do offer more than one buffer allowing to compose (draw) a picture in full before switching to display. This avoids flickering and helps in synchronisation. Everything else is up to your software.


*1 - Well, ignoring some fringe cases like a hardware mouse cursor.

13
  • 4
    Only some later Super VGA chipsets had support for hardware cursor, but since standard CGA/EGA/VGA was expected games or mouse drivers rarely used the feature. Chipset specific Windows drivers could use that and other hardware acceleration/blitting features such as line drawing.
    – Justme
    Mar 24, 2022 at 17:48
  • 1
    Since the question includes "how they were blitted on screen," it seems to me most reasonable to read the question as the OP wants to know how PC games did a software implementation of sprite-like functionality. You correctly describe what the program needs to do, but not how it does it ("how they were programmed").
    – cjs
    Mar 25, 2022 at 5:00
  • @cjs Well, you may have forgotten about this being Retrocomputing.SE, not a generic CS forum. Any generic explanation about manipulating a graphics buffer is, by default, off topic on RC.SE, thus including it might not be appropriate. That's whey the answer points out the RC related information the OP seams to miss.: There is just a graphics buffer to be manipulated directly by an application program. Nothing else.
    – Raffzahn
    Mar 25, 2022 at 16:48
  • 3
    I'd argue that directly manipulating a frame buffer to create sprites - without hardware acceleration and going through several layers of driver abstraction - is very much an RC topic. Mar 25, 2022 at 18:24
  • 2
    @MichaelGraf: If one considers some of the specialized tricks some programs did to exploit the planar modes and bit-masking operators of the VGA, those would definitely seem retrocomputing related, since no other display since has used the same quirky design.
    – supercat
    Mar 30, 2022 at 13:12
14

There is no one true way to do it, and since you receive no help for sprites in hardware, sprites are nothing special really, the pixels of a sprite are drawn on screen like any other pixels are.

If you have to draw a ball and move it around the screen, you just keep track of the ball coordinates yourself, and draw pixels of the ball to the new coordinates, perhaps drawing black or background image pixels back to the old ball coordinates first, before drawing it to new coordinates. And for a ball, if you store it as a bitmap of 16x16 rectangle, you might only want to draw the pixels of the ball, and not the whole rectangle, if the ball is moving on top of a background bitmap. The ball bitmap could have a color that is defined to be transparent so those pixels are not drawn on screen, or it could define separate mask or list of pixels which should be skipped or drawn. And when removing the ball, you can only redraw the ball pixels with background pixels, or for simplicity, you can just repaint the whole dirty rectangle of 16x16 pixels where the ball was.

And when moving the ball, increase the coordinates by the value how much you want to move the ball, in the time you want to move the ball, so that will be the speed of the ball. For example CGA, EGA and VGA all have video modes that output 60 frames per second, it might give you a timebase of moving one pixel per frame, or two pixels per frame for twice as fast movement, or one pixel per two frames for half as fast movement.

There are also other ways of keeping time, as a PC has a timer chip (Intel 8253 or 8254) which can be used to set to tick at some specific rate you want and for example update coordinates 100 times per second and draw 1000 different balls on screen as slowly as the PC is capable of.

You can find tutorials for this in lots of places. For example one source with source code examples might be the graphics programming book by Michael Abrash that can be found online here:[https://www.drdobbs.com/parallel/graphics-programming-black-book/184404919]

Another source might be the VGA Trainer series of tutorials made by Grant Smith a.k.a Denthor of Asphyxia. The first tutorials are included in the PC Game Programmer's Encyclopedia, PCGPE.

5
  • 7
    You may want to just link to Abrash's book, given that Abrash released it for free in 2001. Here's a markdown conversion that claims to have Abrash's blessing, includes EPUB and Mobipocket renderings, and links to the original PDF release.
    – ssokolow
    Mar 24, 2022 at 17:56
  • @ssokolow For cases like this, where you're not changing what the answer says, it's not just reasonable but good simply to edit the answer to include the useful links you've dug up. (This is exactly why we have edit functionality.)
    – cjs
    Mar 25, 2022 at 5:03
  • @cjs Thanks. I wasn't sure if that counted as too much or not.
    – ssokolow
    Mar 25, 2022 at 9:51
  • ...and I just remembered another reason I didn't. There was a hazy uncertainty in the back of my mind that I now realize was the knowledge that Michael Abrash wrote more than one book that Justme might be referring to. Power Graphics Programming, Zen of Graphics Programming, and the Graphics Programming Black Book. If the phrasing was "a [...] by Michael Abrash", that's one thing, but it's "the [...]".
    – ssokolow
    Mar 25, 2022 at 9:57
  • ...and I just found the backstory to how the Markdown conversion got made.
    – ssokolow
    Mar 25, 2022 at 10:03
5

Original hardware back then had very little capability. You could store multiple screens of pixels in video memory, which allowed for double-buffering, but that's about it. If you're interested in reading all about it, you can check out one of the EGA/CGA/VGA programming books, many of which are readily available online for free (e.g. see this book EGA/VGA: A Programmer's Reference Guide).

There were no sprite capabilities written into the hardware, so the software was responsible for drawing the sprites. Most games were written with a double buffer scheme, either in main memory or video memory. For main memory, an area of memory was dedicated for storing the next frame's pixels, and then blitted using something like REPZ MOVS, which was a relatively fast string copy command (about 3 + pixel count clock cycles). For video memory, the next frame is drawn in the video buffer, then the page offset was changed to flip pages; this method was the ideal method for maximum performance, since you saved tens of thousands of clock cycles by not having to blit every frame.

So, during startup, the game would load the sprite data into memory, and each frame, it would draw the background, then each sprite, using either an image mask or a "transparent" color (typically 0, but it was programmer's choice, since it's all in software), then halt for a vsync signal before using an efficient string copy command to avoid screen tearing (where you'd see parts from two different frames), or a page flip, whichever was appropriate, then go back to the start of the loop to handle the next frame.

The graphics memory is laid out in a straight line as far as memory addresses are concerned, so it was necessary to figure out the coordinate for where each pixel was via x times screen width plus y. On really old processors, this was too slow to perform millions of times per second, as multiplication was hideously expensive in clock cycles, so programmers often had to resort to something like:

pixel = (x << 6) + (x << 8) + y

Where << is the shift-bit-left operator. Three adds and two shifts could be done in 10 clock cycles, while a single IMUL took at least 80 clock cycles. This is the difference between 60 frames per second and about 10, just optimizing pixel placement code. A lot of games were written in assembler back then, or at least the video graphics parts, simply because there were only so many clock cycles to go around, and compilers back then were quite literal (today's compilers can optimize nearly as well, or better than, most human programmers).

At any rate, if you're interested in seeing what CGA programming looked like, you could check out some open-source software, such as this repository that includes a ton of CGA code. GitHub has topics for CGA, EGA and VGA, which contains at least a few interesting source codes you might want to check out.

Most commercial games never released their source code, so it can be hard to track down specific examples. That said, you can also try your hand at disassembly/decompilation. Modern tools of this nature produce at least somewhat legible code, especially for binaries that are that old, assuming you can get your hands on the original executable somehow. There are abandonware sites you can look for that host games from companies that no longer exist as well. Using disassembly tools on those would likely be enlightening, most of the software back then was relatively small in size and pretty easy to read with some practice.

4

The easiest way to try out vintage VGA graphics programming is use the BIOS to switch to mode 0x13 (which is 256 colours, 320x200 pixels, 1 byte per pixel, laid out in a linear format), and then write to memory addresses 0xA000:0000 to 0xA000:FFFF.

This snippet is taken from http://www.brackeen.com/vga/basics.html#:~:text=as%20mode%200x13.-,Structure%20of%20mode%200x13,left%20corner%20(Figure%201).

regs.h.ah = 0x00;  /* function 00h = mode set */
regs.h.al = 0x13;  /* 256-color */
int86(0x10,&regs,&regs); /* do it! */

typedef unsigned char byte;
byte far *VGA = (byte far*)0xA0000000L;     
unsigned short offset;
...
offset = 320*y + x;
VGA[offset] = color;
1
  • 3
    For high-performances, Mode X was preferred: 320x240 (so square pixels), 256 colors, with up to triple buffer capacities...
    – Wisblade
    Mar 26, 2022 at 0:42
0

Another thing to keep in mind is, that even in a single game, different parts of a screen or different parts of a game could use different techniques.

E.g. in an adventure game I peeked into, the main view was using dirty rectangles, while other parts were just overwriting with a background color. The sprites were stored as a sequence of nibbles indexing a 16-color palette, 0-index meaning a transparent pixel. The backgrounds were using 256-color palette, though. Another part of the game (intro) used for static but animated sprites XOR-ing with the background and did not redraw at all.

The drawing was done during vertical refresh, to prevent tearing.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .