Legend of Zelda (RPG game)

Use this forum to share and discuss Uzebox games and demos.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Legend of Zelda (RPG game)

Post by nicksen782 »

I figured out how to do SD card writing!

So many articles on the web for PIC micros and Arduino. I finally went back to something that worked. Kinda. I never actually used the write feature of Petitfatfs before but it has been said to work. Looking in the mmc.c code for Petitfatfs I saw what I believed could be made into a FAT agnostic working example, assuming you already have the starting sector for your file and you want to replace the first 512 bytes of it. I have no provisions here for moving to different areas of the file. Perhaps you could use sdCardSkipBytes() for that after figuring out how many sectors in your file you will need to go. That is how I do it with SimpleSD now.

I pulled from these parts, all fortunately in the mmc.c file:
  • disk_writep()
  • send_cmd()
  • rcv_spi()
  • xmit_spi()
  • #define CMD24 (0x40+24) /* WRITE_BLOCK */
  • #define SELECT() PORTD &= ~(1<<6) /* CS=low */
  • #define DESELECT() PORTD |= (1<<6) /* CS=high */
I removed stuff that wasn't related to writing and reduced disk_writep to only be able to write 512 bytes. I hard-code wc. I should be writing an entire sector anyway so that doesn't seem like a problem to me.

Of course, without sdCardFindFileFirstSector() from SimpleSD I wouldn't be able to use these write functions. I've tested this with a few different strings on the emulator (Emscripten CUzeBox) and some real Uzebox hardware. Seems to work as expected.

NOTE: I put the code together from existing code within PFF. This is a minimal example and all that I think I will need.

Code: Select all

// Portions of code here part of PFF, (C)ChaN, 2010

#define CMD24		(0x40+24)			/* WRITE_BLOCK */
#define SELECT()	PORTD &= ~(1<<6)	/* CS=low */
#define	DESELECT()	PORTD |=  (1<<6)	/* CS=high */

/* Returns received data */
unsigned char rcv_spi (void){
	SPDR = 0xff;
	loop_until_bit_is_set(SPSR, SPIF);

	return SPDR;
}

/* Exchange a byte */
/* Returns received data */
/* Data to be sent */
void xmit_spi ( unsigned char dat ) {
	SPDR = dat;

	loop_until_bit_is_set(SPSR, SPIF);
}

u8 SD_Command( u8 cmd, unsigned long arg ){
	unsigned char n, res;

	/* Select the card */
	DESELECT();
	rcv_spi();
	SELECT();
	rcv_spi();

	/* Send a command packet */
	xmit_spi(cmd);							/* Start + Command index */
	xmit_spi((unsigned char)(arg >> 24));	/* Argument[31..24] */
	xmit_spi((unsigned char)(arg >> 16));	/* Argument[23..16] */
	xmit_spi((unsigned char)(arg >> 8));	/* Argument[15..8] */
	xmit_spi((unsigned char)arg);			/* Argument[7..0] */
	n = 0x01;								/* Dummy CRC + Stop */
	xmit_spi(n);

	/* Wait for a valid response in timeout of 10 attempts */
	n = 10;
	/* Receive a command response */
	do {
		res = rcv_spi();
	} while ((res & 0x80) && --n);

	/* Return with the response value */
	return res;
}

u8 SD_WriteSector( unsigned long SectorNumber, u8 *buff ){
	u8 i;
	unsigned int wc;
	unsigned int count;

	/* send block-write command... */
	for(u8 i=0; i<8; i++){xmit_spi( 0xFF );}

	SD_Command( CMD24, SectorNumber << 9);

	xmit_spi(0xFF); xmit_spi(0xFE);		/* Data block header */
	wc = 512;
	while(wc--){ xmit_spi(*buff++); }

	// Deselect the card.
	DESELECT();
}
Example of how to use the above:

Code: Select all

u8 testar_index=0;
u8 testar[] = { "The world is your oyster...\n" };

for(unsigned int i=0; i<8 * 64; i++){
	if(testar_index==sizeof(testar)){testar_index=0;}
	ram_tiles[i]=testar[testar_index];
	testar_index++;
}

