Vectrex Aklabeth (1)

Title screen

Over 4 years ago Pix started VecAkalabeth using the Vectrex32 BASIC cart. He finished the project and VecAkalabeth can be played… although the newest version was not made public yet – since the first versions of this will be part of a Kickstarter reward – so this is not fully available yet (and a bid flickery for my taste).
(Vectrex32 Forum, Pix pages)

40 Months ago another Vectrex Port was started – but apart from some videos and news feeds we never saw the actual thing – and the author went on to other projects.
(Facebook link)

I must admit I never really played Akalabeth – I was a C64 guy not Apple. Akalabeth was before my time as it was conceived in 1979 (or even earlier), at that time I was busy with my Atari 2600.My first “Ultima” to play was Ultima 3 and I loved it. The Vectrex is defenitly missing adventure titels… and for quite some time now I have been thinking of doing a port – without actually starting it – or earnestly looking at the code.

Different BASIC sources can be found online. As far as I know Richard Garriott made Akalabeth free some years ago. This does not mean it is not copyrighted any more, but it means I feel rather confident that making a port of a port of the original game (which is free) will not be persecuted in any way.

During my “porting” I looked at different sources, the original BASIC sources (Apple II), a ported GW BASIC and others. For “my” port I chose to use the language “C” instead of BASIC and as luck has it there was already a “C” port done by Paul Robson. I only had to take his sources and compile it for the Vectrex.
(ehm, no it was not that easy… more on that later)

Anyway during my own adventure a totally new respect for Richard Garriott grew inside of me. Akalabeth may be by todays standards a small game that nobody looks at twice – but in its own way it is a really, really cool software! This game is from 1979! Where computers were no household items. Computers/and programming was absolut “nerd” country. There were virtually no games/adventures/RPG available to the masses. RPG games were played at a table (or by students on a mainframe computer).

And then came Akalabeth!

  • it features a world map (randomly generated)
  • it features dungeons with monsters and loot
  • it features different player characters (Mage and Fighter)
  • it features a “home base” and a “quest giver”
  • it has towns
  • its has dungeons in “3d” graphic and a tile based world map
  • and and and

And all that in not even 400 lines of tightly programmed BASIC.

Absolutely marvelous!!! 

And here I was proud to write some Vectrex programs… with years of experience… and a 17 year old freak in 1979 brought Akalabeth to life.

Absolutely marvelous!!! 

I did it Paul’s way…

(Paul Robson homepage)
Paul Robson did an SDL version of Akalabeth and accidently named it Aklabeth. Nonetheless his port was/is available as source code and since Vide features a gcc C-Compiler I decided I’d rather use “C” than try to get some BASIC translation to the Vectrex.

First thing I did was try to compile his version for my Mac, which more or less worked out of the box – very good! I had a reference where I could check whether things behaved like they should:

Next I built a C project in Vide, an empty one. I replaced the main file with “main.c” from Paul Robson and also copied over the include “aklabeth.h”. I built a Vectrex main loop and commented everything else out.

And “hey” – already I had a working C program displaying nothing!
I identified all SDL primitives and removed them and then commented in the first Aklabeth function: “_MAINIntro()”:

static void _MAINIntro(void)
{
	DRAWText("Aklabeth by P Robson\n\n");
	DRAWText("From the Apple II\n");
	DRAWText("game by R Garriott\n\n");
	DRAWText("Press any key.\n\n");
	HWGetKey();
}

Can’t get any more BASIC than that :-). But since I probably need it anyway sooner or later… I wrote a simple “print” function. The translation to vectrexian than looks like:

static void _MAINIntro(void)
{
	Vec_Text_Width = 0x28;
	while (1)
	{
		CLS;
		Wait_Recal();
		dp_VIA_t1_cnt_lo = TEXT_SCALE;  
		Intensity_a(0x5f);  
		
		print("Aklabeth by Paul Robson." );
		print("From the Apple II");
		print("game by Richard Garriott.");
        	ADDLINE;
		print("Vectrex Port by MALBAN");
		print("(Version 0.9 - BETA)");
		check_buttons();
		if (buttons_pressed()) break;
	}
}

This is more than just straight forward :-). I showed this just as an example of things done.

Little things had to be done… like making sure of variables/sizes like:

  • default size of int and long
  • signed and unsigned
  • converting float/double to int

(as of now, I am still not finished of getting rid of all signed/unsigned warnings…)

Than the next funtions had to be done, like:

  • PLAYERInit()
  • PLAYERCharacter()
  • WORLDCreate()

These were all rather easy, since it was basically throwing numbers around and waiting for user input and printing things on the screen. As helpers I implemented:

  • itoa()
  • ltoa()
  • and several string format functions

For that to work “reliably” and without to much stack reliance, I had to use some RAM space, about 90 bytes. Two strings of 40 bytes for easy conversion and status report, a 4 byte region to convert 8bit numbers and a 6 byte region to convert 16bit numbers.

RAM (interlude)

While we are at it.

  • the world map (21×21) needs 441 bytes
  • the dungeon map needs 121 bytes
  • strings as mentioned above 90 bytes
  • Player data, monster data and some more householding stuff about 100 bytes

