Archive

Archive for the ‘Amiga Porting’ Category

Humans

April 18th, 2007 Ant No comments

Humans now implemented. I’d never noticed that the humans in the Blitz game were animated and could move around on their own. The humans in my remake now do the same thing.

One clever thing the Blitz code did was re-use the mountain data to position the humans. If a human is at x co-ordinate 12, for example, we can extract the y position from the mountain data array simply by feeding in the x position (ie. y = mountainData[x]). Add a small number onto that value (so that the humans don’t overlap the mountain line) and they’ll happily march along just below the line defined by the mountains.

Now I need to get the lander/human pickup code working.

(Later)

Human search code for landers written, so landers can now choose a human and go find it. Never noticed that the landers also follow the contours of the mountains, either.

Problems with the code that I need to fix:

  • When lander arrives at human, it jitters back and forth above it;
  • Need to use x and y values multiplied by 10 to emulate fractional increases (screws up the changes I made earlier in using the bitmask wrapping on sprites, but there’s not much I can do about that).

Think I’ve spotted a bug in the original game, too - if you shoot the human a lander is carrying, it doesn’t reset to “searching” status, so it carries on ascending until it turns into a mutant.

Screenshot of the current game:

Defender DS Screenshot - Humans

Categories: Amiga Porting Tags:

Porting the Code – First Steps

April 14th, 2007 Ant No comments

Started work on porting the code across. As previously mentioned, this is rendered more difficult than it should be by the lack of comments or useful variable/function names. Here’s a snippet of code I ported across today:

NEWTYPE.glow
;
c.q[3]
;
a.q
cn.q
;
End NEWTYPE

So what’s that do, then? What’s a.q do? How about cn.q? You have to dig a lot deeper to work out what it all means (I’m commenting the Blitz code as I go along). This is the main bit of code that works in tandem with the snippet above:

hueadd
USEPATH g(g)
;
cn2=\cn
c2=\c[cn2]+\a:\c[cn2]=c2
;
If c2>=15 OR c2<=3
  If \a>0
    \cn=QWrap(cn2-1,0,3)
  Else
    \cn=QWrap(cn2+2,0,3)
  EndIf
  \a=-\a
EndIf
Return

What this code actually does is perform a palette cycle on an individual palette index. The first chunk of code (the “NEWTYPE”) works in basically the same way as a struct in C. It contains an array of 3 colour components (“c.q”, where “c” stands for “colour” and “q” is the variable type “quick”, or “byte”); the elements in the array represent the red, green and blue components of the colour. “a.q” is a single byte to add to the component each time the subroutine is called, so that basically works as the speed of the cycle. “cn.q” stands for “colour number”, or something similar, and refers to the component that we’re working with at the moment.

The subroutine “hueadd” performs the palette cycle. The “USEPATH” command is equivalent to the VB.NET “With” statement - it just saves typing “g(g)” on every line. Anything starting with a backslash, such as “\cn”, refers to a property of the struct we’re dealing with. The code does this:

  • Get the current component;
  • Increase the component by the cycle speed (“a.q”);
  • Check if we’ve exceeded the bounds of the value of this component (work on the assumption that the OCS Amiga has a range of 16 values in each RGB component, and we don’t want to go to a value that’s too dark to see);
  • If so, check if we’re currently increasing or decreasing the component;
  • If we’re increasing, move to the previous component (or wrap around if necessary);
  • If we’re decreasing, move to the component two slots after this one (or wrap);
  • Switch from increasing to decreasing, or vice-versa.

The second to last step in this function (move two component slots after the current) makes no sense. I’m not entirely sure how the palette fades in Defender can work with this in place, because the logic is broken. Stripping this step out does not in any way mar the cycling effect.

The upshot of all of this is that the red component will increase, then the blue component will decrease; then the green component will increase, and the red component will decrease; etc. It produces a palette cycle effect that runs through the majority of the colours that the Amiga can produce.

I’ve ported this code to C++, into another class (“CyclingPalette”), and tidied up the weirdness. I had to work around the fact that the original routine relies on fractional values - to slow the speed of the fade, “a.q” could be set to a value between 0 and 1. I’ve emulated this by multiplying the RGB values inside the class by 10, then dividing by 10 before exposing them outside of the class or processing them. Much faster than trying to use floats.

