Jump to content
IGNORED

Alex Kidd Port


TheMole

Recommended Posts

Hi All,

 

I've found some more time to work on my Alex Kidd port. I manually converted one of the original games' maps to a format that is compatible with my scrolling routines. Naturally, I lost quite a bit of detail, but the end-result isn't too bad. This is what the original looks like:

AlexKiddInMiracleWorld-SMS-TheIslandofSt

And this is what the same map looks like on the TI:

miracle_island_1.png

 

I had to move some blocks around, or remove some blocks to avoid color clash(can't have two different colored blocks directly next to each other), but all-in-all, most of the details are there. I also originally did a very nice conversion of the cave the old man is sitting in at the end, but I ran out of patterns and had to ditch both that and the clouds. I'm thinking of adding it in a static screen that gets tagged onto the end of the map, but those are details for later. Either way, I'm only using some of the original game maps to test playability and will probably design my own at some stage (so it'd be a sequel/prequel instead of a direct port), so I'm not sure how important those issues are.

 

Next up, I started working on real collision detection and fine-tuning the physics to be a bit more in line with the original game. Seems to be working well, although I still need to add a collision check for the top of the sprite (you could jump into one of the blocks now and get stuck if you really try).

 

Next up: finish the collision detection routines, find a way to make Alex face the other direction and implement punching. Also, add the jumping and punching sound effects. After that, I'll need to figure out how to make the blue blocks and the gold boxes destructable in the scrolling data... punching and destroying blocks is kinda the main mechanic of the game :). It should be doable, but it will be quite convoluted as I have to decide which patterns to put in place depending on the object on the other side of the block.

 

Finally, here's a video recorded in MESS. Normal caveats apply: youtube makes it a bit more choppy than the real thing, and of course the colors are a bit more washed out here.

 

*edit* The latest work-in-progress version is attached to this post for those that are interested in trying it out.

Latest version is from august 24th, 11pm CET.

alexkidd.dsk

Edited by TheMole
  • Like 21
Link to comment
Share on other sites

After that, I'll need to figure out how to make the blue blocks and the gold boxes destructable in the scrolling data... punching and destroying blocks is kinda the main mechanic of the game :). It should be doable, but it will be quite convoluted as I have to decide which patterns to put in place depending on the object on the other side of the block.

 

I was facing the same problem for Scramble in order to explode and remove ground objects. As you probably know I ended up using a combination of sprites and characters. I wanted to use characters as much as possible to limit sprite flicker, but I was forced to use some sprites because I ran out of characters. It would have been much easier just to use sprites provided you could accept the flicker.

 

Anyway, what I did was to make a lookup table for each character transition, for instance "from space to left side rocket", "from left side rocket to right side rocket", or "from right side rocket to wall". For each transition you could look up a data structure that contained the number of the replacement transitions you needed for different actions.

 

One action was to remove an object. The 'remove' entry for "from space to left side rocket" pointed to the transition for "from space to space" (rocket replaced by space), the one for "from right side rocket to wall" pointed to "from space to wall" and so on. I also had an action to explode an object. The 'explosion' entry for "from right side rocket to wall" pointed to "from explosion frame 1 to wall", which in turn would pointed to "from explosion frame 2 to wall" to animate the explosion. The last explosion entry pointed back to the first to form a loop. You could take the loop a number of times and then follow the 'remove' pointer to finally remove the object.

 

The table was a great performance benefit compared to large number of 'if statements' you would otherwise have to write. If an entry in the table was null I knew the required transition character didn't exist and a had to use a sprite.

 

If you do something like that I suggest to write a program to generate the tables for you, because a spent a lot of time setting up the table manually for each level.

  • Like 1
Link to comment
Share on other sites

 

I was facing the same problem for Scramble in order to explode and remove ground objects. As you probably know I ended up using a combination of sprites and characters. I wanted to use characters as much as possible to limit sprite flicker, but I was forced to use some sprites because I ran out of characters. It would have been much easier just to use sprites provided you could accept the flicker.

 

Anyway, what I did was to make a lookup table for each character transition, for instance "from space to left side rocket", "from left side rocket to right side rocket", or "from right side rocket to wall". For each transition you could look up a data structure that contained the number of the replacement transitions you needed for different actions.

 