The vectrex has about 880 bytes of RAM for user usage, but from that RAM also the STACK must be “fed”.
Calculating the above needed RAM gives you 752 bytes (perhaps even a few more), which would give you a maximum stack size of about 128 bytes.

For “usual” Vectrex programs, this should be more than enough… but MAN, “C” is stack hungry!

In my original first translation – C used more about 250 bytes of stack! The program, because of stack overwrites, crashed all over the place. Examining the generated “C” -> assembler code looked terrible stack usage wise – but I did not find a better compiler setting – so somehow I had to live with it.

Two tricks I use now:

  1. I use memory regions twice. This is possible, since everything in Akalabeth is calculated by the “lucky number”. Thus when a dungeon is entered, the “world” is erased and the RAM reused for the dungeon.
  2. 50 bytes of the “string” buffers mentioned above are only used as buffers in between, and only when no heavy duty stack usage is going on. I allow that those string buffers can be overwritten by the stack (in a controlled manor)

Thus I have (virtually) about 170 bytes more RAM, and there is no corrupt STACK anymore.
(the 0.9 version probably doesn’t use that much stack anymore though)

// for Vectrex double usage of memory
unsigned char memBlock[sizeof(WORLDMAP)];

WORLDMAP   *World = (WORLDMAP *) memBlock;     // Current world map
DUNGEONMAP *Dungeon = (DUNGEONMAP *) memBlock; // Current dungeon map

Overworld

Next on “the list” was the overworld map. Here I made a nearly fatal mistake. Paul Robson has in its code a “super modus”, where he uses a different setup for Akalabeth. One of these is to use a 7×7 overworld map (instead of the original 3×3). I liked the7x7 and I wanted that too!

Thing is … printing status information, messages from the game and up to 49 tiles from the world map is a challenge to do in assembler. In translated pure “C” it is very, VERY hard.
Additionally the “mountain” tile (which I do not like…) has many vectors and some additional moves in between), and the original code prints mountains for each “out of bounds” tile.

Anyway… more on that later.

For now, this is what a “tile” looks like in Pauls sources (and similar in the original BASIC source):

	case WT_MOUNTAIN: /* Mountain the cracked effect */
		HWColour(COL_MOUNTAIN);
		HWLine(X(2),Y(6),X(2),Y(10));
		HWLine(X(0),Y(8),X(2),Y(8));
		HWLine(X(2),Y(6),X(4),Y(6));
		HWLine(X(4),Y(6),X(4),Y(4));
		HWLine(X(2),Y(2),X(4),Y(4));
		HWLine(X(2),Y(2),X(2),Y(0));
		HWLine(X(2),Y(2),X(0),Y(2));
		HWLine(X(8),Y(4),X(4),Y(4));
		HWLine(X(8),Y(4),X(8),Y(0));
		HWLine(X(8),Y(2),X(10),Y(2));
		HWLine(X(6),Y(4),X(6),Y(8));
		HWLine(X(10),Y(8),X(6),Y(8));
		HWLine(X(8),Y(8),X(8),Y(10));
		break;

I am sure you all know this… but with Vide->Vecci converting those lists to something Vectrex like is a thing of a few seconds:

Since you can import “text” with parser settings. I set the parser to something like Draw_VLc (with a count to start), and told it to use absolut coordinates… and voila one button press – and Vecci imported the data.

Another few button presses (rotate, center, Isidro all) and export to “C” results in:

#define BLOW_UP 1

const signed char VectorList[]=
{	(signed char) 0xFF, +0x00*BLOW_UP, +0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, -0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x02*BLOW_UP, +0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x00*BLOW_UP, -0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x04*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, -0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x00*BLOW_UP, -0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, +0x02*BLOW_UP, +0x08*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, -0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, +0x00*BLOW_UP, +0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x00*BLOW_UP, -0x04*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, -0x04*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, +0x00*BLOW_UP, -0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x00*BLOW_UP, +0x04*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, -0x04*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0x00, +0x02*BLOW_UP, +0x00*BLOW_UP,  // pattern, y, x
	(signed char) 0xFF, +0x00*BLOW_UP, +0x02*BLOW_UP,  // pattern, y, x
	(signed char) 0x01 // endmarker (high bit in pattern not set)
};

To be continued…

Tagged on:

2 thoughts on “Vectrex Aklabeth (1)

  1. Phillip Eaton

    A lovely insight into what it takes to do a “simple” conversion of a program from one platform to another, thanks for the write-up and I look forward to the continuation. I’m doing something similar in the background, a conversion of a title that started off life as some Z80 assembler from the mid 70s, before magnetic distribution, in fact before computers themselves, were generally available. I thought it would be relatively easy, but the parts that I can actually reuse without modification just get smaller and smaller. It’ll eventually end up mostly a complete rewrite. It’s story is also twisty and turny and it’ll take a long time, but it will get finished. And it’s being built using Forth and assembler, so no compiler warnings to worry about, because with Forth, the program is the compiler!

  2. Peer

    Thanks for the interesting write-up. I still would like to learn more about the C stack issue. So far, I had not encountered such a thing myself. I would like to do a closer investigation of why gcc6809 does this.

    Cheers,
    Peer

Leave a Reply to Peer Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.