Introduction
Arcade Volleyball is a simple volleyball game I remember playing
when I was quite young on a DOS PC. According to
its Wikipedia article, this
game would have been a shareware at the time but it is now in the public domain. I'm not
sure how well known this little game is, but for me, it's a classic!
Also thanks to Wikipedia, I learned that this was originally conceived for the Commodore
64, but was also available for Amiga. The latter was distributed on a floppy
disk included with the Fall 1989 edition of the Compute!'s Amiga Resource magazine,
as an executable accompanied with its
source code.
Source code? This caught my attention! I really like having the (sadly rare) chance to look
at the source code of games from my youth. And this one was coded in C, my favorite programming
language!
As it happens, I recently worked on a few small projects for the Sega Master System
programmed in the C language with the help of
devkitSMS.
So I immediately thought, why not port this game to the SMS? It should be a nice
little week-end project! (and it was!)
Here is a video of this game in action:
The porting process
This is a very simple game and so was the porting process. Still, I tought that it could
be interesting to write a bit about it.
1. Getting my hands on the source code
First I had to find the source code. The Wikipedia article mentioned that it was
included on the cover disk of the Fall 1989 edition of
Compute!'s Amiga Resource... Thankfully,
this has been archived. I found it here:
http://amr.abime.net/issue_1231_coverdisks
2. A quick glance at the code
The game source code came as a single file, ArcadeVolleyball.c, containing about 1500 lines. I went
through it quickly, not trying to understand the game logic in detail, but rather to spot the
inputs (keyboard/joystick) and outputs (graphics, sprites, sound effects).
Main takeaways: The background is drawn programmatically (i.e. it's not an image or a tilemap) and the
game uses 16x26 hardware sprites. The joystick button state is checked by accessing specific memory addresses,
and the sounds effects are samples.
3. A first compile using stubs
Changes to the source code began here. I commented parts of the code that would not apply (loading sound
effects from disk for instance) and added a few empty functions (stubs) to replace those that
were missing (Amiga system functions for instance), or used macros to make some calls disappear completely.
I also defined a few data types, for instance USHORT to mean uint16_t.
No big issues, after a couple of minutes the game could be complied.
4. Reimplementing inputs (keyboard, joystick)
Here is the original joystick code. Obviously reading those memory addresses
has no meaning on the SMS!
void joystick(pl)
int pl;
{
USHORT *joy[2] = {
{(USHORT *)0xDFF00A},
{(USHORT *)0xDFF00C}
};
USHORT *firereg = (USHORT *)0xBFE0FE;
keymove[pl].right=0;
keymove[pl].left=0;
keymove[pl].up=0;
if ((*joy[pl^jflag]&0x0002)==0) keymove[pl].left=-2;
if ((*joy[pl^jflag]&0x0200)==0) keymove[pl].right=2;
if ((*firereg&(0x40<<(pl^jflag)))==0) keymove[pl].up=1;
}
I rewrote the above to call SMS_getKeyStatus(). I also rewrote another function which was checking for the ESC
key to end the game. (I mapped the SMS pause key to this).
5. Dealing with the different screen size
This game is designed for a resolution of 320x200 pixels. But on the SMS, the screen is only 256x192...
At first, I thought of modifying the core of the game to work within the smaller screen, but after looking at the
docollisions() function, this did not seem like a very good idea. There are dozens of checks for hardcoded coordinates
checking the player, ball, net, borders, and the ball velocity would also have to be changed... Chances are I would break
something or alter the feel of the game without knowing it unless I know
exactly what I was doing.
Here is a portion of that docollisions() function:
if (i && tby>81 && tbx>127 && tbx<160) {
if (tby>91) {
if (tbx<148)
bvelx=-abs(bvelx);
else
bvelx=abs(bvelx);
}
else
if ((tbx>147 && 161-tbx>=polecol[91-tby]) ||
(tbx<148 && tbx-133>=polecol[91-tby])) {
if (bvely>0) {
dx=tbx-145;
if (dx<-5) bvelx=-abs(bvelx);
if (dx>5) bvelx=abs(bvelx);
bvely=-abs(bvely);
}
if (abs(bvelx)>32) bvelx=bvelx>>1;
if (abs(bvely)>32) bvely=bvely>>1;
}
Sure, I could understand everything if I really wanted to, but one must chose his battles. Besides, there was
an easier way: Scaling the final coordinates.
The game loops checks where the objects are, handles movement, collisions, etc, and then simply instructs
the hardware to display the sprites at new coordinates. Those coordinates are meant for a 320x200 screen. To scale them down to fit the SMS 256x192 screen, I do the
equivalent of this:
X_sms = X - X/4 - X/16;
Y_sms = Y - Y/8;
With this, a point at the lower right screen coordinate (319,199) will correspond to point 219,174, well inside
the SMS display. So I apply the above transformation before calling SMS_updateSpritePosition.
Collisions between the ball and the players remain functional, as well as collisions between the ball and the environment,
provided that the background elements (net, borders) are drawn at the right places.
6. Extracting the sprites
On the Amiga, the game uses 6 hardware sprites of 16x26 each. Every game element (ball, players) is
built from a pair of adjacent sprites, and therefore occupies 32x26. Image data for the sprites is hardcoded. For instance,
here is the first part of the data for the first half of player 1:
USHORT chip AVSpriteData[20][56] = {
/* lmanOneLeft */
{0x0000,0x0000,
0x001A,0x000F,
0x00D1,0x002F,
0x0103,0x02FF,
0x04C7,0x033F,
...
The above goes on for a total of 20 sprites. There is also a series of calls to function
SetRGB4
to setup the color palette. After learning a bit about how Amiga sprites work and having understood the
sprite image
data format
above, as well as which hardware sprite uses which range of colors in the palette, I wrote a small
program to output a PNG file (see extsprites.c in the source) from the hardcoded data:
Notice how the ball appears squashed? I think this is due to the non-square pixel size of the original 320x200 resolution.
7. Adaptation des sprites
So the original graphics are basically 32x26. As the SMS has 8x8 pixel sprites, this would require
4 adjacent sprites for each player. The SMS has a limit of 8 sprites per line, so whenever the ball would
fall between the two players, some sprites would disappear...
Luckily, as that the SMS screen resolution is lower (256x192 instead of 320x200) the original sprites
needs to be scaled down. I ended up using 9 SMS sprites (3x3 layout) for players, and 4 SMS sprites for
the ball (2x2 layout). When the ball falls between the players, there are exactly 8 sprites per
line. Perfect, no problem!
Here are my final graphics, after a few tweaks to correct the ratio (got get round balls) and many
touch-ups, as scaling down pixel art automatically was, as it usually is, quite a disaster.
Here is a zoomed-in version with a grid delimiting the SMS 8x8 pixel tiles.
8. Sound effects
There are 3 sound effects in the Amiga game:
- Sound when hitting the ball.
- Sound when scoring.
- Sound when loosing a serve.
I could have used
pcmenc to try to use
the original Amiga sounds (samples) on the SMS, but I must admit that I do not like
the original sounds so much.... (according to what I heard by looking at the
Amiga game on Youtube)
Having grown up with the "inferior" DOS version, I think I have come to expect a shrill whistle-like
sound when scoring, and a lower, error-like sound when loosing one's serve. Hitting the ball
does not make sound in the DOS version. On Amiga there is strange sound that shifts
in pitch according to the speed of the ball, but I'm not a fan of it. Sorry.
Anyway, I just went ahead and made sounds I liked using Deflemask, and used PSGlib to play them
back.
9. My personal touch: Paddle support!
According to the Wikipedia page, this game was inspired by a variant of Pong... Playing with a
Paddle is therefore highly appropriate! I added the necessary support, so this version
of Arcade Volleyball can be played with one or two Sega HPD-200 paddles
(or
homemade equivalent).
10. Conclusion
There many other small changes and things I had to do, such as removing the Quit option from the menu,
adjusting text location, drawing the background, etc, but I won't discuss those in details, it would be too boring.
Overall I think that the game works wells and feels
exactly like I remember. I like the result, and I am very happy of having had the chance of making an
SMS port for a game I remember playing fondly!