One action was to remove an object. The 'remove' entry for "from space to left side rocket" pointed to the transition for "from space to space" (rocket replaced by space), the one for "from right side rocket to wall" pointed to "from space to wall" and so on. I also had an action to explode an object. The 'explosion' entry for "from right side rocket to wall" pointed to "from explosion frame 1 to wall", which in turn would pointed to "from explosion frame 2 to wall" to animate the explosion. The last explosion entry pointed back to the first to form a loop. You could take the loop a number of times and then follow the 'remove' pointer to finally remove the object.

 

The table was a great performance benefit compared to large number of 'if statements' you would otherwise have to write. If an entry in the table was null I knew the required transition character didn't exist and a had to use a sprite.

 

If you do something like that I suggest to write a program to generate the tables for you, because a spent a lot of time setting up the table manually for each level.

 

Good point, I forgot you must've had to face the same problem with scramble. My goal is to have the block disappear (replace the map data) and do the animation with sprites. The replacement pointer is a good idea though, I'll probably use that.

Link to comment
Share on other sites

  • 2 weeks later...

Small update:

  • Somewhat improved collision detection (all the collision points are there, but the location of them isn't perfect yet)
  • Alex can now turn left and right (I simply swap the sprite pattern table pointer to a mirrored version when he changes direction)
  • Slightly tweaked physics, Alex can now no longer reverse direction in mid-air on a normal jump
  • Added punching animation and the ability to punch the blue blocks in the level. Still some visual glitches here due to rounding errors when calculating the part of the screen to refresh (you can see it in the video at the 30 second mark)

Next I plan on cleaning up the codebase a bit, fixing the smaller bugs. Then I'll release a disk image for who's interest in playing with it. After that, I'll have to refactor everything to work with disk files instead of storing all the graphics in one big binary. Currently the game fills nearly the entire 32k, but almost half of that are patterns that can easily be loaded into VRAM and then discarded from memory. I'll also need to optimize a bit here and there as I'm nearly at the limit of what I can do at 60fps and I still need to add enemies.

 

Small video showing the new features:

Edited by TheMole
  • Like 6
Link to comment
Share on other sites

  • 2 months later...

So... finally found some more time to work on this. A bunch of stuff has been added, and I'd almost call it "a game" now (except for there being no win condition yet).

 

New stuff:

  • I now load most of the graphics and music from files on disk. I had to do this because I was running out of program space, and this took a shitload of time since I had to figure out how to do file loading on the TI for the very first time, and then how to fit all of it in memory where gcc likes it.
  • Added enemies. Unfortunately, I ran out of sprite pattern definition slots and need to use a placeholder for two out of three enemies (the red balls are supposed to be some sort of plant, and the black ball is supposed to be a kind of bird). I'm not sure how I can resolve this except for maybe sacrificing some animation frames or color for Alex himself, but I'd rather not do that as I kinda like how it makes the game look like it's running on a more high-end system with multi-color sprites.
  • Alex can now die, so no more running across lava or through enemies for him! Also, a sad song plays when he dies...
  • Improved Alex-World collision detection, I'm very happy with how this turned out. Although you can still see small glitches (Alex moving into a block for one or two pixels), you never get stuck and it never looks too horrible.
  • Added jumping and punching sounds, still need to look for a "destroyed enemy" sound
  • Sprite flickering routine.

Still todo:

  • Make the yellow blocks punchable as well. I would need one more sprite definition for the most straightforward way of implementing this, but as I said before I'm out of space for those. I'll probably have to integrate that in the map definition instead, which is going to be a bit more work but should have the added benefit of not introducing even more sprite flickering then I have to suffer already.
  • Add a win condition
  • F18A support ECM3 and hardware scrolling support

Here's a short video showcasing the new features recorded in js99er:

 

Now, I haven't been able to run this on the real deal since my hardware is unfortunately rotting away in my damp basement until I get my house in order (friggin' renovations seem to take forever...), and I have seen some glitches in MESS and some stuttering in Classic99, so I would very much appreciate it if one of you could test it on real hardware and perhaps make a video so I can gauge how smooth it runs on that? For those that want to give it a spin, there's a disk image attached. EA#5, DSK1.ALEXKIDD (needs 32K memory expansion and it the file paths are hardcoded to run from DSK1 for now, sorry about that).

 

*edit* thanks to Rasmus for trying it out and unfortunately reporting that it does not work on real iron. I've removed the download until I've found a fix for this.

Edited by TheMole
  • Like 7
Link to comment
Share on other sites

