-Original Message-
From: Simon Cooke [EMAIL PROTECTED]
To: [EMAIL PROTECTED] [EMAIL PROTECTED]
Date: 21 February 1999 07:38
Subject: Re: SAM FAQ Version 2
Unorthodox frame sync code? Pray tell! I can only think of a couple of
ways of doing it...
OK. Apologies to everyone because this is a quite long. Also, advanced
apologies if it's 'egg sucking' territory to anyone.
God, that sounds patronising... Sorry. :-)
Anyway, deep breath! Here we go:
First, a little description of how SAM Defender works will later highlight my
need to use unorthodox frame sync code.
Defenders' internals are almost exclusively 'stack based'. All of the graphic
routines use the stack for dumping/erasing. The main sprite dumpers are
all hard-coded and use an interleaved format. This format removes the need
for an ADD 128 or SUB 128 to the screen address when moving to the next
screen line, reducing it to a simple INC or DEC of the high-byte with the ADD
or SUB required only when switching the dump direction at the top of the
sprite.
All of the main data structures inside the program are again manipulated using
the stack. One of the main reasons for this is the fact that all of the
movement
within the game is fractional, using 16-bit fixed-point allows easy programming
of inertia and gravity effects. Also, the width of the 'world' is some 2048
pixels,
so 8-bit just doesn't cut it! These 16-bit data lists are much faster to
manipulate
using the stack, especially when all of the usual registers are tied up leaving
only
the fat and lethargic IX or IY pairs, which are to be avoided if performance is
the
key factor.
To simplify, the stack is used almost exclusively because it's so efficient and
fast
at shunting around word-sized chunks of data. There's an *awful* amount of this
being shunted around, especially when you have to dump/erase a 256-pixel wide
landscape, manipulate the data lists for as many as 40 sprites, dump/erase any
of
these sprites that happen to be on the 'window' into the 2048-pixel wide
world...
All this must happen at a constant (or as close as) 50-fps for the game to be
anywhere near the quality of the arcade original. So, the good old stack gives
a
much needed boost in performance. Note the use of 'as close as' above? This
becomes important later.
Because data lists are manipulated using the stack, and these lists contain data
that
is used continuously by the program, any corruption would spell disaster.
Corruption
would occur if an interrupt occurred, for example.
SAM Defender runs with the interrupts in a permanently disabled state to prevent
such
potential corruption. From a performance point of view it wouldn't be
economical to DI,
EI all over the place because stack manipulation is occurring almost
continuously during
the game.
So, back to the question of frame syncing. Usually you can frame sync on
machines
like SAM by having a HALT instruction at the top of your main loop. Trouble is,
HALT
requires the interrupts to be on. Defender runs in a permanent DI state, as
mentioned.
OK, so I could have EI, HALT but that would have meant setting up an ISR that
simply
RETs. Not worth the bother. I'm not complicating the issue here with the
potential for
other types of interrupt to occur...
Originally I was frame syncing by reading the FRAME INT bit on port 249 at the
top of
the main loop. This worked fine, until the game 'missed' a frame, at which time
the
main loop will actually catch the next frame, causing a drop down to 25-fps.
Everything
switching to half-speed for a time, including the sound, then back to full-speed
is just
plain ugly!
If you watch the arcade original you'll notice that it misses frames too. In
fact, it misses
more frames than my SAM version does! But, there's one crucial difference. It
doesn't
drop to half-speed but rather jumps/jerks a bit, with very little slowdown.
How?
I concluded that the programmer was able to detect the fact that the game had
missed a
frame and simply didn't bother waiting for it when the program got back to the
top of the
main loop. He simply allowed the program to continue as if it *had* caught the
last frame,
which technically it did but was a little late!
SAM Defender can never miss any more than *one* frame in a given cycle of the
main loop.
So, knowing you've missed a frame, you can simply continue processing as if you
*had*
waited for it. In other words, on a missed frame remove the frame wait at the
top of the
main loop.
The benefit here is that the program is allowed to run *full belt* for the next
cycle, and for
any cycles after that where a frame is missed, until everything falls back in
sync.
The on-screen effect is identical to the arcade original, a slight jumpiness and
a very small
slowdown which is proportional to the amount of 'extra' work, beyond a normal
frame, that
the program has to do. No more 50% speed drop in game motion, and no audio
quality
loss. Much more pleasing! It also let me allow the