The second thing I’ve ported over today is the background mountain generation code. The original Blitz code loaded a binary file and used each char as the Y position of a pixel on the mountain range. The mountains are 2048 pixels wide, and the original coders simply created a bitmap that size and drew the mountain to it. I’ve pinched that binary file and included it in my C++ project, and I’ve duplicated their mountain generation code. Currently, the program re-draws the mountain every frame; I’m sure there’s a more optimal way of doing this.

I also duplicated the code for drawing the small representation of the mountains used in the scanner - it does some neat bitshifting to reduce the size of the image by 8 times (the original reduces it by 16 times, but I’ve got an extra screen to play with).

The Blitz bitshifting caused some problems - consider this code:

32 LSL 2+1

The “LSL” command is an m68k assembly instruction meaning “Logical Shift Left”. On first glance, it would seem to translate to C++ like this:

32 << (2 + 1)

However, for some reason the Blitz coders decided that LSL wasn’t going to work like that. What it actually does is this:

(32 << 2) + 1

Bonkers.

Anyway, got a few things done today - palette cycling, the mountain range, the start of the scanner code, and I’ve even got scrolling working.

Categories: Amiga Porting Tags:

BB2 Defender – Observations

April 14th, 2007 Ant No comments

Some observations I’ve made whilst playing the game, in no particular order.

The attract sequence consists of three screens. Screen one is a list of all 6 enemies (they appear on-screen in a reverse explosion animation) and the control options. Screen two shows some simple instructions. Screen three is the high-score table, that uses the enemy explosion animations as fireworks.

Landers (main enemy) locate a human, make their way over to it, pick it up, and ascend to the top of the screen. They fire at the player occasionally. Once at the top of the screen they turn into “mutants”. They don’t seem to choose the closest human, and I don’t think they’re allocated a particular human to pick up when they’re created. They must choose one at random (source code will confirm).

Mutants make their way straight towards the player (jittering a little - must add a random amount to the y pos) and shoot regularly. They try to position themselves above the player (and can move off the top of the screen) in order to avoid being shot.

Bombers (small squares) move in a sine wave (wrapping around at the top/bottom of the screen) dropping small bombs. Bombs stay on screen for a second or two.

Pods appear (not sure) to be given a velocity when they are created, and they then don’t change their movement during the duration of a game. Shooting a pod results in it splitting into swarmers. Not sure of the behaviour of swarmers.

Baiters are UFOs that have erratic flight patterns. They seem to alternate between hovering and darting for the player. They shoot at the player frequently, appear at intervals during the game, and don’t have to be destroyed to finish a level.

Destroying all humans results in the landscape exploding and all remaining enemies becoming mutants.

There’s a starfield behind the mountains - I’d forgotten about that.

The game uses palette rotation all over the place instead of animation. I’ll have to write a palette rotation routine. In fact, it’s probably single palette indexes having their colours changed.

Catching a falling human after destroying the lander trying to carry it away gives a bonus of 500 points, and landing it safely again gives another 500 points. Allowing it to fall gives a bonus of 250 points.

When destroyed, the player’s ship turns white and flashes on and off a few times before exploding.

There can be a maximum of 4 bullets shot by the player’s ship on-screen at once.

Categories: Amiga Porting Tags:

Blitz Basic Sprite Ripping

April 14th, 2007 Ant No comments

That was easy enough. Got the latest version of AmiBlitz installed and spent a while hunting around for documentation on the Internets.  I eventually remembered that I did actually buy Blitz 2 back in the day and have the original manual on my bookshelf. Some time later and I’ve got the sprite ripper code written and the first set of sprites extracted.

“Sprite ripper” is probably a little overdramatic when you consider that all the program does is load the sprites, blit them to a bitmap and save it as an IFF, but hey - it’s all good.

Read more…

Categories: Amiga Porting Tags:

movem.l d0-d7/a0-a6,-(sp)

April 11th, 2007 Ant No comments

(Written 10/04/2007)