Now, I haven't been able to run this on the real deal since my hardware is unfortunately rotting away in my damp basement until I get my house in order (friggin' renovations seem to take forever...), and I have seen some glitches in MESS and some stuttering in Classic99, so I would very much appreciate it if one of you could test it on real hardware and perhaps make a video so I can gauge how smooth it runs on that? For those that want to give it a spin, there's a disk image attached. EA#5, DSK1.ALEXKIDD (needs 32K memory expansion and it the file paths are hardcoded to run from DSK1 for now, sorry about that).

 

It's looking great on emulation, but unfortunately you disk code doesn't seem to work on the hardware. I have tried both with my nanoPEB and a real floppy disk. After loading the initial E/A 5 file it stops with a green screen. You can reproduce this on MESS if you set it up to use a standard TI DSSD Floppy controller.

 

I had the same problems with Titanium. You have take care not to overwrite the disk buffers at the top of VDP RAM. You also have to leave various address in PAD intact. What I did was to make a copy of the entire 256 bytes at program start up that I swapped back before any disk operations.

Link to comment
Share on other sites

In Classic99, if you enable "corrupt DSK RAM", it will overwrite the most common memory addresses used by disk after a DSR call with a fixed pattern so you can more easily tell if you were using it for something else. This was added to help make it easier to write DSR code that runs on a real TI Disk controller. For harder cases, you can enable real TI Disk Controller support in the Classic99.ini by first configuring a disk as a floppy image (must be an image file, not a folder) and then editing Classic99.ini so that the disk "Type" is "3" (it should be '2' when you open the file). The debug log will confirm that it's working. This will only work on DSK1 right now (maybe 2 and 3 but I wouldn't count on it - this is why it's not in the GUI yet ;) ).

 

Assuming you're using my debugger, anyway.

Link to comment
Share on other sites

In Classic99, if you enable "corrupt DSK RAM", it will overwrite the most common memory addresses used by disk after a DSR call with a fixed pattern so you can more easily tell if you were using it for something else. This was added to help make it easier to write DSR code that runs on a real TI Disk controller. For harder cases, you can enable real TI Disk Controller support in the Classic99.ini by first configuring a disk as a floppy image (must be an image file, not a folder) and then editing Classic99.ini so that the disk "Type" is "3" (it should be '2' when you open the file). The debug log will confirm that it's working. This will only work on DSK1 right now (maybe 2 and 3 but I wouldn't count on it - this is why it's not in the GUI yet ;) ).

 

Assuming you're using my debugger, anyway.

 

Thanks for the tip, Tursi. Unfortunately, I can only use Classic99 once in a while to test something since I've moved to Mac (I need to borrow my gf's PC to test on Windows). It's a shame too, 'cause although MESS is very powerful, nothing beats Classic99's debugger in terms of ease of use and features.

Link to comment
Share on other sites

Anyone know the exact VDP and scratchpad locations to mind? I found an article in MG's smart programmer that shows anything below 0x37d8 should be fair game (and 0x3be4 if you do the equivalent of a "call files(1)"), but it still borks even if I avoid that area completely.

Link to comment
Share on other sites

Anyone know the exact VDP and scratchpad locations to mind? I found an article in MG's smart programmer that shows anything below 0x37d8 should be fair game (and 0x3be4 if you do the equivalent of a "call files(1)"), but it still borks even if I avoid that area completely.

(0x37cf if using a compact flash.)

 

Since the code appears to work with the HFDC, I would verify x83E0-83FF (GPLWS) is untouched and in particular, R13/R14/R15 (0x83FA,83FC,83FE). 0x8370 should be preserved as well. Also be sure your own program isn't using 83E0 as its workspace.

 

Both the TI and Myarc cards use VDP for PABs and data passing; however, the Myarc cards (and IDE, SCSI, Horizon) use on-card RAM to buffer all or most of what is normally in the reserved VDP space. CorComp and nanopeb/cf7 devices also use the reserved VDP space.

Link to comment
Share on other sites

(0x37cf if using a compact flash.)

 

Since the code appears to work with the HFDC, I would verify x83E0-83FF (GPLWS) is untouched and in particular, R13/R14/R15 (0x83FA,83FC,83FE). 0x8370 should be preserved as well. Also be sure your own program isn't using 83E0 as its workspace.

 

Both the TI and Myarc cards use VDP for PABs and data passing; however, the Myarc cards (and IDE, SCSI, Horizon) use on-card RAM to buffer all or most of what is normally in the reserved VDP space. CorComp and nanopeb/cf7 devices also use the reserved VDP space.

 

