Allow me to address your points:
I think there is value in having a primary game running in a main loop. The primary game keeps track of your score, lives, and the game speed.
All those stats don’t need a primary game running, but just a manager that holds their values, potentially saves them to SRAM, and which each GameState (minigames’ base class) access to modify their own behavior or to update those stats.
Game speed is especially a reason there should be something calling a step function for each game. Because if we want to run at double-time, the main game can call the update function twice.
If each microgame had its own main loop and was running independently, every game would need to individually implement speed-up behavior on its own.
Calling twice or more an update method during a game frame would not be a very good way to affect the simulation’s speed. First, because you loose the ability to scale the game’s speed using lower than 1 steps (like to increase the game’s speed from 1 to 1.5); if the game consists of say 50 minigames, increasing the speed in the way you suggest will be a nightmare to balance since the speed progressing from the first to the last will be too big. Second, the minigames’ simulation will depend on their performance, and unless you have absolute control of it, you will end up with objects slowing down and speeding up not intentionally. Third, it could break other processes that may run asynchronously to the minigame’s logic (like VIP’s o Timer’s interrupts, for example).
To avoid those problems, and maybe others, but at the same time to achieve the desired effect, modern game engines pass to the update methods a deltaTime argument that can be scaled by the programmer. That way the simulation is not dependent on the game’s performance, the scaling can be non linear and async processes are not affected. The VUEngine’s GameState’s execute method passes such a value to the game objects, but it is only used to affect animations (that is, the engine’s classes only use the time delta to affect the animations’ speed, but derived classes specific to each minigame could use the time delta for other purposes), while things like physics simulations are updated by another facility.
Speed scaling can be implemented in a GameNameGameState class from which all minigames inherit. In this way, the “primary game” doesn’t need to exist, since it would just be logic shared by all minigames, achieving your goal of avoiding the need to reimplement on each one the same functionality.
On game speed, what do you think about implementing speeding up? Is calling the update function multiple times adequate, or maybe some VUEengine parameters can be tweaked?
As I said before, calling multiple times an update method during the same game frame wouldn’t be a good idea in general, no only in the context of the VUEngine. On the other hand, adding support for time delta scaling to the engine would be easily done.
If the programmer needs access to the time remaining, we can expose it in “common.h”.
By using inheritance, there is no need to use global variables. The time remaining would be a protected attribute of the GameNameGameState class accessible to all the minigames.
jorgeche
VirtualChris schrieb:
You can download the latest ROM here:
http://www.atari2600land.com/insecticide/ic20170324.vb
Hey, I was finally able to test your game on the VB to try to reproduce the problem that you’re experiencing. I was able to see it only by quickly turning on and off the VB while on the character selection screen, and I only noticed different behavior in the third screen.
Here are a couple of things that you may want to check:
1) Make sure that all variables in the problematic screen (where the ant passes by) are properly initialized.
2) Make sure that you are erasing all BGMAP and CHAR memory when entering that screen and before drawing anything to it.
3) Make sure that you are controlling properly the WORLD layers that each image is assigned. It is possible that the ant and the label are being rendered behind the background.
VirtualChris schrieb:
I’m just going to give up on the whole thing. I can’t fix it so the game displays the logo and ant on the intro screen. Here’s what I’ve been doing:
>Starting the game. Everything goes as normal.
>Turn it off at the fighter select screen.
>Wait one second.
>Turn it back on.
>The ant does not display where it is supposed to be.
WHY?!
Could you send me a ROM? Maybe I’m not fully understanding the problem and seeing what is happening could help.
I did a quick, and not very scientific test, and 0x2000 should be enough to prevent the access to the work RAM before it stabilizes. Besides that, I wasn’t able to reproduce a similar problem with our game by turning on and off the VB very quickly. So, my conclusion is that your problem is either related to the usage of local non-initialized variables or some other bug in your program. Sorry if that is not helpful enough.
VirtualChris schrieb:
Turns out it didn’t work after all. Should I try increasing it to 4,000?
I will have to make some tests to try to infer how big a value is needed to achieve the 200usec wait. In the meantime, you could give 0x4000 a go (or even 0x10000)
BTW: How quickly are you turning the VB on and off? I’ve noticed that our game doesn’t work if I turn on and off the VR very fast. Although, I haven’t checked if the problem happens with commercial games, will check it tomorrow.
VirtualChris schrieb:
I increased the 2000 number to 3000. At first I thought it was broken because it didn’t start, but it eventually started up and works great! I want to thank you tremendously for your help.
You’re welcome, I’m glad it finally worked. 🙂
VirtualChris schrieb:
OK, I figured out how to put your code into the file. I get the following message when compiling my code with gccvb:crt0(.text+0x2): undefined reference to ‘_bss_start’
crt0(.text+0x6): undefined reference to ‘_bss_start’
crt0(.text+0xa): undefined reference to ‘_bss_end’
crt0(.text+0xe): undefined reference to ‘_bss_end’
You already have the code to clean the bss section. Roll back your crt0.s to its original form.
I suspect that the problem is caused by not waiting for the work RAM to stabilize before cleaning it, so, after rolling back to the original crt0.s, add this to it just after “_start”:
/* wait for WRAM */
movea 0x2000, r0, r6
wait_for_wram_loop:
add -1, r6
bnz wait_for_wram_loop
Recompile it with “as” and try again. If the problem persist, try increasing the 0x2000 value an give it a couple more tries.
VirtualChris schrieb:
I meant I don’t have the crt0.o file. Sorry about the confusion.
If you’re using this version of GCCVB
(http://www.planetvb.com/content/downloads/tools/gccVB%202.95%20(Precompiled%20NVC%20Version).zip)
then you have crt0.o. It is located in v810/lib/. There are even two versions of it: crt0.o and crt0.o.bak.
VirtualChris schrieb:
I think I might have found a folder for the one I’m using, but it doesn’t have that crt0.s file, but it does have a crt0.o file in it. Apparently I’m using gccVB version 2.95.
I guess that you have the version published in
http://www.planetvb.com/modules/tech/?sec=tools&pid=gccvb
If so, then you already have a ctr0.o file that is cleaning up the bss section and the stack’s space provided that it was compiled from crt0.s and not from crt0_old.s (didn’t bother checking its contents).
You can get crt0.s from GCCVB 2.95’s source:
http://www.planetvb.com/content/downloads/tools/vb_v810_gcc_03.tar.gz
You already have the “as” command, so you can use it instead of “v810-as” to compile crt0.s, just to make sure that the cleaning of WRAM is in the crt0.o file that you’re using (don’t forget to make a backup of the one that is currently working for you before replacing it!).
Just for completion, this is the code that does the cleaning:
/* clear .bss section and unintialized RAM */
movhi 0x0501,r0,r4
jr loop_start1
loop_top1:
st.h r0,0[r5]
add 2,r5
loop_start1:
cmp r4,r5
blt loop_top1
If the problem persists, then it means that you already had the cleaning up in place. So, the issue could be related to how soon you’re accessing the work RAM in your program. I will refer you to the official documentation, page 4-5-1. I’m completely ignorant regarding the relation between assembly operations and the amount of time the CPU takes to execute them, but the doc says that you need to wait 200usec before accessing work RAM, and the crt0.s file that you have access it almost immediately to clean it. So, it could be the case that the weird behavior is caused by not waiting for the work RAM to stabilize before trying to write the 0s of the cleaning. You can try the following and check if it solves the problem: add the following code to crt0.s just below “_start:” and recompile it:
/* wait for WRAM */
movea 0x2000, r0, r6
wait_for_wram_loop:
add -1, r6
bnz wait_for_wram_loop
The 0x2000 value is just arbitrary and a vague guess on my part so, if it doesn’t work, try to increase it and repeat the test.
jorgeche
VirtualChris schrieb:
‘v810-as’ is not recognized as an internal or external command, operable program or batch file.I put a batch file in a whole bunch of different folders and they all came up with this message. I’m afraid you’re going to have to dumb it down for me. A lot.
What compiler and version of it are you using?
VirtualChris schrieb:
… The game itself works fine. It’s the part where you turn it off and then turn it back on that is troubling me. You’d think when you turned off the Virtual Boy, everything in the code would reset to 0. But this is not the case for some reason. The trouble I’m having is making stuff reset to 0 by hand.
You cannot make that assumption. It is always a best practice to initialize all variables to a suitable value before using their data.
One way to make sure that all global / static non-initialized variables are set to zero when the program starts is to clear the program’s bss section (they are allocated in it) in the crt0.s file before the call to the main function is done.
To clear the bss section, you need something like the following code. Take into account that it depends on how the program’s sections are defined in the vb.ld script that you’re using (that is, there needs to be something similar to __bss_start and __bss_end in it):
/* clear .bss section */
movhi hi(__bss_start), r0, r6
movea lo(__bss_start), r6, r6
movhi hi(__bss_end), r0, r7
movea lo(__bss_end), r7, r7
jr end_init_bss
top_init_bss:
st.h r0, 0[r6]
add 1, r6
end_init_bss:
cmp r7, r6
blt top_init_bss
You will have to recompile the crt0.s file into crt0.o and replace the one most likely present in your compiler’s folders (were you should find its source too, the crt0.s file). To recompile it execute the following:
v810-as -o crt0.o crt0.s
For local variables, you’re out of luck since they are allocated in the stack and it gets “dirty” during the program’s execution. You can clear all work RAM, and hence the stack, in the crt0.s file too. That could make a difference, in your case, when you power off and on the VB since it is already working for you on a cold power up, but it would be kind of hack-ish and still prone to very hard to track down bugs because, eventually, the non initialized local variables will be used and produce undefined behavior. So, you should still properly initialize all the variables before using them.
jorgeche
And thanks to ElmerPCFX, a couple of BIG issues with the VBJaEngine’s architecture were found and fixed.
As he said, and besides a couple of small fixes that are pending on my side, the engine and the demo run properly with GCC’s latest version, and they compile way faster too!
It was a great collaboration that I hope will help both communities. 🙂
jorgeche
Hey Guy:
This sound amazing, if I understand it correctly:
• The entire project will be open-source. The core functionality of the emulator (no bells or whistles) will be set up as a simple library that can be incorporated into any existing C-compatible application. Introducing Virtual Boy emulation capabilities into other projects will be very simple to pull off.
I’m starting to think about a level editor for the VBJaEngine, and having emulator capabilities in it could allow us to avoid the need to have to maintain in sync two versions of the engine. Do you think that it will be feasible to get your core emulator to work inside a custom Eclipse plugin (or any customizable IDE at all)?
Regards,
jorgeche
Hi ElmerPCFX. I’d gladly try your patches. I was trying some of the things that you’ve talked about in this thread on gcc 4.4 without much success. Anyway, I’m curious about whether or not I will be able to squeeze some performance out of VBJaEngine by using your patches.
BTW: some features that we use and may or may not be affected are:
1) Recursive calls
2) Variadic functions
(Excuse my lack of ASM/Compiler knowledge if the these are not related at all to your patches.)
VirtualChris schrieb:
Hi jorgeche,
I did not understand the second option which was recommended. It all looked like gobbledygook to me, so I went with the first option, which was to force a wait. As it turned out, that option worked for me greatly. Thank you for helping me out, and now that that problem is done, I can work on the game some more.
Glad to be of help ;).
Hi Chris:
By quickly checking your code, it seems that you are not waiting for the VPU’s drawing process to end before writing to DRAM. The lines like the following, in the walkingaround function, could be the offenders:
// put quincy on the screen.
vbSetWorld(29, WRLD_ON, quincyx, -2, quincyy, quincycutx, 0, 0, 14, 32);
You need to either brute force the wait, and write to DRAM only after it:
while (VIP_REGS[XPSTTS] & XPBSYR);
Or use interrupts, on these posts you can find more info about them:
http://www.planetvb.com/modules/newbb/viewtopic.php?topic_id=4110&post_id=14206#forumpost14206
http://www.planetvb.com/modules/dokuwiki/doku.php?id=direct_screen_draw
The interrupt approach is preferred, and gives great results on hardware, but it is tricky to understand it at first sight, and you have to make sure that no race conditions arise because of the asynchronous nature of the interrupts. This is what I do:
// VPU’s interrupt handler
void vpuInterruptHandler()
{
// here you write to DRAM, in your case:
vbSetWorld(29, WRLD_ON, quincyx, -2, quincyy, quincycutx, 0, 0, 14, 32);
}
while (true)
{
// update each subsystem
// wait to sync with the game start to render
// this wait actually controls the frame rate
while(!(VIP_REGS[INTPND] & GAMESTART));
VIP_REGS[INTCLR]= GAMESTART;
// at this point disable the VPU’s XPEND interrupt
VIP_REGS[INTENB]= 0;
// calculate the data to write to DRAM while the VPU
// is busy drawing
doSomething();
// after you have finished calculating the data to write to DRAM
// enable the VPU’s XPEND interrupt
VIP_REGS[INTENB]= XPEND;
// do any other process that is not DRAM related
doSomethingElse();
}
I’m omitting some details, you can check the full code at the following links, just look for the Game_update and VPUManager_interruptHandler methods:
Jorge
Hi speedyink, thanks for the feedback.
I’m not doing debugging on hardware just yet (because I get obsessed with improving the performance and there are still some features to implement and a few bugs to fix on the emus). Maybe KR155E has tested it but didn’t want to share the bad news 😛
For the moment, the engine is meant to run bug free (minus some graphical glitches related to layers’ sorting) on Reality Boy, next will be Mednafen, and finally on hardware.
Greg Stevens schrieb:
I did notice that the virtual boy automatically wraps coordinates to the other side of the screen if they go outside the screen bounds. Maybe there is an obvious reason as to why but I thought it would only do that for BGMaps and Worlds if the OVR[something] flag was set. So if any object goes off the right side of the screen it automatically appears on the left hand side without having to do any special coding. I’m actually going to have to do coding to remove that “feature” if I can’t find a flag or register that is causing that to happen.
Hey Greg, I’m experiencing a similar issue, did you find a way to disable the “feature” without having to turn off the WORLD?
Hey, you don’t need to worry about the size of your data, as long as you read and write to SRAM following its constraints. That is, you will need a BYTE* pointer to the data to save/read, a displacement in SRAM that reflects the displacement of your data (you will want to use a struct to hold any data to save in order to use this approach) and the size of the data to save.
You can take a look at the following classes and search for the use and definition of the save/read methods:
Refactoring the code to suit your needs should be easy enough.
Jorge
I’ve always wanted to implement a level editor for the engine, but it is just now that it has reached a state where I think that it makes sense to do it. The problem is that there are 5 o 6 very important features yet to be implemented in the engine, and they are much more interesting for me.
So, if and when we decide to make the engine’s skeleton demo publicly available, we will consider implementing the level editor. It doesn’t bother me that much that the engine doesn’t get any usage beside me or KR1553 (the fact that he has taken interest on it is more than enough for me already), but the level editor doesn’t look like a cool challenge, and if the engine is not used by anyone else, it doesn’t really make any sense to me to take time away from it to work on an editor.