for(u8 t=0; t<1; t++){ SD_WriteSector(sectorStart, &ram_tiles); }
... Then put it into your computer and open the .bin with a hex editor. The string should be there.
User avatar
D3thAdd3r
Posts: 3293
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

Cool good job figuring this out. I will have to remember to go back and copy what you did here when I get around to requiring the non-PFF writing.
User avatar
D3thAdd3r
Posts: 3293
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

Per our previous discussion on the limited flash space, I am just going to drop some ideas that might be worth consideration. That SD->MIDI streaming will help big time, but the works needs to be done to make it real. Not sure if you are using a custom wave file, but that can save maybe 1.5k by eliminating unused waveforms. You already experimented and found some compiler flags that saved space which is a big one.

Here is in my opinion, the absolute best idea I can think of. I have said this one in various places before, and I know the arguments against it, but I would say it warrants the hour of research before dismissing it. I think you will be convinced after that hour, as I was when I did it. I don't even remember the number I determined, because it didn't matter.

You have a rom that has the full title screen(use all the flash tiles you want), all the code to register new players, copy, delete, choose a slot, all that. You have another rom that has all the overworld tiles/sprites, and the code required to play on the overworld and save progress. You have a 3rd rom for the underworld tiles/sprites and all the code for that and the ability to save. When you register a new player or load a save slot on the first rom, it prompts you to hold a button sequence down and it automatically resets itself(and brings up the bootloader no matter what, since you are holding buttons). The rom told you before that, to load for example, "ZELDA_OW.UZE" when the bootloader appears. You do that, it flashes, and you are playing on the overworld. If you quit and come back in a week, the overworld is already flashed and the character slot you chose before is still active, and you would need to manually load say "ZELDA_LD.UZE" rom to choose another slot(then of course reload the OW rom to play). Most of the time you would keep playing the same one you have been on. You would always be able to resume days later a dungeon you are stuck on(assuming that was the last thing flashed) without another flash. Every rom has code to tell you what the next rom you need to load is, and it stores that on the SD card. When you load a rom, it first checks to make sure you loaded the right one. Otherwise it tells you to load the one originally requested until you do, and you continue on.

When you walk down into a dungeon door, it prompts you to hold a button sequence and choose "ZELDA_UW.UZE". Then you play through the dungeon, die 10 times without any reloading, then when you beat it, grab triforce and leave, you do a reset+flash to go back to the Overworld. I don't think this takes too long for a user to get used to, and it is just a few seconds. I got this idea from the original release of Zelda, which was on the Famicom Disk System before they had 128K rom cartridges available:

As you can see, people used to physically flip a disk to play Zelda. Resetting and choosing a rom is much faster and easier than that. Zelda on FDS was a revolutionary game, despite this small thing.

One thing that might bring skepticism is how many flashes this costs the '644. I have never heard of anyone, even people who are developing on hardware all the time, wearing out a '644. It should be at least 10,000 writes, and I bet most people have used maybe 500 if they were a heavy Uzebox user for years. I did an experiment on it a while back, where I just played Zelda like I normally would for an hour, and counted how many times would require flashing in the proposed solution. It was ridiculously small compared to 10,000. I don't believe anyone would ever wear out an Uzebox in 10 years if they played Zelda often. I think there is the initial shock factor for how unorthodox this is, but when taken logically and doing the experiment, I think the math shows this is no issue at all. The minor inconvenience for rom changing is small compared to having a full Zelda port in my opinion. The last downside is that you need to maintain 3 roms, with some code that is the same between them...I don't think this is too hard and you have a lot more freedom. I think there are 3 very small down sides, versus a huge upside for you here. My 2 cents, please try the experiment and see what you think on it.

Edit- I meant 10,000 writes instead of 100k. You could record the number of flashes to SD to show the user how many times they flashed. If they flashed 1000 times, that means they put up with 5000 seconds of load time...unlikely and only a tenth. Eh, at least I can think of no other optimizations that get you back 64k ;)
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Legend of Zelda (RPG game)

Post by nicksen782 »

I might consider that for dungeon 9. It would have the different music for the dungeon, the full credits music too and as many bad guys as I can fit into flash. Perhaps the main menu data select screen could indicate which rom the file is expected to use and will tell you to load that rom first. Still, as great an idea as it sounds, I would like to avoid rom swapping unless it can be made really easy such as telling the bootloader to at least auto select (not auto flash) a certain rom. I would need to juggle multiple copies of the game (and a bug fix to one would need to go to the others) so I don't want to do that until I am 100% certain that the general game mechanics are solid and will not change. By dungeon 9 I would hope they are. :)

... anyway. Flash IS at a premium and flash savings are on my mind. It is well known that the bulk of your game's flash will be music and graphics so I've been on a hunt to reduce their flash usage as much as possible while still having the game look/sound decent.

UPDATE:
Overworld: 106 tiles
Underworld: 122 tiles
Shared & Sprites: 165 tiles. (Items, Link, Old Man/Woman, HUD tiles)
Overworld Sprites: 48 tiles (Moblin and Octorock)
Underworld Sprites: 17 tiles (Stalfos and Keese)
Current Total: 348 tiles (22272 bytes)

I went through and cut/replaced lots of tiles for the Overworld. I went from 157 to 106 by removing most of the green blocks and replacing with green trees and also some tile reductions for water. It still looks decent. I reduced the dungeon screen walls with simpler graphics. Still looks good and saved me about 16 tiles. Plus, it added a little color to my rather bland dungeon colors.

Also, the game mechanic for taking damage from enemies is in place and health is represented correctly with hearts. There is a player game over screen now (very similar to the original.)

I'm now at 55216 bytes used (84.3% full). I think I've gained enough room to finish the rest of the main game. If streaming music could be figured out then that would be super awesome.

PS -
The link: www.nicksen782.net/r_uzezelda.php will now let you play different versions. I've had to juggle feature-sets because of flash usage. At least this way you can see past progress. And, as always, the most up to date version is available for play there too.

I'm also not against Mode 13 or Mode 64. I still have to complete the game mechanics at this point so a change in video mode can wait until later.
User avatar
D3thAdd3r
Posts: 3293
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

If you have it at 55K without any compromising cuts, you might be well off then. The SD music streaming should help keep it there.

On the multiple roms idea, I guess it wouldn't be as bad as I initially thought. You can have a makefile that generates multiple roms with different flags, and then just use "#if" to coordinate. I'll look into those MIDIs more. Best is what you are doing, keep developing and see where and what becomes necessary.
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Legend of Zelda (RPG game)

Post by nicksen782 »

The purpose of ram tiles is for sprite blitting against background tiles. I understand that. In effect no tile can overlap another. However, we can make it look like they do. For each tile overlapped you will need 1 ram tile. If two sprites overlap the same tile then you still only need the 1 ram tile since all those tiles would be combined together.

The assignment of ram tiles to sprites is a fairly automatic practice. There may be an order the ram tiles get used in, there may not. That is not so important. What is important is how many ram tiles that are in actual use by sprites.

I have found a way. It is rather simple really.

This will allow you access to some variables found in the kernel files:

Code: Select all

extern unsigned char free_tile_index, userRamTilesCount;
This will display the value: (in CUzeBox)

Code: Select all

void _emu_whisper(int port, unsigned char val){
	#ifdef DEBUGMSG
	if(port==0x39 || port == 0){
		unsigned char volatile * const _whisper_pointer1 = (unsigned char *) 0x39;
		*_whisper_pointer1 = val;
	}
	if(port==0x3A || port == 1){
		unsigned char volatile * const _whisper_pointer2 = (unsigned char *) 0x3A;
		*_whisper_pointer2 = val;
	}
	#endif
}
_emu_whisper(0, free_tile_index);
 _emu_whisper(1, userRamTilesCount);
We need enough ram tiles to cover each tile that a sprite overlaps, even if only by 1 pixel. With the above code running in your main loop (or a vsync callback function) you can have an instant view of the actual ram tile usage. With this information I was able to reduce the flickering of my sprites enough to actually add 2 more bad guys onto the screen. To do this I simply had the bad guy stop (on an 8x8 pixel boundry of course) at the end of their current movement if the free ram tiles count was 4 less than RAM_TILES_COUNT (may need to adjust that a bit.) A 2x2 tile sprite that is aligned on boundries will only require the 2x2 ram tiles (4) to be displayed. Stopping the sprite from moving on the X and Y saved me some ram tiles and it also makes the bad guys appear to be cautious.

I thought that this information might be useful!

Progress on UzeZelda continues!
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Legend of Zelda (RPG game)

Post by nicksen782 »

New updates:
http://www.nicksen782.net/r_uzezelda.php looks a little nicer. Shows different versions/iterations with their features and their controls. As always, the latest version is available there too above the new colored tables.

Version 3: 2017-04-06:
In this update I managed to get Manhandla working. That was the boss of the 3rd level. He is likely to be the most complex boss considering it is basically 5 independent enemies that move in unison and each with their own hit points. It looks pretty good, give it a shot!
There is now sprite vs sprite hit detection. I'm using it so that the player takes damage if touched by an enemy.
Health is represented by hearts now. If all hearts are lost then the game will go into a game over animation that is similar to the original game.

Flash Space:
I had other stuff finished such as all the title screens in the beginning of the game. However, the instructions for them take up too much space right now. I'll put them back in when I have finished the remaining game logic. At that point I'm going to go through a full refactor to try to get as much efficiency out of it as I can. Flash space limitations are my current enemy. I'm going to want to figure out music streamed from SD soon because that could save me another couple kilobytes. Speaking of saving bytes I did Manhandla differently than I have other enemies. For the tiles I had chosen for Manhandla it would have been 21 tiles (1.344k). There would have been some tile duplication though. But, I noticed that all I really needed were the middle part (only half of it) and the open/close claw. I would need to do X and Y flipping and map the sprites differently (2 sprite maps per character/piece). Result is 11 tiles at 704 bytes. That is a 640 byte savings and the logic wasn't too terribly different. I need those bytes.

I'm going to work on loading screen data from SD now. This would provide what bad guys, secrets, triggers, exits, location, etc for the screen. It will make the game quite a bit more like a game! ... maybe provide the player a weapon since it is rather unfair that only the bad guys can do damage. :twisted:
User avatar
D3thAdd3r
Posts: 3293
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

Wow very neat man! That is a good chunk of enemies and really the flicker is not too terrible at all. I very much want a sword to hit them with 8-)
User avatar
D3thAdd3r
Posts: 3293
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

I like seeing the progress on this, and can only imagine how cool it would be to have a real full on adventure game completed for Uzebox. Do you have any new stuff on this one?
User avatar
nicksen782
Posts: 714
Joined: Wed Feb 01, 2012 8:23 pm
Location: Detroit, United States
Contact:

Re: Legend of Zelda (RPG game)

Post by nicksen782 »

Hey! I do actually. Bad guys and the player can fire projectiles and the projectiles that are available to be fired are configurable per character. I've cleaned up the code for the screen scroll transitions. Additionally I've been learning about how compiler flags work and I've been able to further reduce the filesize of the game.

I'm currently working on the data system for handling things such as what characters are placed on the screen and where, also a different method for determining what multiple of 602 in the datafile to start from to determine where to start reading for a screen. I'm currently using a sort of lookup table with "multi" values. They are arranged such as (X, Y) on the world screen can be used to get the correct indexes of the lookup table array. This takes a minimum of 384 bytes plus whatever overhead is needed by reading from progmem.

Unfortunately I haven't completed the new data system yet so the "live" copy is broken (the older versions remain available of course.) I'm going to store this new "screen data" directly after the tilemap data for a screen. This way I would not need to do another seek. I need to recreate my system of creating the game SD binary for this to work. I'm in a planning stage now.

I'm still working on this project! I intend to complete this thing.

Oh, has anybody been able to figure out a method of streaming music data from SD? I've completely disabled the sound engine for now. (Saves around 10k or so.)
Attachments
Initial concept.
Initial concept.
concept.png (63.73 KiB) Viewed 7228 times
Post Reply