I touch 0x83FA (R13) all the time. Couldn't survive without it. :)

 

Gazoo

Link to comment
Share on other sites

Are there limitations as to where the PAB can be put in VRAM? I've managed to have it load one file (the song) using the TI controller when putting the PAB at 0x2400, but it doesn't work at 0x3400... which seems very odd to me.

 

I'm still struggling with loading stuff directly to the right location in VRAM as well, but that I can probably figure out once I understand the limitations a bit better. It's still going to be a struggle to get things cleanly in memory without using too many RAM buffers. My VRAM layout is pretty specific and fairly packed.

0x0000-0x0400	Frame0 @ 0x0000
0x0400-0x0800	SIT1   @ 0x0400
0x0800-0x0C00	Frame1 @ 0x0800
0x0C00-0x1000	SIT2   @ 0x0C00
0x1000-0x1400	Frame2 @ 0x1000
0x1400-0x1800	SDT_R  @ 0x1400
0x1800-0x1C00	Frame3 @ 0x1800
0x1C00-0x2000	SDT_L  @ 0x1C00
0x2000-0x2400	Frame4 @ 0x2000
0x2400-0x2800	PAB    @ 0x2400
0x2800-0x2C00	Frame5 @ 0x2800
0x2C00-0x3000
0x3000-0x3400	Frame6 @ 0x3000
0x3400-0x3800	
0x3800-0x3C00	Frame7 @ 0x3800
0x3C00-0x3FFF	SAL    @ 0x3F00
		CT     @ 0x3FC0


FrameN  = Pattern definitions for frame n of smooth scrolling defs (1k each)
SIT1/2  = Screen Image Table, nametable for front and backbuffers (756 bytes each)
SDT_L/R = Sprite pattern definitions for Alex is facing left/right respectively (1k each)
SAL     = Sprite attribute table (128 bytes)
CT      = Color table (16 bytes)

Note that I need file loading for two things: to load the music file in RAM (I do this first) and to load the scrolling pattern definitions into VRAM (Frame1-8 in the memory map above). This is all done at the start of a level, so once everything is in place I don't have to take the VDP disk buffers into account anymore. But, I do need an efficient way of getting this stuff into VRAM in the first place.

 

Worst case scenario, I could:

  • Load all patterns, one by one into normal RAM via the song buffer and move them to the lower 8k of VRAM.
  • Load the song into RAM
  • Move the pattern definitions to their correct locations in VRAM a couple of bytes at a time

But that seems awfully wasteful.

Edited by TheMole
Link to comment
Share on other sites

You may already know the following and are trying to solve another problem. I apologize in advance if that is so:

 

You should be able to load directly from file to your desired VRAM location as long as your file record length is set up correctly. It seems you are looking to load 1KB blocks for the frames. If you use DF128 files, you can load 8 records at a time to any VRAM location you choose. You can also use direct sector access to load 4 sectors at a time.

 

The one thing that puzzles me is what you mean by "PAB". If you are including the VRAM buffer in the term and implementation and you have not changed the default of 3 simultaneous files, putting your "PAB" at >3400 will step on the system disk buffers at the top of VRAM. I usually think of a PAB as including only the file's I/O parameters, which includes the file's pathname, e.g., DSK1.FILENAME. The PAB, then, would be only 24 bytes and should work just fine at >3400.

 

You can also load any size "PROGRAM" file to load into a spot in VRAM using the DSR's LOAD opcode. You just need a separate file for each load. If everything you want to load is in one long, contiguous section of VRAM, you would only need one "PROGRAM" file.

 

...lee

Link to comment
Share on other sites

The one thing that puzzles me is what you mean by "PAB". If you are including the VRAM buffer in the term and implementation and you have not changed the default of 3 simultaneous files, putting your "PAB" at >3400 will step on the system disk buffers at the top of VRAM. I usually think of a PAB as including only the file's I/O parameters, which includes the file's pathname, e.g., DSK1.FILENAME. The PAB, then, would be only 24 bytes and should work just fine at >3400.

 

You can also load any size "PROGRAM" file to load into a spot in VRAM using the DSR's LOAD opcode. You just need a separate file for each load. If everything you want to load is in one long, contiguous section of VRAM, you would only need one "PROGRAM" file.

 

...lee

 

Thanks for the feedback Lee.

 

I use PAB in the same way as you, basically as a structure holding the I/O parameters. It works if I put it at 0x2400, not if I put it at 0x3400. However, I am not sure what you mean by VRAM buffer, unless you're talking about the "top of VRAM" section reserved for disk access, but I don't think you can move that around, right?

 

