Jump to content
IGNORED

Is 10 FPS a too low framerate for a LYNX tile based game?


Nop90

Recommended Posts

Sorry I did not explaned clearly.

 

With tgi_setframerate you set the phisical framerate, that is the LCD refresh rate. I know of the possible values for tgi_setframerate, and since I didn't change it, I expect it is 75Hz.

 

I was talking about the number of buffer swap i can perform in a second, that depends on the time the code can do a game loop. 49 FPS is the average value of tgi_updatedisplay() that I can do in 1 second after a lot of code optimizations.

 

Yepp, I did expect this. That's why I propose a fixed rate. Looks better esp. for animations.

So if you want to stick with the 75Hz refresh, you can have a 37.5 FPS.

Link to comment
Share on other sites

Please educate me why 75 is bad? If I make a game where I cannot keep up with the hardware framerate and catch every frame so how long do I need to wait for the next opportunity to swap buffers. 1/75 , 1/60 or 1/50 of a second? Imho the 1/75 is the shortest time.

 

Atari did suggest 60Hz. 50Hz looks ugly with the original LCD. And 75Hz does not leave enough time during VBL to handle things.

Since the average FPS he achieves is 49, there are some skips. So going down to 60Hz/30FPS still gives good animations IMO.

Link to comment
Share on other sites

 

The timekeeping is based on the number of VBL interrupts per second. So the CLOCKS_PER_SEC divisor is a constant that should match the VBL timer's programming. But there may be some real-life things that affect reading the timer register at such a high rate on a real Lynx. At least that could be one explanation of why the results are not steady.

 

You could also skip the division and just compare the clock() to the previous value.

now = clock();

....


while (clock() != now) {
    // Interrupt has occurred
    now++;
    if (now - lastsecond == 75) {
        lastsecond = now;
        fps = frames;
        frames = 0;
    }
}

 

I'd suggest "now - last >= 75" in the game loop. But I'd do this in the VBL interrupt.

Edited by 42bs
Link to comment
Share on other sites

Had a good surprise.

 

decided to set tgi_setframerate(75), and now I have 75 FPS in game.

 

Lessone learned: never thrust default behaviur.

 

Now the game i too fast, I'll try if it works better at 60 Hz, otherwise I'll have add some frameskipping code.

 

For the timer I used this code:

 

#define _FPS 75

...

tgi_setframerate(FPS)

...

while(!exit)

{

tick1 = clock();
fcount++;
if(tick1 >= tick2+FPS)
{
gametime -= 1;
tick2 = tick1;
fps =fcount;
fcount=0;
}
}

In the emulator this works fine. This evening I'll send a new build to karri for a new test .

Link to comment
Share on other sites

I don't want to be the party killer ( :grin: ), but are you sure about your frame rate?

It's pretty hard to get decent FPS on the lynx, even the best games dont get above 20fps (and I'm generous).

 

Are you running the full game with logic and everything?

 

I was in doubt as well. But without seeing the whole loop, I didn't want to say something.

Just as hint: A "CLS-Sprite" takes about 15% of a 60Hz frame.

Link to comment
Share on other sites

I don't want to be the party killer ( :grin: ), but are you sure about your frame rate?

It's pretty hard to get decent FPS on the lynx, even the best games dont get above 20fps (and I'm generous).

 

Are you running the full game with logic and everything?

 

I code from so much time to be sure about nothing.

 

But I can say that my code is calling at every game loop all the game logic and the input handling, waits for the tgi_busy() to return false, draws a chain of sprites of a fixed number of 30 elements, sets an updated palette (for animations made rotating colors) then calls the tgi_updatedisplay().

 

There is no frame skip code. I the last build I set the framerate to 60Hz and now the game speed is fine.

 

The code for counting the frame is in my previus post, so you can check it. It shows how the seconds counter is decreased, and in the emulator the timer ticks at the right speed if compared with my watch.

 

Apart from this, sometime I made so big mistakes in my code that the measured framerate could be a compltly random value :)

 

 

I was in doubt as well. But without seeing the whole loop, I didn't want to say something.

Just as hint: A "CLS-Sprite" takes about 15% of a 60Hz frame.

 

good for me that I don't clear the screen and only draw the change occurred on the content of the two framebuffer.

 

I would like to share the code to make you check what I did, but I can't because it's property of the Retroguru team. If my next project will be something new, or a port of an open source game, I'll be happy to share all my work, as I did for all my projects in the 3ds scene.

 

