![]() |
Forum Index : Microcontroller and PC projects : Micromite eXtreme:Bad case of the scrolls
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Watch the video ![]() Here is the firmware Beta to play with 2018-01-27_041904_MMX5.04.12Beta.zip The VGA subsystem has been converted to use a double buffered approach which allows complex manipulations to take place on the hidden image and then it to be switched. This all takes place automatically and means that screen refresh and drawing activities are now totally without any sort of flicker Code for the demo Option explicit option default NONE dim integer i const move=4 load image "tiger640" sprite load #1, "apple" sprite show #1,270,190 for i=0 to 50 blit scrollH move next i for i=0 to 50 blit scrollV move next i do for i=0 to 100 blit scrollH -move next i for i=0 to 100 blit scrollV -move next i for i=0 to 100 blit scrollH move next i for i=0 to 100 blit scrollV move next i loop |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Looks good Peter ![]() Not played with it yet - but two initial questions: 1> Is it a wrap-around 'move' (as it appears to be in the video). If not, then where does the 'new' data come from? 2> Before your Do-Loop, you have two 'For I = 0 to 50' loops. Are these loading some 'hidden data' (and hence answering question 1 above? As I mention; I have not loaded the firmware yet to play with - otherwise I could probably work all this out. Not near a MMX hence curiosity taking over . . . Cheers for any answers |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
I know this is just a Beta, but going to highlight some things that you may, or may not, be aware of: (This is on VGA, with console, and CPU252, with USB keyboard 1> The console is not displaying on the VGA. However, if I do a GUI TEST LCDPANEL it then shows 'historic' typing (i.e. it is as if it needs triggering to be switched in). At end of GUI TEST LCDPANEL, everything disappears again. 2> GUI TEST LCDPANEL is very slow at drawing circles will continue to play ![]() |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
With your 'scrolling tiger' code I am seeing three things: 1> When scrolling down, there is 'garbage' appearing (from the top as it scrolls down) on the left hand edge of the screen (approx 12pixels wide) 2> when scrolling right, the whole left hand 12? pixels seems to be one or two pixels lower than it should be. The 'garbage' from above point suddenly displays the correct image data (but just a bit low) 3> When scrolling up, you then get 12? pixels of garbage appearing from the bottom NOTE: Then when scrolling left, everything seems 100% correct The above repeats every time even after a reset / power-down WW |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Better seen with screen-shots! ![]() Left Image = Scrolling up (similar thing occurs when scrolling down - but 'garbage' appears from the top) Middle Image = Scrolling Right. I now see from this photo that there is 'Repeat' in the left column of pixels Right - Close Up of Pixels in left column (for scrolling Up) - Looks like 10 Pixels wide (same width for Up/Down/Right) Hope this helps debug . . . WW |
||||
Heidelberg Newbie ![]() Joined: 22/01/2018 Location: AustraliaPosts: 13 |
Great progress! Welldone ![]() -Heidelberg |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
WW There is a problem with console not respecting the double buffering which I didn't test but I'm not seeing any of the of the other issues. Please reflash with below and/or test with a different monitor. GUI test will be slower but this should not affect real world applications 2018-01-27_183846_Micromite.X.production.zip |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Attached is 5.04.12 Beta2 2018-01-27_232555_MMX5.04.12Beta2.zip This should fix the console issue and has significant performance improvements. In addition it starts to implement layers. There is a new parameter on the SPRITE SHOW command, syntax is now SPRITE SHOW #n, x , y [, layer] Layer is optional and defaults to 1 Layer 0 is considered to be background and sprites on layer 0 will scroll with the background whereas sprites on all other layers will maintain position even when the background moves Demo program of this is below. I think I've solved the strange image issues WW was finding. I believe he saw it because his monitor was slightly overscanning the image and seeing things outside of the "real" image. I've now ensured that the horizontal scroll does not write outside of the 640 pixels. For diagnostic purposes there is a temporary command "SWAP". This swaps the display between the two image buffers so if you see any issues try this to see what is on the previously displayed image. Option explicit option default NONE dim integer i const move=3 load image "tiger640" sprite load #1, "apple" sprite load #2, "apple" sprite show #2,200,190,0 sprite show #1,400,190 for i=0 to 50 blit scrollH move next i for i=0 to 50 blit scrollv move next i do for i=0 to 100 blit scrollH -move next i for i=0 to 100 blit scrollV -move next i for i=0 to 100 blit scrollH move-2 next i for i=0 to 100 blit scrollV move-2 next i loop |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Thanks Peter. Will test later when back in front of MMX . . . ![]() |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
One more update MMX5.04.12 Beta 3 2018-01-28_054042_MMX5.04.12Beta3.zip This starts to implement collisons. This is best explained by a very simple commented example Option explicit option default NONE load image "tiger640" sprite INTERRUPT collision ' specify the interrupt routine to handle collisions sprite load #4, "apple" sprite load #17, "apple" sprite show 4,-10,-10 'show a sprite overlapping the top and left of the screen pause 1000 sprite show 4,600,440 'move the sprite to overlap the bottom and right of the screen pause 1000 sprite show 17,550,400 'show a sprite overlapping the previous sprite and the bottom of the screen do loop end ' sub collision ' collison with the left of the screen returns code 100 ' collision with the top of the screen returns code 101 ' collision with the right of the screen returns code 102 ' collision with the bottom of the screen returns code 103 local integer i print "Collision on sprite ",sprite(S) 'sprite(S) returns the sprite that moved to cause the collision print "Total of ",sprite(C,sprite(S))," collisions" 'sprite(C, #n) returns the number of current collisions for i=1 to sprite(C,sprite(S)) print "Collision with item ",sprite(C,sprite(S),i) 'sprite(C, #n, #m) returns details of the mth collision next i print "" end sub This gives the result: ![]() Yet to be implmented is collisions caused by moving the background (layer 0) and any sprites on that layer. Collision detection is currently based on the rectangular outline of the two sprites. Only sprites on the same layer collide. My intention is that sprites on layer 0 will collide wiith all other layers - comments? Collision code for info/comment void ProcessCollisions(int bnbr){ int k, j=1, n=1; //We know that any collision is caused by movement of sprite bnbr // a value of zero indicates that we are processing movement of layer 0 and any // sprites on that layer CollisionFound=false; sprite_which_collided=-1; if(blitbuff[bnbr].collisions!=NULL){ FreeMemory(blitbuff[bnbr].collisions); //clean up any previous collision result blitbuff[bnbr].collisions=NULL; } if(bnbr!=0){ // a specific sprite has moved if(layer_in_use[blitbuff[bnbr].layer]>1){ //other sprites in this layer for(k=1;k<MAXBLITBUF;k++){ if(j == layer_in_use[blitbuff[bnbr].layer]) break; //nothing left to process if(blitbuff[k].layer == blitbuff[bnbr].layer && k != bnbr){ j++; if( !(blitbuff[k].x + blitbuff[k].w < blitbuff[bnbr].x || blitbuff[k].x > blitbuff[bnbr].x+blitbuff[bnbr].w || blitbuff[k].y + blitbuff[k].h < blitbuff[bnbr].y || blitbuff[k].y > blitbuff[bnbr].y + blitbuff[bnbr].h)){ if(blitbuff[bnbr].collisions==NULL){blitbuff[bnbr].collisions=GetMemory(256);} blitbuff[bnbr].collisions[n++]=k; CollisionFound=true; sprite_which_collided=bnbr; } } } } // now look for collisions with the edge of the screen if(blitbuff[bnbr].x<0){ if(blitbuff[bnbr].collisions==NULL){blitbuff[bnbr].collisions=GetMemory(256);} blitbuff[bnbr].collisions[n++]=100; CollisionFound=true; sprite_which_collided=bnbr; } if(blitbuff[bnbr].y<0){ if(blitbuff[bnbr].collisions==NULL){blitbuff[bnbr].collisions=GetMemory(256);} blitbuff[bnbr].collisions[n++]=101; CollisionFound=true; sprite_which_collided=bnbr; } if(blitbuff[bnbr].x + blitbuff[bnbr].w > HRes){ if(blitbuff[bnbr].collisions==NULL){blitbuff[bnbr].collisions=GetMemory(256);} blitbuff[bnbr].collisions[n++]=102; CollisionFound=true; sprite_which_collided=bnbr; } if(blitbuff[bnbr].y + blitbuff[bnbr].h > VRes){ if(blitbuff[bnbr].collisions==NULL){blitbuff[bnbr].collisions=GetMemory(256);} blitbuff[bnbr].collisions[n++]=103; CollisionFound=true; sprite_which_collided=bnbr; } } else { //the background layer has moved } if(n>1) blitbuff[bnbr].collisions[0]=n-1; } |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
In theory this Beta completes the MMX "games" implementation including sprites, background scrolling and collision detection. Testing has just been on the VGA monitor. Some of the functionality will also work on SSD1963 displays but this needs fully testing and bug fixing. 2018-01-29_052011_MMX5.04.12Beta4.zip The concept of the sprite implementation is as follows: 1. Sprites are full colour and of any size. The collision boundary is the enclosing rectangle. 2. Sprites are loaded to a specific number (1-50) 3. Sprites are displayed using the SPRITE SHOW command 4. For each SHOW command the user can select a "layer". This defaults to 1 if not specified 5. Sprites collide with sprites on the same layer, layer 0, or the screen edge 6. Layer 0 is a special case and sprites on all other layers will collide with it 7. The SCROLL commands leave sprites on all layers except layer 0 unmoved 8. Layer 0 sprites scroll with the background and this can cause collisions 9. There is no practical limit on the number of collisions caused by SHOW or SCROLL commands 10. The sprite function allows the user to fully interrogate the details of a collison 11. A SHOW command will overwrite the details of any previous collisions for that sprite 12: A SCROLL command will overwrite details of previous collisions for ALL sprites 13: To restore a screen to a previous state sprites should be removed in the opposite order to which they were written "LIFO" My test program is: Option explicit option default NONE dim integer testrun=1 load image "tiger640" sprite INTERRUPT collision ' specify the interrupt routine to handle collisions sprite load #4, "apple" sprite load #2, "apple" sprite load #17, "apple" sprite load #49, "apple" ' ' test 1 - sprites on different layers don't collide except with layer 0 sprite show 4,10,10,1 sprite show 2,50,50,2 pause 200 sprite show 17,35,30,0 pause 1000 ' ' test 2 - single sprite overlaps the corner sprite hide 17 sprite hide 2 sprite show 4,-10,-10 pause 1000 ' ' test 3 - one sprite moves and hits another sprite show 4,10,10,7 sprite show 2,100,100,7 pause 200 sprite show 2,50,50,7 pause 1000 ' ' test 4 - collision in contact with edge sprite hide 2 sprite hide 4 sprite show 2,-10,10,7 pause 1000 ' ' test 5 - all layers collide with layer 0 sprite hide 2 sprite show 17, 55,10, 0 sprite show 2, 100, 10 pause 1000 ' ' test 6 - scroll causes collision with edge of screen for layer-0 sprite sprite hide 2 BLIT scrollv 30 pause 1000 ' ' test 7 - scroll causes collision between sprites BLIT scrollv -120 sprite show 4,10,10,7 sprite show 2,110,10,7 blit scrollv 70 pause 1000 end ' ' This routine demonstrates a complete interrogation of collisions ' sub collision local integer i print "Test : ",testrun testrun = testrun +1 if sprite(S) <> 0 then 'collision of individual sprite print "Collision on sprite ",sprite(S) 'sprite(S) returns the sprite that moved to cause the collision process_collision(sprite(S)) print "" else 'collision of one or more sprites caused by background scroll print "Scroll caused a total of ",sprite(C,0)," sprites to have collisions" for i=1 to sprite(C,0) print "Sprite ",sprite(C,0,i) process_collision(sprite(C,0,i)) next i print "" endif end sub sub process_collision(S as integer) local integer i ,j print "Total of ",sprite(C,S)," collisions" 'sprite(C, #n) returns the number of current collisions for i=1 to sprite(C,S) j=sprite(C,S,i) if j=100 then print "collision with left of screen" else if j=101 then print "collision with top of screen" else if j=102 then print "collision with right of screen" else if j=103 then print "collision with bottom of screen" else print "Collision with sprite ",sprite(C,S,i) 'sprite(C, #n, #m) returns details of the mth collision endif next i end sub Results are |
||||
panky![]() Guru ![]() Joined: 02/10/2012 Location: AustraliaPosts: 1114 |
Your programming skills never cease to amaze - can't wait to have a play. Thanks Peter, Doug. ... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it! |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
What is the chance of expanding the scroll function to optionally specify a (rectangular) region to scroll, instead of the entire screen? Visit Vegipete's *Mite Library for cool programs. |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
Totally go along with that suggestion (will then allow different parts of the screen to scroll in different directions (think Frogger) A couple more things to consider regarding scrolling: 1> A flag to indicate 'wrap-around scroll' (as current), OR no-wrap around (think Defender landscape) 2> An 'automatic scroll' so once it is triggered, it will continue until stopped. For this to work, a 'speed' parameter is needed (this would be unbelievably useful - liken it to PWM command (as in once it is set, it keeps going with zero user code required). Possibly worth changing the name of the command too (if 'command token' quantity is not an issue). BackGround SCROLL, PAPER SCROLL, or something similar maybe more meaningful? Just ideas . . . . |
||||
cosmic frog Guru ![]() Joined: 09/02/2012 Location: United KingdomPosts: 302 |
Also would you be able to scroll a picture a lot larger than the actual screen so the screen becomes a window allowing the bigger picture to be moved (scrolled) about underneath it? Dave. |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Time for a reality check The free memory on a MMX in VGA mode is 192k. This represents a frame buffer 640x800x3 with nothing left for the program so it is completely impractical to have a picture "a lot larger than the actual screen". For a TFT screen you can only hold about 1/3 of a frame with the full 468K available so it can't be done at all and the existing code has to read from the screen to use its framebuffer for any manipulations. you can use SPRITE or BLIT. Perhaps BLIT SCROLLH is more obvious? We are very tight on both the maximum number of commands and functions in the firmware so it is not sensible to add additional "names" unless relating to completely different functionality Come on. settick 100,scrollit do loop sub scrollit sprite scrollh 5 end sub So when you scroll you get a blank area that scrolls in that needs overwriting in the program rather than a filled area that scrolls in that need overwriting. Then you also need a complete set of logic for sprites that scroll off the screen Without sprites you can do that now with BLIT x1,y1,x2,y2,w,h With sprites you run into all sorts of issues. What does the firmware do if a user places a sprite half in and half out of a scroll area? Does the firmware then have to keep multiple LIFOs by scroll area etc.? We can all speculate on lots of "nice-to-have" features but the MM firmware has to work in a simple way which can be documented and where the interactions of various commands can be understood. The current version is already difficult enough in this respect, layers, LIFOs etc. AFAIK only one person ever really wrote games for the CMM. If someone seriously starts game coding and runs into particular limitations then that is the time to consider enhancements rather than a "wouldn't it be nice" wishlist now. To do some of the things identified above would require moving the MMX firmware to one of the new 176-pin chips with 32Mb of memory. This is a major undertaking, there are no suitable hardware development platforms available so one wuld need designing before even starting and once ported big chunks of existing code would need re-writing. Old games were targeted at specific hardware platforms and used various types of hardware support for their graphics tricks. The MMX is a generic platform without any specific graphics hardware. It can never support all the various flavours of graphical manipulations. I've tried to provide a basic toolkit of commands that allow games to be written but it must then be up to the developer to work within the limitations of what has been provided. |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2944 |
For things such as scrolling landscapes, yes you would simply 'populate' the blank area with the new 'landscape' after the scroll is done. No need to worry about any sprite logic as sprites will be on layers other than 0. Any sprites that were on Layer 0 simply scroll 'off' the screen. Without sprites you can do that now with BLIT x1,y1,x2,y2,w,h With sprites you run into all sorts of issues. What does the firmware do if a user places a sprite half in and half out of a scroll area? Does the firmware then have to keep multiple LIFOs by scroll area etc.? The BLIT is so slow compared to the SCROLL; so this would be a very useful inclusion. Can the logic simply remain the same as it is now (regarding sprite removal/redrawing); and at the stage where you currently 'scroll' the whole screen, you simply scroll the individual 'rectangles' instead? No issues then with Sprites? You are the 'Graphics Master' and you certainly know your stuff very well. These are just suggestions for 'basic' game playing and I can see them being more than 'nice-to-haves'. I have had a lot of enquiries recently from 8-bit 'followers' regarding MicroMite eXtreme with VGA as people are wanting to re-write retro games. All other points I agree - have already used the SetTick to 'auto scroll' - working ok-ish so will see if I can make 'smoother' by varying the number of pixels scrolled in relation to 'graphic block' size. Trying to make a good 'explosion' graphic now ![]() WW |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Yes, precisely because it has to deal with rectangles that don't cover the whole screen and therefore works pixel by pixel. It could be optimised but that is probably 2 solid days coding/debugging here is the current BLIT code which is called three times per move, once for each colour (R, G, B) void DoBlitVGA(unsigned int *p, int x1, int y1, int x2, int y2, int w, int h) { int x, y, i; unsigned int *p1, *p2; int s1, s2; i = (VGA_HRES_FULL/32); w--; y1+=Y_OFFSET; //start at line VGAoffset y2+=Y_OFFSET; //start at line VGAoffset x1+=X_OFFSET; x2+=X_OFFSET; if(x1 >= x2 && y1 >= y2) { for(y = 0; y < h; y++) { p1 = &p[(y1 + y) * i + x1/32]; p2 = &p[(y2 + y) * i + x2/32]; s1 = 31 - (x1 & 0x1f); s2 = 31 - (x2 & 0x1f); for(x = 0; x <= w; x++) { *p2 = (*p2 & (~(1 << s2))) | (((*p1 >> s1) & 1) << s2); if(--s1 < 0) { p1++; s1 = 31; } if(--s2 < 0) { p2++; s2 = 31; } } } return; } if(x1 >= x2 && y1 < y2) { for(y = h - 1; y >= 0; y--) { p1 = &p[(y1 + y) * i + x1/32]; p2 = &p[(y2 + y) * i + x2/32]; s1 = 31 - (x1 & 0x1f); s2 = 31 - (x2 & 0x1f); for(x = 0; x <= w; x++) { *p2 = (*p2 & (~(1 << s2))) | (((*p1 >> s1) & 1) << s2); if(--s1 < 0) { p1++; s1 = 31; } if(--s2 < 0) { p2++; s2 = 31; } } } return; } if(x1 < x2 && y1 >= y2) { for(y = 0; y < h; y++) { p1 = &p[(y1 + y) * i + (x1 + w)/32]; p2 = &p[(y2 + y) * i + (x2 + w)/32]; s1 = 31 - ((x1 + w) & 0x1f); s2 = 31 - ((x2 + w) & 0x1f); for(x = w ; x >= 0; x--) { *p2 = (*p2 & (~(1 << s2))) | (((*p1 >> s1) & 1) << s2); if(++s1 > 31) { p1--; s1 = 0; } if(++s2 > 31) { p2--; s2 = 0; } } } return; } if(x1 < x2 && y1 < y2) { for(y = h - 1; y >= 0; y--) { p1 = &p[(y1 + y) * i + (x1 + w)/32]; p2 = &p[(y2 + y) * i + (x2 + w)/32]; s1 = 31 - ((x1 + w) & 0x1f); s2 = 31 - ((x2 + w) & 0x1f); for(x = w ; x >= 0; x--) { *p2 = (*p2 & (~(1 << s2))) | (((*p1 >> s1) & 1) << s2); if(++s1 > 31) { p1--; s1 = 0; } if(++s2 > 31) { p2--; s2 = 0; } } } } } |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
Without sprites you can do that now with BLIT x1,y1,x2,y2,w,h With sprites you run into all sorts of issues. What does the firmware do if a user places a sprite half in and half out of a scroll area? Does the firmware then have to keep multiple LIFOs by scroll area etc.? Not the firmware's problem, although my impression from your documentation is that it would be handled fine already. If I read it correctly, your firmware erases all sprites, scrolls the entire screen the requested direction and distance, then redraws all the sprites again. Layer 0 sprites have their coordinates changed according to the scroll, all other layer sprites do not change position. My query was merely whether the middle part of the process, scrolling, could optionally be applied to a smaller portion of the screen. WhiteWizzard's Frogger example suggests that the middle section might be repeated for more than one region to avoid the overhead of erasing and redrawing each sprite for each scroll. This gives rise to the following command sequence: sprite hide ALL BLIT scroll dx,dy,region 'HEY! Vegipete changed the format! Bad Vegipete! 'more BLITs if required sprite show ALL 'Note: only redraws sprites hidden by the previous hide ALL Sprites on layer 0 have their locations altered according to the BLIT scrolls, no matter where they are on screen. It's up to the programmer to deal with that. ----------- For reference, I started writing (well, porting) a game for the Maximite but at the time, sprites hadn't developed enough to make it simple enough, so I stopped. These latest developments look fantastic, motivating me to get back at it. I couldn't find an MZ100 PCB around here with a VGA port so I've started drawing one so that I can build something to play with. And once I get an MMX100 with VGA running, I'll port my Break-Out version to it... Visit Vegipete's *Mite Library for cool programs. |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |