42bs Posted February 5, 2019 Share Posted February 5, 2019 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. Quote Link to comment Share on other sites More sharing options...
42bs Posted February 5, 2019 Share Posted February 5, 2019 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. Quote Link to comment Share on other sites More sharing options...
42bs Posted February 5, 2019 Share Posted February 5, 2019 (edited) 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 February 5, 2019 by 42bs Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 5, 2019 Author Share Posted February 5, 2019 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 . Quote Link to comment Share on other sites More sharing options...
LordKraken Posted February 5, 2019 Share Posted February 5, 2019 (edited) I don't want to be the party killer ( ), 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? Edited February 5, 2019 by LordKraken Quote Link to comment Share on other sites More sharing options...
42bs Posted February 5, 2019 Share Posted February 5, 2019 I don't want to be the party killer ( ), 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. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 5, 2019 Author Share Posted February 5, 2019 I don't want to be the party killer ( ), 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. Quote Link to comment Share on other sites More sharing options...
+karri Posted February 5, 2019 Share Posted February 5, 2019 (edited) 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 February 5, 2019 by karri 1 Quote Link to comment Share on other sites More sharing options...
LordKraken Posted February 5, 2019 Share Posted February 5, 2019 (edited) 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 February 5, 2019 by LordKraken Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 5, 2019 Author Share Posted February 5, 2019 (edited) 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 February 5, 2019 by Nop90 2 Quote Link to comment Share on other sites More sharing options...
42bs Posted February 6, 2019 Share Posted February 6, 2019 (edited) 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 February 6, 2019 by 42bs Quote Link to comment Share on other sites More sharing options...
+karri Posted February 6, 2019 Share Posted February 6, 2019 The new version with 60 fps works like a dream. The clock is now steady on a real Lynx. The game is also a lot easier now as I have more than twice the time Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 6, 2019 Author Share Posted February 6, 2019 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. Quote Link to comment Share on other sites More sharing options...
+karri Posted February 6, 2019 Share Posted February 6, 2019 (edited) 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 February 6, 2019 by karri Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 6, 2019 Author Share Posted February 6, 2019 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. Quote Link to comment Share on other sites More sharing options...
42bs Posted February 6, 2019 Share Posted February 6, 2019 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? Quote Link to comment Share on other sites More sharing options...
+karri Posted February 6, 2019 Share Posted February 6, 2019 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"); Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 6, 2019 Author Share Posted February 6, 2019 (edited) 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 February 6, 2019 by Nop90 Quote Link to comment Share on other sites More sharing options...
+karri Posted February 6, 2019 Share Posted February 6, 2019 (edited) 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 February 6, 2019 by karri Quote Link to comment Share on other sites More sharing options...
42bs Posted February 6, 2019 Share Posted February 6, 2019 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 ... 1 Quote Link to comment Share on other sites More sharing options...
+karri Posted February 6, 2019 Share Posted February 6, 2019 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. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 15, 2019 Author Share Posted February 15, 2019 Almost finished the Higscore table and added eeprom saving, but have to test it. Is there an emulator implementing the eeprom saving feature? Quote Link to comment Share on other sites More sharing options...
42bs Posted February 15, 2019 Share Posted February 15, 2019 AFAIK handy does it. Quote Link to comment Share on other sites More sharing options...
Nop90 Posted February 15, 2019 Author Share Posted February 15, 2019 I have Handy for windows v0.95 (dated 2007) and it doesn't seem to work. Should it create a save file calling the writing eeprom function? Quote Link to comment Share on other sites More sharing options...
42bs Posted February 15, 2019 Share Posted February 15, 2019 (edited) 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 February 15, 2019 by 42bs Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.