For the moment, if my code runs fine, I'm happy.That's enough.

Link to comment
Share on other sites

I can assure that the game is really smooth with absolutely no staggering anywhere. So the frame rate is adequate whatever it is. It is definitely much higher than in any of my games. ;)

 

It is a full game running with all options. And sound.

Edited by karri
  • Like 1
Link to comment
Share on other sites

Really looking forward to see it in action then :)

 

Reducing the number of sprites to 30 max is probably the kind of optimization I should look into, but in bomberman games, it's pretty difficult to render only the modified part as there is so much going on...

Edited by LordKraken
Link to comment
Share on other sites

 

Reducing the number of sprites to 30 max is probably the kind of optimization I should look into

 

I read somewhere in this forum that after 20 sprites the framerate starts to lower, but this effect should also be related to sprite size.

 

Many lynx developers use large sprites because blitting few large sprites is more efficient that many small ones.

 

When I designed the porting strategy I decided to maintain the game map size (that is designed for a 320x240 screen) resizing the images from 16x16 pixels to very small tiles of 8x7 pixels.

 

This caused the incredibly low framerate in my first version (4 FPS, raised to 10 FPS with heavy optimization of the game logic), but when I changed the drawing strategy, I think that so small tiles was the key to have a very good framerate.

 

Sooner or later I'll try to port a scrolling platform on Lynx, and this will require a very different code design to have good performance. These challenges are the funny part of coding on retro systems.

Edited by Nop90
  • Like 2
Link to comment
Share on other sites

 

I code from so much time to be sure about nothing.

 

But I can say that my code is calling at every game loop all the game logic and the input handling, waits for the tgi_busy() to return false, draws a chain of sprites of a fixed number of 30 elements, sets an updated palette (for animations made rotating colors) then calls the tgi_updatedisplay().

 

...

 

good for me that I don't clear the screen and only draw the change occurred on the content of the two framebuffer.

 

I would like to share the code to make you check what I did, but I can't because it's property of the Retroguru team.

 

...

 

Good strategy. Since writing the whole screen can be done in 2ms, it is very likely that your FPS calculation is correct.

 

Regarding source: I thought it is open source, but its _only_ free ;-) Anyway, no need to see the sources anymore.

 

So it is a vast improvement from 5FPS to 75FPS. :)

Edited by 42bs
Link to comment
Share on other sites

Well, than I'll convert the four missing mod tracks, fix two tracks that don't sound well and add the sfx. This will complete the alpha version.

 

For the final version I have to wait that our gfx artist fixes the gfx, than I'll start to post some screenshots.

 

One possible release date could be at Revision demoparty (april 2019).

 

Karri, does the flipscreen work fine? I had problems flipping the controls, but made it to work with a code trick and need to know it it's ok on real Lynx.

Link to comment
Share on other sites

Flipscreen works in every part of the game - retroguru screen, menu screen, in game.

 

Are you going to implement reset and pause?

 

The way I used to implement the "keyboard" controls is below. It has only 5 keys but they are carefully tuned to get the typical touch and feel of Lynx keys. There is no need to code any flip-aware code for the joypad. It works correctly by using tgi_flip() to toggle the screen.

 

#include <conio.h>
if (kbhit()) {
    switch (cgetc()) {
    case 'F': // Pause + Opt2
        tgi_flip();
        break;
    case 'R': // Pause + Opt1
        return RESTART_LYNX;
    case '1': // Opt1
        intro = true;
        break;
    case '2': // Opt2
        play_next_tune();
        break;
    case '3': // Opt1 + Opt2
        erase_eeprom();
        break;
    case 'P':
        paused = 1 - paused;
        if (paused) {
            //abc_pause();
        }
        break;
    default:
        break;
    }
}

Edit: There is actually a 6th key that has never been used afaik. The character '?' will be read if you press Pause + Opt1 + Opt2.

Edited by karri
Link to comment
Share on other sites

Thanks karri, already used your code (in the past days lurked all the Atari Lynx programming section).

 

The version you tested catches the key events for pause and reset, but the handlers are empty.

 

The game already can be paused with the A key that brings to a menu where you can continue the game, restart the level or go back to the main menu. The pause instead key could halt the game and the music keeping the game in a loop till the pause key is pressed again. Is this the right way to implement it?

 

