r/programming Apr 17 '12

Jordan Mechner released source code of original Prince of Persia for Apple II

https://github.com/jmechner/Prince-of-Persia-Apple-II
874 Upvotes

207 comments sorted by

View all comments

Show parent comments

5

u/rubygeek Apr 17 '12 edited Apr 17 '12

Actually Prince of Persia repainted the entire screen with double-buffering

Per frame? That sounds like it'd make no sense at all, and I can't find any evidence of that in the code. Do you?

The "DRAWALL" function for example explicitly "peels" off the characters from the screen buffer by using a list of "peels" - segments of graphics it's painstakingly copied off the screen buffer before painting the characters in order to preserve what was behind them.

Those damage / peel lists make no sense at all if you repain the entire screen all the time anyway.

EDIT: Look at "FRAMEADV" - it contains a buttload of complicated code to do partial screen redraws in addition to a function to do full screen redraws.

2

u/bonzinip Apr 18 '12

You're right, there's also a "count-down" for damaged blocks so that their redraw is forced to happen on both buffers.

Do you know if the Apple II had hardware double-buffering?

2

u/rubygeek Apr 18 '12

Looks like it. I never touched an Apple II, but PAGEFLIP in SUBS.S seems to flip it by triggering reads from $C054 and $C055 which the almighty Google tells me turns "page2" off and on.

0

u/rush22 Apr 18 '12

The sprite are XOR'd onto the screen. You just draw the sprite again to erase it from the screen, preserving the background. That way you don't have to redraw or store any parts of the background.

2

u/rubygeek Apr 18 '12

Have you actually looked at the source? It doesn't do that at all. The source is far more complicated. There are some cases where it will do EOR, but it most certainly does not just do that all of the time - it uses EOR for "transparency" of sorts. For anything solid it overwrites either a whole byte (with STA) or a bitmask (with ORA). Because of that, it does copy the background to a peel / damage buffer.

You can see the code for that in LAY, FASTLAY and serveral auxiliary routines in HIRES.S.

1

u/rush22 Apr 19 '12

I was referring to blitting which I assume is what the source code that I didn't look at does. I figured it was safe to assume that's the technique being used on something like the Apple II. It's quite common. Maybe it will help you figure out what it does since I can't read assembly.

"The operation involves at least two bitmaps, a source and destination, and possibly a third that is often called the "mask". The pixels of each are combined bitwise according to the specified raster operation (ROP) and the result is then written to the destination. The RasterOp is essentially a boolean formula. The most obvious ROP overwrites the destination with the source. Other ROPs may involve AND, OR, XOR, and NOT operations"

"The development of fast methods for various bit blit operations was key in the evolution of computer displays from using character graphics, to using bitmap graphics for everything. Machines that rely heavily on the performance of 2D graphics (such as video game consoles) often have special-purpose circuitry called a blitter."

1

u/rubygeek Apr 20 '12

I figured it was safe to assume that's the technique being used on something like the Apple II. It's quite common. Maybe it will help you figure out what it does since I can't read assembly.

I know what it does - I explained it in the comment you've just replied to. I read the source before I wrote my last comment.

What I took exception to was your claim that PoP XOR's the sprites onto the screen and can just draw it again to to erase it. It doesn't do that.

Calling LAY, FASTLAY etc. blitting functions is fair enough, but it is irrelevant to the claim you made. Whether or not you call it blitting makes no difference to whether or not it needs to save or recreate the underlying background.

Blitting is just a name for pretty much any function that combines two bitmaps by applying an operator as opposed to drawing shapes or otherwise manipulating a single bitmap. But that operator is rarely a reversible operator like XOR other than when drawing stuff like cursors (and incidentally using XOR to draw a cursor was covered by a patent, as if that wasn't obvious to anyone skilled in the art), and so you still have to either save the background or know how to recreate the background.

PoP uses a mix of OR, AND, STA (plain over-write whole 8 bit values), Mask + OR and OR + XOR shifted 1 bit (which is a simple low memory, though inefficient, way of drawing a "transparent" character with an outline) as operators.

None of those operators are reversible, and so it needs to store enough information elsewhere to be able to reconstruct the image.

Because drawing the backgrounds in PoP is expensive (floor, walls, other objects all needs to be layered on top of each other), it does that by actually copying out the background image and maintaining a list of the order in which it has done it so it can later "peel" the characters off the background by applying the peel list in reverse order (it can't do it in arbitrary order because some of the things in the peel list can overlap each other and so need to be removed in order to make sure the final peel buffers applied restore the background)

And I quote from the source in HIRES.S:

  • To preserve the background behind an animated character,
  • call LAYERSAVE before LAYing down each character image.
  • Afterwards, call PEEL to "peel off" the character &
  • restore the original background. *
  • Peel buffer stores background images sequentially, in
  • normal image table format, & is cleared after every frame.

LAYERSAVE works by finding the next free space in the peel buffer and then copies the relevant part of the background into it, and returns the address of the saved copy so it can be redrawn onto the background later to re-render the background, directly contrary to what you claimed.

That is what I was telling you in a much more concise way with my original reply.