Jump to content
IGNORED

343 Scanlines Why?


Primordial Ooze

Recommended Posts

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 by Primordial Ooze
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Seems like you forgot to attach the new source code again.

Sorry i was very busy today doing a million things at a time! :woozy: 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. :skull:

Edited by Primordial Ooze
Link to comment
Share on other sites

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. :skull:

 

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.

Link to comment
Share on other sites

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. :skull:

 

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

Link to comment
Share on other sites

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).

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 by Primordial Ooze
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

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...