About the reset feature, is there some hard reset register to trigger, or is it enough to make the code go to the start of the main function and reinitialize everything? If I'm not wrong this is what you do in the solitaire cart.

Link to comment
Share on other sites

About the reset feature, is there some hard reset register to trigger, or is it enough to make the code go to the start of the main function and reinitialize everything? If I'm not wrong this is what you do in the solitaire cart.

 

Where RESET leads the player depends on you. You might first restart the level and if a second time pressed reset to the start of game.

 

BTW: Do you plan to save high-scores?

Link to comment
Share on other sites

Aah. I was just stupid as I did not understand that you have to use right/left after the pause to choose where you want to go.

 

I prefer just dropping to main menu.

 

But on the multicarts you could do:

*MSTERE0 = 0; // enable all audio channels
asm("sei");
*MAPCTL = 0; // memory mapping for boot state


ptr = (u8*) 0xfd00; // timers and audio
count = 0x40;
while (count--)
{
*ptr++ = 0;
}


*((u8*) 0xFD80) = 0;
*((u8*) 0xFD81) = 0;
*((u8*) 0xFD92) = 0;
*((u8*) 0xFD9C) = 0;
*((u8*) 0xFD9D) = 0;
*((u8*) 0xFD9E) = 0;
*((u8*) 0xFD9D) = 0;


ptr = (u8*) 0xfda0; // palette
count = 0x20;
while (count--)
{
*ptr++ = 0;
}


asm("brk");
Link to comment
Share on other sites

Just implemented pause and reset (done exiting the game loop to return in a while(1) loop before the intro, where I clean all the global variables but the "firstTime" one that avoids reloading the drivers).

 

Made a quick test and it seems to work fine, but have to check that there is no missing loop to be stopped by the "reset" variable.

 

 

 

BTW: Do you plan to save high-scores?

 

I started the project with the idea to implement a classic Lynx cart without savings (for both hiscores and the last played level).

 

Score is not so important in a puzzle game where if you die, you can continue from the level you are starting again from 0 points. The real game goal is to solve all the levels. If you get a very nice score you can always take a picture with the smartphone :).

 

I could change my mind and add it again, but than I will need to check if I have enough memory left for the player name input and the highscore screen. Intro, menu and game logic code are already splitted in different cart segments.

 

Is there a difference in the cost of programmable carts with or wihout the saving feature? Or is the cart the same?

Edited by Nop90
Link to comment
Share on other sites

Adding an eeprom does not have to change the much. The mostly used eeprom has 128 bytes. What I do most of the time is to allocate 128 bytes of RAM for the high scores. Then I read the eeprom into that char array at startup. I usually write a magic cookie in the first byte to see if the read was successful. After that I deal with the RAM copy only. When there is some writes I just mirror the writes to eeprom also. So it works just the same with or without eeprom.

 

Currently most carts that are being released are without eeproms. My carts are always with eeproms. So this is a choice you can make later when you decide how to make carts.

 

The modern eeprom's do not need th be erased before writing. So you only need 2 functions:

 

extern int __fastcall__ lnx_eeprom_read(unsigned char pos);
extern void __fastcall__ lnx_eeprom_write(unsigned char pos, int val);

val0 = lnx_eeprom_read(0);
lnx_eeprom_write(0, val);

 

The data is always 2 bytes, the address range 0..63

lynxeeprom.txt

Edited by karri
Link to comment
Share on other sites

T-Tris does not only save the high-score, but also the game when paused on EEPROM. You can turn off the Lynx and continue later ...

 

That is news for me. Sounds cool.

 

The biggest save I have done is on the hidden Scott Adams Pirates Cove on MegaPak I. It took the entire eeprom to save all items and locations on the adventure.

Link to comment
Share on other sites

  • 2 weeks later...

At least the source have a eeprom.cpp file ...

 

Edit: It must be activated in the LNX header:

typedef struct {
UBYTE magic[4];
UWORD page_size_bank0;
UWORD page_size_bank1;
UWORD version;
UBYTE cartname[32];
UBYTE manufname[16];
UBYTE rotation;
UBYTE aud_bits;
UBYTE eeprom;
UBYTE spare[3];
} LYNX_HEADER;

eeprom & 7 == 1 => 93c46, 2 => 56, 3 => 66, 4 => 76, 5 => 86

eeprom & 0x80 => 8 bit mode

Edited by 42bs
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...