I'm loading 1k blocks from disk as PROGRAM files, or at least that's what I'm trying to do. I'm going to have to take a close look at the code again to see if I'm not confusing some of my pointers to the different locations in VRAM.

Link to comment
Share on other sites

However, I am not sure what you mean by VRAM buffer, unless you're talking about the "top of VRAM" section reserved for disk access, but I don't think you can move that around, right?

 

Nevermind, just figured out that you mean the VDP address that is part of the PAB structure.

Link to comment
Share on other sites

..., but I don't think you can move that around, right?

 

You can, but you really need to know what you are doing. It's not for the faint of heart. I believe Harry does it with his XB compiler.

 

 

I'm loading 1k blocks from disk as PROGRAM files, or at least that's what I'm trying to do. I'm going to have to take a close look at the code again to see if I'm not confusing some of my pointers to the different locations in VRAM.

 

Post some code if you don't mind my looking over your shoulder.

 

...lee

  • Like 1
Link to comment
Share on other sites

Post some code if you don't mind my looking over your shoulder.

 

Thanks for the offer. These are the relevant functions from the file handling code I've cobbled together (it is heavily inspired by code from Lucien2's Nyogsothep game, so credit should go to him):

#define PAB_NAMELENGTH_ADDR	(*(volatile int*)0x8356)
#define PAB_VDP_LOCATION	0x2400
#define DEFAULT_BUFFER_LOC	0x2C00

// File operations
#define	OPEN 				0
#define CLOSE 				1
#define READ 				2
#define WRITE 				3
#define LOAD_VDP			5
#define SAVE_VDP			6

int dsr_link(int op) 
{
	volatile int stat;
	__asm__
	(
		"mov 	%1,r1	\n\t"
		"blwp	@dsrlnk \n\t"
		"stst 	%0"
		:"=r"(stat)
		:"r"(op)
		:"r0","r1"
	);
	
	return stat;
}

int file_operation(pab* p, int op) 
{
	int EOF = 0, stat, err;

	p->opcode	= op;
	p->flags	= p->flags & 0x1F;

	vdpmemcpy(PAB_VDP_LOCATION, (unsigned char*)p, sizeof(pab));
	PAB_NAMELENGTH_ADDR = PAB_VDP_LOCATION + 0x09;
	
	stat = dsr_link(;
	vdpmemread(PAB_VDP_LOCATION, (unsigned char*)p, sizeof(pab));

	err = p->flags >> 5;
	if (err == 5) 
	{
		EOF = 1;
		err = 0;
	}
	else 
	{
		if (err) 
			err++;
		else 
			if (stat & 0x2000) 
				err = 1;
	}
	
	if (err && p->opcode != 1)
		close(p);

	return err;	
}

int load_to_vdp(pab* p, const char* filename, int vdp_buffer_location, int n) 
{
	p->record_number	= n;
	p->buffer_address	= vdp_buffer_location;
	strcpy(p->name, filename);
	p->name_length		= strlen(p->name);

	return file_operation(p, LOAD_VDP);
}

int load(pab* p, const char* filename, char* buf, int n) 
{
	int ret = load_to_vdp(p, filename, DEFAULT_BUFFER_LOC, n);
	vdpmemread(DEFAULT_BUFFER_LOC, (unsigned char*)buf, n);

	return ret;
}

Then, for loading I simply use:

pab 	fd;
load(&fd, "DSK1.SONGSFX1", (char*)leveltheme, 2624);

which loads the song to CPU RAM and actually works.

 

The following however:

pab 	fd;
load_to_vdp(&fd, "DSK1.PATT1_0", 0x0000, 1024);

is supposed to load 1 frame of pattern definitions to >0000 in VRAM, but doesn't work... As you can see from the code above, the load function simply uses the load_to_vdp function with some defaults, so I'm stumped as the why one would work and the other wouldn't.

 

Both files are in PROGRAM format, by the way.

Link to comment
Share on other sites

I didn't realize you were coding it in C—nice!

 

I don't understand the __asm__ code, so I'll have to take it on faith that it emits the correct ALC. The fact that load() works certainly implies that it does.

 

The only thing that comes to mind is that the VRAM address of 0x0000, which constitutes a null pointer (I think), perhaps causes a problem. Does it also fail at a non-zero VRAM address?

 

...lee

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...