Yaron Nir Posted May 6, 2019 Share Posted May 6, 2019 Hi All, Is there a good reference code i can review that does software sprites (move objects on screen in antic mode 4 or E)? any help would be appreciated. cheers Quote Link to comment Share on other sites More sharing options...
danwinslow Posted May 6, 2019 Share Posted May 6, 2019 I'm pretty sure that any code you find in C for that kind of thing would actually be mostly inline assembler. Also, 4 is a character mode, so smooth movement on a pixel basis is difficult but just moving the character is very easy, since the computer takes care of rendering the character bit pattern. I don't have any source handy, but I've experimented with mode E a bit. My efforts are nowhere near expert but I will say that lookup tables of various sorts (screen line address, mult and divide, color masks, etc.) are a requirement. Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted May 6, 2019 Author Share Posted May 6, 2019 Well I am more interested in soft sprites in antic mode E Can you share a bit more about this? Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted May 7, 2019 Author Share Posted May 7, 2019 has any1 here done software sprites using CC65? Quote Link to comment Share on other sites More sharing options...
baktra Posted May 7, 2019 Share Posted May 7, 2019 Hello, 1. It is likely you will have to code in assembler, because of the speed. 2. There are certain general principles related to sprites, but you will have to tailor your solution to Atari. But what's written there for VGA is mostly valid for any bitmap mode on Atari.\ This thread can provide some insights too. And you can get inspired by X:8 if you decide to go with character sprites. 1 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted May 7, 2019 Author Share Posted May 7, 2019 Thanks baktra, will have a look Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted May 7, 2019 Author Share Posted May 7, 2019 i am writing a test app in CC65 code to demo software sprite moving on screen using shifting. once done i will post it here, hopefully get tones of rejections, improve my code and repost 1 Quote Link to comment Share on other sites More sharing options...
Yaron Nir Posted May 8, 2019 Author Share Posted May 8, 2019 (edited) ok, After quite sometime I was able to come up with a sample app of software sprites in CC65. the sample is a simple ball moving on mode E screen using the joystick. This maybe a bit long, but I hope it will help those who are seeking: 1. Learn how to work with Mode E 2. Create a display list of mode E with 2 LMS instructions 3. Learn about software sprites basics and how to develop them in CC65 let's start..... Mode E (The bitmap mode) Looking at atari spec, mode E gives a resolution of 160x192. it is referred as bitmap mode and each line is exactly 1 scanline. If you want to completely build a mode E by yourself that will take up the entire screen, it will take up to 192 scanlines in your display list. Problem or technical challange in that is that screen can't pass the 4K boundary. This mode takes up to 7680 bytes which are more then the 4K boundary. The solution will be to use 2 LMS instructions in the display list. This means that the display list will be divided into 2 parts. The first part will set the first 96 scanlines (192/2=96) of the entire screen and the second part will contain the other 96 scanlines. Mode E - The bit pairs Similar to mode 4 (char mode), mode E also works with pair bits. If a byte is presented by 8 bits i.e. 00000000 then in mode E this byte will actually present up to 4 pixels "lit" on screen (byte is 8 bits divided by 2 as bits come in pars = 4 pixels to be displayed). 1 scanline provides 40 bytes, so the total pixels that can be displayed in 1 scanline is 40 * 4 = 160. The bit pairs are corresponding to the playfield colors of atari: 00 COLBAK 01 COLPF0 10 COLPF1 11 COLPF2 In decimal the values for bit pairs are : 0,1,2,3 In order to "lit" 3 out of 4 pixels in 1 byte of screen memory with the color blue (let's say that COLPF0 is blue) all you need to do is write a byte that holds the bitpairs for the lit pixels with the value of COLPF0: The byte will look like this: 01010100 01 is the pair value for COLPF0 (blue in this example) and that bitpair appears 3 times in the byte sequence(010101) The last bitpair is 00 which is the value of COLBAK and in our case this pixel will not be "lit up". Define mode E display list consider the following display list: unsigned char DisplayList[] = { DL_BLK8, // 0 DL_BLK8, // 1 DL_BLK8, // 2 DL_LMS(DL_MAP160x1x4), // 3 0, // 4 0, // 5 // 6 DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, // 13 DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, // 101 DL_LMS(DL_MAP160x1x4), 0, // 102 0, // 103 DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, // 110 DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4,DL_MAP160x1x4, // 198 DL_JVB, 0, // 199 0 // 200 }; The above display list is set to create Mode E (used by the value: DL_MAP160x1x4) on screen. It uses as described above 2 LMS instructions to point to 2 memory locations that will hold the enitre screen bytes. The zeros in the display list above must be filled in with the proper addresses. Basically we need to fill in the following 3 memory address: 1. Display list address (where the JVB (jvb is jump vertical blank instruction) will go to) 2. LMS 1 - first memory address 2. LMS 2 - second memory address Here is a code snippet to fill in the memory addresses respectivly: void setupDisplayList(void) { DisplayList[4] = SCREEN_MEM%256; DisplayList[5] = SCREEN_MEM>>8; DisplayList[102] = SCREEN_MEM_BOTTOM %256; DisplayList[103] = SCREEN_MEM_BOTTOM >>8; DisplayList[199] = DISPLAY_LIST_MEM%256; DisplayList[200] = DISPLAY_LIST_MEM>>8; memcpy((void*)DISPLAY_LIST_MEM,&DisplayList,sizeof(DisplayList)); OS.sdlst = (void*)DISPLAY_LIST_MEM; bzero((void*)SCREEN_MEM,7680); } The modulu (%) give the hi-byte part and the >>8 (which is divide by 256) gives the lo-part. Last line clears the entire screen (screen size is 7680 bytes) Software Sprite So, what exactly does the term software sprite refers to? Well, it is basically anything that can be drawn on screen and be moved arround. This is done on the software layer in oppose to things on screen donw in the HW layer such as player missile graphics). When thinking about software sprites you need to consider the following aspects: 1. Memory allocations - these type of developing can be very of high memory consumption 2. Performance - dealing with a lot of objects on screen can slow down the performance as everything is done on the software side and consume lots of memory. 3. Timing - drawing on the screen memory must be done at the right time to avoid conflicts and issues. In the example I have prepared, I draw a ball using bitpair '11' which is represent by COLPF2. This is the ball represented by the bit pairs: 00000000 00111100 11111111 11111111 00111100 00000000 It is basically a byte array in memory which looks like this: unsigned char ball[6] = { 0,60,255,255,60,0 }; or in hex unsigned char ball[6] = {0x00, 0x3C,0xFF,0xFF,0x3C,0x00} Draw on screen To draw this ball on screen mode E, all I have to do is to copy the byte array into screen memory where-ever I want to show the ball, for example: for (i=0;i<6;i++) { POKE(SCR_MEM+21+40*i,array[i]); } Note that a CC65 memcpy routine can be used to replace the for loop. Moving the ball - shifting the frame Now we want our ball to move, let's say by the joystick corresponding movements. how do we move the ball? One possibility would be to draw the ball one byte next to the byte that it was already drawn in and erase the ball from the original byte it was drawn. This could work, but the movement will not be as smooth as we desire and will look jerky and inconsistent. The solution is to shift the ball within the byte itself so the "lit pixels" will move left or right. Shifting the pixels inside the array will cause parts of the ball to disappear. in order to solve that we will need another byte next to the one the ball is drawn in that will hold the "mirror" shift of the first byte. The shifted frames should be resulted from the orginial ball frame. We shift the orginial ball in the left byte and in the right byte and then on the shifted frames we do the second third shifts. To understand better, let's see how the shifting will look like: Ball original on the left byte: Left byte Right byte 00000000 00000000 00111100 00000000 11111111 00000000 11111111 00000000 00111100 00000000 00000000 00000000 (ball appears on screen, on the left byte) Ball shift once: Left byte Right byte 00000000 00000000 00001111 00000000 00111111 11000000 00111111 11000000 00001111 00000000 00000000 00000000 (ball appear on screen, now moved 1 pixel to the right, it is shown in the 2 bytes the left and right) Ball shift twice: Left byte Right byte 00000000 00000000 00000011 11000000 00001111 11110000 00001111 11110000 00000011 11000000 00000000 00000000 (ball appear on screen, now moved another pixel to the right, it is shown in the 2 bytes the left and right) Ball shift 3rd time: Left byte Right byte 00000000 00000000 00000000 11110000 00000011 11111100 00000011 11111100 00000000 00111100 00000000 00000000 (ball appear on screen, now moved another pixel to the right, it is shown in the 2 bytes the left and right) Ball original on the right byte: Left byte Right byte 00000000 00000000 00000000 00111100 00000000 11111111 00000000 11111111 00000000 00111100 00000000 00000000 (ball appears on screen, basically now on the right byte) at this point if the ball continues to move to the right, we will need to do the same but now the right byte will become the left byte and the byte next to the right will become the right byte and so on...... to move the ball to the left, we will do the same thing but in reverse to move the ball up or down we will need to move it 40 bytes above or below the current byte. the ball starts with leading zeros and end with trailing zeros and that is not to leave trails while it moves up or down. Pre-shifting to increase performane and to make usage of atari fast memory operations we can "prepare" the ball shifted images in memory and then just copy each shifted frame to the screen whenever is needed. this will be done very fast. This technique is called "pre-shifting", which means, we prepare the shifting frames upfront. Here is a code snippet on how to prepare the shifted images of the ball: void createShiftedFrames() { for (i=0;i<HEIGHT;i++) { spriteShifted[2][i] = spriteShifted[0][i] >> 2; // left byte shift >> 2 spriteShifted[3][i] = spriteShifted[0][i] << 6; // right byte shift << 6 spriteShifted[4][i] = spriteShifted[0][i] >> 4; // left byte shift >> 4 spriteShifted[5][i] = spriteShifted[0][i] << 4; // right byte shift << 4 spriteShifted[6][i] = spriteShifted[0][i] >> 6; // left byte shift >> 6 spriteShifted[7][i] = spriteShifted[0][i] << 2; // right byte shift << 2 } } Wait for VBLANK it is most common to wait until a vblank occurs and only then do the screen calc and drawing. A good reference for vblank can be found in books like de-re-atari. I used a common trick (learned here in AA forums) to determine that a VBLANK already occured. It is basically using the atari clock at address 0x20: void waitForVBLANK(void) { currClockFrame = OS.rtclok[2]; while (OS.rtclok[2] == currClockFrame); } When this routine finishes, it is almost definately that a VBLANK has occured. some might aruge and say that this trick is not 100% accurate and that there will be situations where this will fail. I haven't encountered with any and it seems to be working perfectly well for me. plus establishing a true VBI routine in CC65 is not easy as these 2-line routine code bove Differentiating joy movement and drawing A good programming practice is to handle the joystick movement and just update some flags, then have a drawing routine that draw the current state based on the current flags. code snippet for main routine that can looks something like this: waitForVBLANK(); handleJoystick(); updateBall(); The joystick routine will determine if joystick was moved up,down,left,right and will set positions on screen accordingly. updateBall will make sure the proper frame is drawn on screen based on those positions updates. Wrapping up I know this was a bit long, but I hope it was useful to someone. The code attached is not optimal and can be refactored for better results, but I think it that for this purpose it is just fine. At this point I would like to say special thank you to my good friend popmilo(Vladimir J.) - you are my true guru Any comments notes remakrs dislikes or other share thoughts would be happily accepted Attached is xex and a zip containing the code EDIT: I've edited the post as i found many language mistakes and some phrasing i didn't like. hope it came out better.... soft sprites.rar softsprite.xex Edited May 8, 2019 by Yaron Nir 8 Quote Link to comment Share on other sites More sharing options...
popmilo Posted May 8, 2019 Share Posted May 8, 2019 Nice explanation Yaron ! Please carry on developing this tech. I like that you're trying to do it in pure cc65. 3 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.