Legend of Zelda (RPG game)

Use this forum to share and discuss Uzebox games and demos.
User avatar
D3thAdd3r
Posts: 3222
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

nicksen782 wrote:

Code: Select all

while (!GetVsyncFlag()) {} ClearVsyncFlag();
Right, it is functionally equivalent. If you do not use WaitVsync(1) anywhere in your code, it might be ok to add the buffering code just that. Most code that I ever see will have some part where there are some few frame delays using WaitVsync(x) that doesn't make it back to the main loop each frame. In any case it does not matter how it happens, just that the SD music buffering code gets a chance to run every frame;just seems easier to use WaitVsync() IMO, but I do not know what your code is like.
nicksen782 wrote:Both of these would wait until the next vsync, right?
Yes they do the same thing, and this tight loop is indeed happening during user code...which will eventually get interrupted, the frame drawn, then the vsync flag set by the kernel. This code will resume, and of course when it does the flag was just set before the frame was drawn(but after the user code was interrupted to do so).
nicksen782 wrote:You are suggesting reading from the SD card while in the user-code portion, right?
Yes, I think it is the only reasonable way to juggle SD music and also arbitrary other uses you might require. It could be done in assembly, but not cycle counted form since it is unknown what delay it will be, so it would need to happen after frame rendering anyway. Most of it is variable length waiting, so I think there is no need for assembly.
nicksen782 wrote:My question is how long of a wait is it?
It is always deterministic but it is not possible to assign one value to answer this, because each frame is going to take a different amount of time. From collision detection, logic branches, ram tile blitting on/off tile boundaries, music, etc. there are a whole host of things that happen in a frame that make each one have much different timing. Of course it doesn't matter so long as you know the worst case never takes too long, which is what basically all Uzebox games do. The amount of time spent waiting is how many cycles you didn't use out of the full user code time which always changes. Vsync always happens at exactly the same time, but it is based on how many lines you are drawing, etc. I don't remember off the top of my head the exact cycle count, but that interrupt should go off roughly 28,686868/60 = ~478,114 cycles of which each scanline takes IIRC 1820 cycles. So if you run 200 scanlines tall, it takes 364,000 cycles away from user code. Also even during user code when the frame is not being drawn, user code will repeatedly get interrupted(but you wont even notice) by the hsync that continues to drive audio even for "invisible scanlines". To clarify, user code is always the same amount of time each frame, but waiting for vsync is always different depending on how many cycles got used.
nicksen782 wrote:So, you've discussed when to fill the buffer. What about how to get the sound engine to read from that buffer instead of progmem? This buffer would be ram-based and therefore (correct me if I am wrong) would take less cycles/time because of reducing the progmem reads. Does that mean that we could gain some time with the modifications to the sound engine? How much time?...
In the pseudo code above I showed a modified song player that should basically work. It is ram based so you save a cycle here and there on LD vs. LPM, but this is unfortunately very insignificant to the large amount of cycles that you will be waiting for the SD card inter sector delay. This delay depends on the card, but in all cases SD music will absolutely be slower than the flash music player. Basically the custom WaitVsync() is trying to claw back those extra cycles that go to waste, and hoping that happens often enough, that there is no real performance penalty to the main program and the music never stalls.
nicksen782 wrote:Any reason not to put the SD card read portion in there so it is automatic?
Having the separation allows more flexibility in how it can be used. My early experiment did not do this, and it was unusable. The player code needs to run every frame like it does normally to keep the music going, and there is no sense in manually controlling that through the program because it is needless complexity added to anything that wait for a frame to end. Having the buffer part separate is just to keep it light weight, as it will be in a tight loop waiting on vsync(we want it to stop when it happens, so we do not eat too far into the next frame) and potentially the state machine could run several times per frame or no times a frame, where the song player will always run once.
nicksen782 wrote:Another question, there is the pre and the post vsync call back. Why might you choose to use one vs the other? I'm using the post vsync callback to increment some counters that I use as simple timers (such as when the title screen triforce should change color or how long a character should flash after being hit.) Is that appropriate?
Let me get back to you on that, as to be honest I have forgotten which one I even use for what. For controllers I use PostVsync so I think timers would be good there also. For graphics modifications I think I had to use Pre?..but it is very specific to what you are doing. If you are doing sprite recoloring from sprites blit by the kernel like Alter Ego did(BTW do not do this, it is very inefficient and the wrong way) it does indeed matter which one...basically if it works for your purpose then it works, but certain things that need to happen after different kernel functions you need to use the appropriate one. Even using the wrong one to read joypads could potentially introduce a 1 frame delay in player response for no reason.

Hope that made any sort of sense, it is sort of difficult to explain because it involves so much of the Uzebox kernel.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Legend of Zelda (RPG game)