Googled for the Defender sourcecode and eventually found it. I’ve got the Amiga Format version of Blitz Basic 2 running in WinUAE in Parallels, but I’ll need to get the latest AmiBlitz working soon. Looking through the Defender code, I’m not sure how much use it’s going to be. Here’s a quick example:

x3=((xp+864) LSR 4)&127+95:y3=Int((y+28) LSR 3) If x3<>hsx OR y3<>hsy Gosub alloff Use BitMap 2 BlitMode EraseMode Blit 30,hsx,hsy BlitMode CookieMode Blit 30,x3,y3 hsx=x3:hsy=y3 Use BitMap db Gosub allon EndIf

Aside from the fact that I can’t remember most of the Blitz-specific commands, we’re dealing with proper retro code here - no comments, meaninglessly short variable names, gosubs instead of functions and magic numbers. All of this junk is interspersed with this kind of thing:

MOVE #$0020,$dff09a

Yep, it’s Motorola 68000 assembly. Good job I bought those books on asm and the 68k CPU, really. Looks like they’ve used floating point (well, emulated floating point) maths, too. I’d rather avoid using floats if possible because they’re so slow, so I’ll probably have to rework the algorithms (if I can work the damn things out).

Started extracting the sound. Apparently sounds need to be in 8-bit signed RAW format. They’re currently in IFF-8SVX format, which is nothing more than 8-bit signed RAW with a header. If I can get hold of the 8SVX spec I can automate stripping the header and I won’t need to worry about converting the samples. Handy!

Windows problems are currently getting in the way of any further progress, though. My virtual Windows disk is now so fragmented that it’s taking forever to do anything at all. All stop whilst Windows defrags. Fortunately I didn’t bother to dual boot and kept Windows safely confined in Parallels, or I’d be stuck waiting for the damn thing to finish churning now. Deleted a load of stuff, too - won’t be needing the XNA framework or C# for a start.

(Later)

Tidied up the destructor for the slideshow - it now runs multiple times without crashing. I wasn’t clearing the vector and one of the array properly. (Though running it too many times still crashes it - there’s something I’ve missed…)

Also made improvements to the menu system. Added a pointer back to the menu set for each menu, so I can store menu-wide variables in there. The menu set now contains pointers to the the button clicked/released sound files, for example. Menus also contain a pointer to their parent (if they are submenus) to enable the back button to work (when I write it). Enabled the clicked sound.

There were two tricky things here. The most difficult was getting the cyclic dependencies sorted out. As the menus contain a pointer to the menu set, and the menu set contains a vector of menus, we have a problem where file A includes file B, which includes file A. Cyclic dependency. The solution is to scrap the problematic include and replace it with a forward definition. So, instead of having this in “menuset.h”:

#include "menu.h"

include <vector>

using namespace std;

class MenuSet { private: vector<Menu*> menus; }

We have this:

#include <vector>

using namespace std;

class Menu;

class MenuSet { private: vector<Menu*> menus; }

In the menuset.cpp file, we need to include both “menuset.h” and “menu.h”.

This only works if we’re dealing with pointers. If we had a vector of menu objects, rather than pointers to menu objects, we’d be stuffed.

The second tricky problem was getting the sounds into the class. This is tricky because you need to pass in a pointer to the size of the sound, not just a pointer to the sound itself. The datatypes are:

const void *sound; const u32 *sound_size;

The functions (defines, actually) that play back sounds expect the sound_size variable to have the name “sound_size”, where “sound” is the name of the sound. Just to make things more complicated, the docs for the PALib suggest that any sounds (with the name “<name>.raw” in the data directory) included in the project (with “#include “<name>.h”, where “name” is the name of the file minus the extension) will be available within the project as “<name>_raw” (where name is the name of the file minus the extension). In fact, they are available as just “<name>” (minus extension, etc).

I’ve also tidied up the menu system’s destructors.

Random thoughts:

  • I wonder if I can write an OctaMED player? I vaguely remember the C sourcecode for the player and a description of the file format shipping with SoundStudio;
  • I wonder if I can make the high score save file adhere to the IFF standard? Just been reading about that, y’see…
Categories: Amiga Porting, MenuDS Tags: