Primordial Ooze Posted December 13, 2011 Share Posted December 13, 2011 (edited) Hi, I just finished adding the crosshair sprite into the game and am having difficulties. For some reason when i added the code for the crosshairs, my scanline count went up to 343 which is way too high. I figured it had something to do with the lightgun detection code and disabled the call to the routine, except my scanline count remained the same at 343. I can't see how displaying 2 sprites will cause the scanline count to jump to 343. I have attached the latest version of the source code for reference. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Edited December 13, 2011 by Primordial Ooze Quote Link to comment Share on other sites More sharing options...
Brian O Posted December 13, 2011 Share Posted December 13, 2011 Hi, I just finished adding the crosshair sprite into the game and am having difficulties. For some reason when i added the code for the crosshairs, my scanline count went up to 343 which is way too high. I figured it had something to do with the lightgun detection code and disabled the call to the routine, except my scanline count remained the same at 343. I can't see how displaying 2 sprites will cause the scanline count to jump to 343. I have attached the latest version of the source code for reference. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze You forgot to add your attachment. -B Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 13, 2011 Author Share Posted December 13, 2011 Hi, I just finished adding the crosshair sprite into the game and am having difficulties. For some reason when i added the code for the crosshairs, my scanline count went up to 343 which is way too high. I figured it had something to do with the lightgun detection code and disabled the call to the routine, except my scanline count remained the same at 343. I can't see how displaying 2 sprites will cause the scanline count to jump to 343. I have attached the latest version of the source code for reference. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze You forgot to add your attachment. -B Sorry, it's been attached now. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted December 14, 2011 Share Posted December 14, 2011 The first thing I would do is change the way you're checking the timer. You're checking to see when the timer has reached zero, but if your kernel takes too long, the timer may pass zero before you get around to checking it. In my opinion the best way to check the timer is to check whether or not the timer interrupt flag has been set: wait_loop lda TIMINT ; the timer interrupt flag is bit 7 of this address bpl wait_loop ; if bit 7 is still 0, the timer hasn't finished counting down You can still exceed the timer's period, but if you do you'll know right away. On the other hand, if you're checking for the timer being 0 and it's already hit 0 and wrapped around, you'll have to wait for it to count down to 0 again, and hope your loop catches it the second time. You may need to adjust the values you're setting the timer to, since the timer interrupt flag doesn't get set until the timer rolls from 0 to 255, so you'll be waiting for at least 1 more count. Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted December 14, 2011 Share Posted December 14, 2011 Also, it looks like you're using the y register to draw 191 lines, but you're also setting the timer for 199 lines. Is this all within the same frame, or on two different frames? Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 The first thing I would do is change the way you're checking the timer. You're checking to see when the timer has reached zero, but if your kernel takes too long, the timer may pass zero before you get around to checking it. In my opinion the best way to check the timer is to check whether or not the timer interrupt flag has been set: wait_loop lda TIMINT ; the timer interrupt flag is bit 7 of this address bpl wait_loop ; if bit 7 is still 0, the timer hasn't finished counting down You can still exceed the timer's period, but if you do you'll know right away. On the other hand, if you're checking for the timer being 0 and it's already hit 0 and wrapped around, you'll have to wait for it to count down to 0 again, and hope your loop catches it the second time. You may need to adjust the values you're setting the timer to, since the timer interrupt flag doesn't get set until the timer rolls from 0 to 255, so you'll be waiting for at least 1 more count. I changed all the loops that used the INTIM register to bpl like you said and now all i'm getting is a black screen. Also, it looks like you're using the y register to draw 191 lines, but you're also setting the timer for 199 lines. Is this all within the same frame, or on two different frames? The 199 lines timer code is related to the lightgun routine posted by Eckhard Stolberg and not my code. I also changed that code to 191 lines and still am getting a black screen. It seems like we are going backwards instead of forwards. I have attached the modified source code for reference. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
Eckhard Stolberg Posted December 14, 2011 Share Posted December 14, 2011 Seems like you forgot to attach the new source code again. In the timer loops you also changed the INTIM to TIMINT (which is a different register), right? Because otherwise the bpl would end the loop immediately if the value loaded into the timer was bigger than 127 (which is the case in the lightgun position test). Anyway, the reason why you are generating too many scanlines has nothing to do with the timers. It's simply because your ScanLoop takes more than 76 cycles in some cases. That means some instructions will be executed in a second line. These extra lines add up quite a bit in your program. If you make sure that your ScanLoop never uses more than 76 cycles between (and including) two "sta WSYNC"s in every possible case, then your display should be fine. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 (edited) Seems like you forgot to attach the new source code again. Sorry i was very busy today doing a million things at a time! It's attached this time! In the timer loops you also changed the INTIM to TIMINT (which is a different register), right? Because otherwise the bpl would end the loop immediately if the value loaded into the timer was bigger than 127 (which is the case in the lightgun position test). Nope, no wonder why the screen went all black... EDIT:Fixed Anyway, the reason why you are generating too many scanlines has nothing to do with the timers. It's simply because your ScanLoop takes more than 76 cycles in some cases. That means some instructions will be executed in a second line. These extra lines add up quite a bit in your program. If you make sure that your ScanLoop never uses more than 76 cycles between (and including) two "sta WSYNC"s in every possible case, then your display should be fine. How exactly would i do that cause at this point I'm completely stumped. All i'm doing is displaying 2 sprites and a playfield nothing too complex. Are you saying if i add 2 WSYNC's will solve my problem cause i'm been at it for a bit now. Edited December 14, 2011 by Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 How exactly would i do that cause at this point I'm completely stumped. All i'm doing is displaying 2 sprites and a playfield nothing too complex. Are you saying if i add 2 WSYNC's will solve my problem cause i'm been at it for a bit now. If you can't fit the work required into one scan line you need to create a 2, 3, or 4 line kernel (as required) for your game. You need to work out how many cycles the code for each scan line will take. If you randomly add WSYNCs you won't end up with a robust kernel. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 How exactly would i do that cause at this point I'm completely stumped. All i'm doing is displaying 2 sprites and a playfield nothing too complex. Are you saying if i add 2 WSYNC's will solve my problem cause i'm been at it for a bit now. If you can't fit the work required into one scan line you need to create a 2, 3, or 4 line kernel (as required) for your game. You need to work out how many cycles the code for each scan line will take. If you randomly add WSYNCs you won't end up with a robust kernel. Ok, so where exactly do i start? Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 In your last posted source code start at line 483 and look up how many CPU cycles each instruction takes. Make sure you take into account page crossings and branches taken or not taken. When you get to line 582 you can work out where to put the WSYNCs (or not if you can break the code into chunks of 76 cycles). You might also find Andrew Davie's tutorials on sprite handling useful too (sessions 21 to 23). Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 In your last posted source code start at line 483 and look up how many CPU cycles each instruction takes. Make sure you take into account page crossings and branches taken or not taken. When you get to line 582 you can work out where to put the WSYNCs (or not if you can break the code into chunks of 76 cycles). You might also find Andrew Davie's tutorials on sprite handling useful too (sessions 21 to 23). I was thinking about upgrading my kernel into a 2 line kernel as stated above you said adding WSYNC randomly will make my kernel unrobust. So how do i start in terms of creating a 2 line kernel? Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 So how do i start in terms of creating a 2 line kernel? By working out the CPU cycles for the work required in the game. There is no other way on the 2600. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 (edited) So how do i start in terms of creating a 2 line kernel? By working out the CPU cycles for the work required in the game. There is no other way on the 2600. So what your saying is a 2/3/4 line kernal is simply additional WSYNC's in the correct cycle count places? Sincerely, Primordial Ooze EDIT: I added an additional WSYNC where the 2nd line should start along with a comment on it and it still doesn't seem to solve my problem. I have attached the latest revision of my source code. Edited December 14, 2011 by Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 So what your saying is a 2/3/4 line kernal is simply additional WSYNC's in the correct cycle count places? No! If your kernel takes exactly 76 cycles per scan line you don't need a WSYNC. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 14, 2011 Author Share Posted December 14, 2011 So what your saying is a 2/3/4 line kernal is simply additional WSYNC's in the correct cycle count places? No! If your kernel takes exactly 76 cycles per scan line you don't need a WSYNC. Ok well i added the additional WSYNC as per your instructions and it didn't seem to make a difference. Could please take a look and see what I'm doing wrong as after trying to figure it out by myself and posting the source code a few time, i really seem to be between a rock and a hard place. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 How about adding the CPU cycles per instruction in the comments? Then you'll have an exact idea about the amount of work the CPU has to do. Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted December 15, 2011 Share Posted December 15, 2011 A 2-line (or 3-line, or 4-line, etc.) kernel is a lot like a 1-line kernel, but your loop takes more cycles. Whether or not you need any WSYNC instructions depends on how many cycles the loop contains and how precisely-timed it is. Thus... If your loop takes less than 76 cycles-- or really, less than 73 cycles-- then include 1 WSYNC to fill out the line and it will be a 1-line kernel. Don't forget to factor in the cycles needed for decrementing or incrementing your loop counter and testing it, plus the cycles needed to loop back, plus the 3 cycles for strobing WSYNC. Also, keep in mind that branches take 2 cycles if they aren't taken, or 3 cycles if they're taken, or 4 cycles if they're taken but the target address is on a different 256-byte page. If your loop takes exactly 76 cycles-- including the overhead cycles for updating the loop counter, checking it, and branching-- then you can omit the WSYNC. This is still a 1-line kernel. If your loop takes more than 76 cycles but less than 152 cycles (or less than 149 cycles), then include at least 1 WSYNC and it will be a 2-line kernel. You may not need to use 2 WSYNCs if you don't care so much about the timing on the second line. But if you need the instructions on both lines to be lined up so they always execute at specific points on each line, then include 2 WSYNCs. If your loop takes exactly 152 cycles-- excluding any WSYNCs but including all the extra overhead stuff-- then you don't need any WSYNCs. And so on for a 3-line kernel, 4-line kernel, etc. Of course, the number of times you execute the loop will depend on how many lines it draws-- like 192 times for a 1-line kernel, but only 96 times for a 2-line kernel, or 64 times for a 3-line kernel, etc. If you do go with a 2-line kernel, or 3-line kernel, etc., you might need to use a separate variable or register as an index for the graphics. For example, a simple 2-line kernel might look something like this: ldy #0 ; index for the graphics ldx #96 ; counter for the loop loop lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC ; finish off the first line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC dex ; decrement the loop counter bne loop ; not done yet? then loop again Since updating the loop counter, checking it, and looping back take time, you might want to put the first WSYNC at the beginning of the loop so the instructions for the first line are always lined up exactly the same as the instructions for the second line... ldy #0 ; index for the graphics ldx #96 ; counter for the loop loop sta WSYNC ; finish the current line and start a new line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC ; now finish this line and start another new line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index dex ; decrement the loop counter bne loop ; not done yet? then loop again Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 15, 2011 Author Share Posted December 15, 2011 A 2-line (or 3-line, or 4-line, etc.) kernel is a lot like a 1-line kernel, but your loop takes more cycles. Whether or not you need any WSYNC instructions depends on how many cycles the loop contains and how precisely-timed it is. Thus... If your loop takes less than 76 cycles-- or really, less than 73 cycles-- then include 1 WSYNC to fill out the line and it will be a 1-line kernel. Don't forget to factor in the cycles needed for decrementing or incrementing your loop counter and testing it, plus the cycles needed to loop back, plus the 3 cycles for strobing WSYNC. Also, keep in mind that branches take 2 cycles if they aren't taken, or 3 cycles if they're taken, or 4 cycles if they're taken but the target address is on a different 256-byte page. If your loop takes exactly 76 cycles-- including the overhead cycles for updating the loop counter, checking it, and branching-- then you can omit the WSYNC. This is still a 1-line kernel. If your loop takes more than 76 cycles but less than 152 cycles (or less than 149 cycles), then include at least 1 WSYNC and it will be a 2-line kernel. You may not need to use 2 WSYNCs if you don't care so much about the timing on the second line. But if you need the instructions on both lines to be lined up so they always execute at specific points on each line, then include 2 WSYNCs. If your loop takes exactly 152 cycles-- excluding any WSYNCs but including all the extra overhead stuff-- then you don't need any WSYNCs. And so on for a 3-line kernel, 4-line kernel, etc. Of course, the number of times you execute the loop will depend on how many lines it draws-- like 192 times for a 1-line kernel, but only 96 times for a 2-line kernel, or 64 times for a 3-line kernel, etc. If you do go with a 2-line kernel, or 3-line kernel, etc., you might need to use a separate variable or register as an index for the graphics. For example, a simple 2-line kernel might look something like this: ldy #0 ; index for the graphics ldx #96 ; counter for the loop loop lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC ; finish off the first line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC dex ; decrement the loop counter bne loop ; not done yet? then loop again Since updating the loop counter, checking it, and looping back take time, you might want to put the first WSYNC at the beginning of the loop so the instructions for the first line are always lined up exactly the same as the instructions for the second line... ldy #0 ; index for the graphics ldx #96 ; counter for the loop loop sta WSYNC ; finish the current line and start a new line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index sta WSYNC ; now finish this line and start another new line lda (graphics_vector),y sta GRP0 ; do some more stuff iny ; increment the graphics index dex ; decrement the loop counter bne loop ; not done yet? then loop again Thanks, i managed to do exactly what you said and got a stable scanline count. What i would like to know is if i did it correctly because for some reason, my player0 is slightly messed up having pixels where there shouldn't be(see beak) and colors that shouldn't be(beak is black, back of head color is incorrect). Once we manage to get these bugs fixed i will test it on a real atari and let you know the results. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted December 16, 2011 Share Posted December 16, 2011 Thanks, i managed to do exactly what you said and got a stable scanline count. What i would like to know is if i did it correctly because for some reason, my player0 is slightly messed up having pixels where there shouldn't be(see beak) and colors that shouldn't be(beak is black, back of head color is incorrect). Once we manage to get these bugs fixed i will test it on a real atari and let you know the results. Any assistance would be greatly appreciated. When I run it in Stella and step through the program in the debugger, it looks like GRP0 is being set before the scanning beams reach the duck, but COLUP0 is being set after the scanning beams have passed the duck, so it's probably related to timing more than anything. I don't know if the duck will always be at the same spot on the screen or will be moving around on the screen. If it will always be at the same spot then you may just need to update the x index before you load the colors, or rearrange the color table a bit-- in other words, when you set GRP0 to the left of the duck, you're setting the graphics for that line, but when you set COLUP0 to the right of the duck, you're setting the color for the *next* line, if you see what I mean. Otherwise, if the duck will be moving around on the screen, you'll want to see if you can reorder some of the instructions so GRP0 and COLUP0 are always set during the HBLANK period. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 16, 2011 Author Share Posted December 16, 2011 Thanks, i managed to do exactly what you said and got a stable scanline count. What i would like to know is if i did it correctly because for some reason, my player0 is slightly messed up having pixels where there shouldn't be(see beak) and colors that shouldn't be(beak is black, back of head color is incorrect). Once we manage to get these bugs fixed i will test it on a real atari and let you know the results. Any assistance would be greatly appreciated. When I run it in Stella and step through the program in the debugger, it looks like GRP0 is being set before the scanning beams reach the duck, but COLUP0 is being set after the scanning beams have passed the duck, so it's probably related to timing more than anything. I don't know if the duck will always be at the same spot on the screen or will be moving around on the screen. If it will always be at the same spot then you may just need to update the x index before you load the colors, or rearrange the color table a bit-- in other words, when you set GRP0 to the left of the duck, you're setting the graphics for that line, but when you set COLUP0 to the right of the duck, you're setting the color for the *next* line, if you see what I mean. Otherwise, if the duck will be moving around on the screen, you'll want to see if you can reorder some of the instructions so GRP0 and COLUP0 are always set during the HBLANK period. Ok, where exactly in code does the horizontal blank period occur? Is it the part of the code where the controller checking and game logic occurs considered the horizontal blank period? Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 16, 2011 Share Posted December 16, 2011 This is a useful image when 2600 coding :- http://www.atariage.com/forums/topic/27192-session-6-tv-timing-diagram/ A refresher of the tutorials before that would probably help too. 1 Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 16, 2011 Author Share Posted December 16, 2011 This is a useful image when 2600 coding :- http://www.atariage....timing-diagram/ A refresher of the tutorials before that would probably help too. Thanks for that image. I read the tutorials and know how a game image is drawn on a tv screen. However i'm having a bit of difficulty trying to associate the timing with my code as my project is getting quite complex! Anyway based on the image and the following quote Session 2: Horizontal Blank when the beam is resetting to the left of the next scanline My guess is the code should be between dey ; subtract one off the line counter thingy bne ScanLoop or ScanLoop sta WSYNC Please let me know if i'm correct and advise me which one is the correct answer. Any assistance would be greatly appreciated. Sincerely, Primordial Ooze Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 16, 2011 Share Posted December 16, 2011 You really need to start working out the CPU cycles each instruction takes and putting it in the comments then you'll have a much better idea about when the registers are updated to the relative position of the scan line. Quote Link to comment Share on other sites More sharing options...
Primordial Ooze Posted December 16, 2011 Author Share Posted December 16, 2011 You really need to start working out the CPU cycles each instruction takes and putting it in the comments then you'll have a much better idea about when the registers are updated to the relative position of the scan line. That seems like a lot of work. This project is starting to get the best of me and i may end up abandoning it. Isn't there another way besides counting cycles? Sincerely, Primordial Ooze 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.