Post by Artcfox »

nicksen782 wrote:My question is how long of a wait is it? I thought about trying to calculate it and display it somewhere but the code for doing that would affect the timing. Best I can think of is doing something like setting a pin high and then low after each vsync and then timing it with an o-scope. Somehow I figure there must be a better way.
I know that Uzem will print out a cycle count between two successive calls to wdr.

Code: Select all

__asm__ __volatile__ ("wdr");
// put code you want a cycle count for here
 __asm__ __volatile__ ("wdr"); 
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 »

Wow! That could be really helpful. I use CUzeBox though.

Is there some other way to get the result value and then I could send it to the "whisper port"?
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Legend of Zelda (RPG game)

Post by Artcfox »

nicksen782 wrote:Wow! That could be really helpful. I use CUzeBox though.

Is there some other way to get the result value and then I could send it to the "whisper port"?
I switched to CUzeBox, but still use Uzem for the things that CUzeBox can't do yet, though it looks like that feature can be added to CUzeBox without much trouble.
User avatar
D3thAdd3r
Posts: 3222
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

TCNT0 is the 16 bit timer that could be used to measure when vsync will happen. You could probably average the value out and put it out the whisper port each frame before waiting on vsync flag to see how many cycles are left.
User avatar
Jubatian
Posts: 1564
Joined: Thu Oct 01, 2015 9:44 pm
Location: Hungary
Contact:

Re: Legend of Zelda (RPG game)

Post by Jubatian »

I will look in this for CUzeBox, I mean the wdr solution. Yes, it isn't something hard to accomplish for console output, I just would like to do it in some manner it has sensible displayable result as well.

EDIT: I did an update to CUzeBox to allow it measuring cycle counts between WDR instructions.
User avatar
D3thAdd3r
Posts: 3222
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

Did you get this converted over to simpleSD already? I'm going to work on SD music streaming a bit, and I do not have any full fledged game that actually used simpleSD yet. I can probably figure out the generic case, but probably before long(if I actually sit and focus on it for a couple hours of coding) it will be testable in game and can be dialed in from there. Naturally no better place than in Zelda itself!
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 »

D3thAdd3r wrote:Did you get this converted over to simpleSD already?
Yes I did, some time ago. I also have the ability to write to the SD card as well.
User avatar
D3thAdd3r
Posts: 3222
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Legend of Zelda (RPG game)

Post by D3thAdd3r »

Cool, the first game to do so AFAIK. I got a lot done towards the music streaming, and now found a rare race condition that can cause issues when the main program is adjusting buffer positions and gets interrupted...it requires a separate smaller work buffer and lock the main program uses to write in addition to an added buffer for loop starts to avoid having any worst case timing scenarios. I think around 64 bytes of ram grand total will be safe and not have race conditions besides being faster and more generic than my prior approach. Close to test ready.
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 »

RAM, RAM, precious RAM. 64 bytes is a ram tile.

I've been thinking about writing a blitter wrapper that assigns sprites to available sprite indexes (contiguous or not) and those in indexes would be managed. This would require individual sprite processing as opposed to processing say a 2 by 2 sprite map block. Benefits of this would be that I wouldn't have to pre-assign spite indexes to everything. Right now projectiles get their own dedicated sprite indexes as do the player and the bad guys. Running out of ram tiles to blit sprites happens but I often run into having ramtiles available but sprites that aren't because they are hard-assigned. If a bad guy has been defeated then those ram tiles and sprite ids should return to the 'bank' to be used by anything. For a screen with lots of shooting bad guys I could reduce the bad guy count by 1 and gain 4 more sprites. With a little more time and insanity perhaps a system could be made that flickers and allows for what appears to be 'more' on the screen than should be available.

Here is a funny example. I have Link shooting rocks. Get enough rocks on the screen and you end up throwing Link's head (specifically that sprite.) If you shoot arrows it happens much more quickly.

Zelda has been slowed by the development of the anti-spam bot but it has not been forgotten! I look very forward to putting music back into the game!
D3thAdd3r wrote:Cool, the first game to do so AFAIK.
Nobody is using Simple SD? Chickens and Choppers uses it, right? Tornado 2000 uses it. I don't know what else. Are you referring to the ability to write to the SD card? I may be the first considering that the SD write code isn't included in Simple SD. Mine is rather simple as it just writes a full block. I've tested it and I know that it works but I'm not using it yet. I intended to use it to keep track of doors that were unlocked, items that were collected (such as the letter, Triforce pieces), bosses defeated, etc. Hopefully 512 bytes is enough. I'm currently reserving the first 512 bytes of the SD datafile for this.
Post Reply