Jump to content
jrok

"no_blank_lines" and scanline count

Recommended Posts

I'm looking for a little guidance to understanding timing in Atari. In the attached file, I'm getting a scanline count that jumps between 261 and 262 when I include the "no_blank_lines" option.

 

From what I can grasp, if the number of scanlines goes over 262, Atari is unable to execute all of the program's operations before the next screen is drawn, and that the screen will roll. But what does it mean when the scanline count drops below 262? In cases where I've gone over, I've been able to resolve it by optimizing my routines. But how would one go about troubleshooting a scanline count that is dropping below 262?

 

Thanks in advance,

Jarod.

Shooter.bas

Shooter.bin

Share this post


Link to post
Share on other sites
I'm looking for a little guidance to understanding timing in Atari. In the attached file, I'm getting a scanline count that jumps between 261 and 262 when I include the "no_blank_lines" option.

 

From what I can grasp, if the number of scanlines goes over 262, Atari is unable to execute all of the program's operations before the next screen is drawn, and that the screen will roll. But what does it mean when the scanline count drops below 262? In cases where I've gone over, I've been able to resolve it by optimizing my routines. But how would one go about troubleshooting a scanline count that is dropping below 262?

 

Thanks in advance,

Jarod.

That's weird. bB uses the Atari's interval timer to keep track of when it needs to perform the vertical sync routine or start drawing the active portion of the screen, so you'd think the scan line count should be the same regardless of which kernel options you've selected. However, setting the timer to a particular value-- say, LDA #35, STA TIM64T-- can give slightly different results depending on (1) how far into the scan line the Atari happens to be when the timer is set, and (2) how far into the scan line the Atari happens to be when it detects that the timer has finished counting down. It's possible that when the no_blank_lines option is used, it's causing a slight difference in the timing of the first factor. (The timing of the second factor normally varies a great deal depending on your code.) If that's the case, it might be necessary to nudge the value that bB's setting the timer to, in order to get a steady line count regardless of whether the no_blank_lines option is used. I'll check it out and see.

 

By the way, what are the dates on your std_kernel.asm and std_overscan.asm files? I want to make sure I check out the ones you're using.

 

Michael

Share this post


Link to post
Share on other sites
From what I can grasp, if the number of scanlines goes over 262, Atari is unable to execute all of the program's operations before the next screen is drawn, and that the screen will roll.

That's not worded quite right. You make it sound like the vertical sync will occur at the same time regardless, and that if the program tries to do too much between vertical syncs, it won't have time to do everything, so some of the things don't get done.

 

What actually happens is that the Atari will do everything you tell it to do, regardless of whether it takes 10 scan lines or 100 scan lines to perform all of the commands.

 

After the Atari finishes performing your code, it falls into a little loop that keeps checking the timer's value (or maybe the value of the timer interrupt flag) until it's time to do the vertical sync.

 

When your program tries to do too much and takes too long, usually the timer has already finished counting down to 0, so the vertical sync will occur later than usual. It could be just 1 scan line later, or it could be a great many scan lines later, depending on how the timer got set and how that little "wait loop" is constructed.

 

Anyway, the screen rolls because the TV ends up drawing too many scan lines before the Atari tells it to do a vertical sync. The screen can also roll if the TV draws too few scan lines before the Atari tells it to do a vertical sync.

 

Michael

  • Like 1

Share this post


Link to post
Share on other sites
That's weird. bB uses the Atari's interval timer to keep track of when it needs to perform the vertical sync routine or start drawing the active portion of the screen, so you'd think the scan line count should be the same regardless of which kernel options you've selected. However, setting the timer to a particular value-- say, LDA #35, STA TIM64T-- can give slightly different results depending on (1) how far into the scan line the Atari happens to be when the timer is set, and (2) how far into the scan line the Atari happens to be when it detects that the timer has finished counting down. It's possible that when the no_blank_lines option is used, it's causing a slight difference in the timing of the first factor. (The timing of the second factor normally varies a great deal depending on your code.) If that's the case, it might be necessary to nudge the value that bB's setting the timer to, in order to get a steady line count regardless of whether the no_blank_lines option is used. I'll check it out and see.

 

By the way, what are the dates on your std_kernel.asm and std_overscan.asm files? I want to make sure I check out the ones you're using.

 

Michael

 

Thanks, man. My files only give me the local installation dates, and I don't see and versioning information in their comments. I've attached both of them below.

std_kernel_and_overscan.zip

Share this post


Link to post
Share on other sites
From what I can grasp, if the number of scanlines goes over 262, Atari is unable to execute all of the program's operations before the next screen is drawn, and that the screen will roll.

That's not worded quite right. You make it sound like the vertical sync will occur at the same time regardless, and that if the program tries to do too much between vertical syncs, it won't have time to do everything, so some of the things don't get done.

 

What actually happens is that the Atari will do everything you tell it to do, regardless of whether it takes 10 scan lines or 100 scan lines to perform all of the commands.

 

After the Atari finishes performing your code, it falls into a little loop that keeps checking the timer's value (or maybe the value of the timer interrupt flag) until it's time to do the vertical sync.

 

When your program tries to do too much and takes too long, usually the timer has already finished counting down to 0, so the vertical sync will occur later than usual. It could be just 1 scan line later, or it could be a great many scan lines later, depending on how the timer got set and how that little "wait loop" is constructed.

 

Anyway, the screen rolls because the TV ends up drawing too many scan lines before the Atari tells it to do a vertical sync. The screen can also roll if the TV draws too few scan lines before the Atari tells it to do a vertical sync.

 

Michael

 

 

Yes I think it makes much more sense the way you put it. But when you say "depending on how the timer got set and how that little 'wait loop' is constructed," do you mean that one could set these manually with batari? In other words, I guess I'm not exactly sure what "the timer" is in Atari parlance... is it hardware or software? When and how is it set?

 

Thanks again.

 

Jarod

Share this post


Link to post
Share on other sites

Okay, I see by your program code that you're using the bankswitch_SC.inc file, which uses std_kernel_SC.asm instead of std_kernel.asm. I checked my copy of std_kernel_SC.asm and modified it a bit (I added another "sta WSYNC" in one spot), then tested your program both with and without the no_blank_lines option. It seems to give a steady 262 lines now. The updated std_kernel_SC.asm file is attached, and the place where I changed it is shown below.

 

Michael

 

 ifconst no_blank_lines; <-- this is line 595
sta WSYNC
sta WSYNC; <-- I added this extra sta WSYNC
endif

std_kernel_SC.zip

Share this post


Link to post
Share on other sites
when you say "depending on how the timer got set and how that little 'wait loop' is constructed," do you mean that one could set these manually with batari?

Yes, you can set the timer manually in bB-- but you wouldn't want to do that, unless you're writing your own kernel, or else are trying to do something very clever and extremely tricky. bB sets the timer itself, and if you set the timer as well, you'll be changing it from the value that bB set it to, which will most likely mess up the timing of bB's TV frame. See Is it possible to use too few scanlines with bB? for an example of setting the timer, thereby causing the scan line count to be way off.

 

In other words, I guess I'm not exactly sure what "the timer" is in Atari parlance... is it hardware or software?

The Atari 2600 has three chips in it-- the 6507 (which is its CPU), the TIA, and the 6532 (also called the RIOT). "RIOT" is short for "RAM-Input/Output-Timer," and the 6532 chip contains the Atari's 128 bytes of RAM, its input/output registers, and its timer registers. I posted some documentation about the 6532 chip in Please explain RIOT Timmers. Don't feel bad if it doesn't make much sense to you-- a lot of it's over my head, too! (I'm not a hardware guy or engineer.)

 

When and how is it set?

The timer can be set to one of four different intervals by writing a value to TIM1T, TIM8T, TIM64T, or T1024T. In bB you would do this by saying

 

   TIM1T = some_value_between_1_and_255
  rem * or
  TIM8T = some_value_between_1_and_255
  rem * or
  TIM64T = some_value_between_1_and_255
  rem * or
  T1024T = some_value_between_1_and_255

As soon as you write a value to one of these four registers, it will set the timer to that value, then the timer immediately (1 cycle later) decrements by 1. For example, if you say "TIM64T = 12," the timer will be set to 12, but then (1 cycle later) it will go to 11. After that, the timer will continue to count down at the interval you've selected-- once every 1 cycle (if you set the timer with TIM1T), or once every 8 cycles (if you set it with TIM8T), or once every 64 cycles (if you set it with TIM64T), or once every 1024 cycles (if you set it with T1024T). After the timer counts down to 0 and stays there for the specified number of cycles, it will roll over to 255, then resume counting down once every 1 cycle.

 

The moment the timer rolls over from 0 to 255, a "timer interrupt flag" will get set. This gives you two different ways to test whether or not the timer has finished counting down-- you can check the timer directly to see what its value is, or you can check the timer interrupt flag to see if it's been set.

 

There are different approaches to setting the timer and then checking it, and the approach you decide to use can affect the sort of results you get. Here are some examples-- written in bB-- of some different approaches you could use:

 

   TIM64T = 30
  gosub do_some_stuff
wait_for_it
  if INTIM <> 0 then wait_for_it
  WSYNC = 0

This approach sets the timer using the 64-cycle interval, then-- after you've returned from your do_some_stuff subroutine-- it waits until the timer (INTIM, short for "INterval TIMer") equals 0, at which point it does a WSYNC to wait for the beginning of the next scan line, and resumes with whatever comes next in your program.

 

   TIM64T = 30
  gosub do_some_stuff
wait_for_it
  if INTIM > 0 then wait_for_it
  WSYNC = 0

This code is almost the same as the first approach-- the only difference is the comparison (> 0 vs. <> 0). The difference is small, but can affect the outcome. For example, the first approach will wait until the timer is equal to 0, but the second approach will wait until the timer is 0 or less than 0 (meaning between 128 and 255).

 

   TIM64T = 30
  gosub do_some_stuff
wait_for_it
  if !TIMINT{7} then wait_for_it
  WSYNC = 0

This approach checks the timer interrupt flag (TIMINT) instead of the timer itself. It will wait until bit 7 of the interrupt flag register is set to 1, which indicates that the timer has finished counting down and has wrapped around from 0 to 255.

 

These aren't the only possible approaches, but there's no need to try to cover all of the possibilities if you aren't familiar with using the timer yet.

 

The most important thing to know right now is that you do *not* want to mess around with the timer in bB unless you're writing your own kernel, because bB's "canned" kernels set the timer for you, and if you set the timer on your own, you'll just mess things up for bB.

 

On the other hand, if you *are* writing your own kernel-- either using bB code (which is possible), or using assembly code (which is the preferred method)-- then the important question becomes "Why would I want to use the timer?"

 

When you write a program, the instructions can take different lengths of time to execute depending on various factors-- whether or not any page-crossing occur, or (more importantly) whether the program takes one branch or another. So if you have a lot of different tasks to do-- checking and responding to any input signals, checking and responding to any collisions, choosing between different routines depending on the value of some variable, etc.-- then once all those tasks have been completed, you can't be sure how much time they all took. But the thing is, drawing the TV frame requires some pretty precise timing, so you end up with the correct number of scan lines in the picture.

 

The timer provides an ideal way of handling this type of situation. If you have a certain number of scan lines available-- either in the blank area at the bottom of the screen, or in the blank area at the top of the screen-- then you can set the timer to count down a certain number of cycles that would be equivalent to that many scan lines. After you set the timer, you can go perform your tasks. Then, when you've completed your tasks, you put your program in a little loop that waits for the timer to finish counting down. As soon as the timer has finished counting down, you can go on to the next step in drawing the TV frame.

 

If you want more information about writing your own kernels or using the timer, you might want to check out Andrew's tutorial on 2600 programming, or search the forums for other topics that deal with the timer.

 

Michael

Share this post


Link to post
Share on other sites
Okay, I see by your program code that you're using the bankswitch_SC.inc file, which uses std_kernel_SC.asm instead of std_kernel.asm. I checked my copy of std_kernel_SC.asm and modified it a bit (I added another "sta WSYNC" in one spot), then tested your program both with and without the no_blank_lines option. It seems to give a steady 262 lines now. The updated std_kernel_SC.asm file is attached, and the place where I changed it is shown below.

 

Michael

 

 ifconst no_blank_lines; <-- this is line 595
sta WSYNC
sta WSYNC; <-- I added this extra sta WSYNC
endif

 

 

THANK YOU SIR.

 

I've noticed that certain Atari games carry constant scanline rates above 262. Am I wrong to think that they use similiar technique to balance the number of scanlines?

Share this post


Link to post
Share on other sites
THANK YOU SIR.

You're welcome. :)

 

I've noticed that certain Atari games carry constant scanline rates above 262. Am I wrong to think that they use similiar technique to balance the number of scanlines?

I don't know the answer for certain-- you'd need to check the source code for each game to be sure-- but I should think that the vast majority of games use the timer to keep a steady (or mostly steady) scan line count, if that's what you're asking.

 

Michael

Share this post


Link to post
Share on other sites

Thanks for all the helpful advice. This is a lot of information to "process", if you'll pardon the pun, but it is very fascinating stuff. I'm not much of hardware guy either, but much of it is actually starting to make sense!

 

Cheers,

Jarod.

Share this post


Link to post
Share on other sites
Posted (edited)

Is there an existing Bbasic tutorial program that deals with manipulating scanlines and cycles? As a noob I understand the rudimentary process but have no idea how to implement it or take it into account, other than "If program wiggles, reduce code/routine size".  Particularly taking advantage of vblank and overscan maybe?


 

Like I kinda get what it is, but not how to use it. I can read RT's EXCELLENT help page all day, but I find the easiest way to learn is to reverse engineer very simple existing .bas files. Without those examples I find it really hard to implement anything useful. I'm reading this https://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#vblank 

 

but I'm still not sure HOW MUCH code I can put into the vblank, not that I've tried it yet. A very simple 2 bank example .bas would be super duper helpful. Thanks again to everyone for all your great help and input!



 

Edited by freshbrood

Share this post


Link to post
Share on other sites

I'm not sure what information you are asking for here. The amount of code you can execute in a frame depends on the specifics of the code, which may also vary from frame to frame based on conditional code, loops, etc. It also depends on whether you use the score, and if you use a minikernel, without which there is more time per frame to run your code.

Share this post


Link to post
Share on other sites
Posted (edited)

search the forums this stuff comes up fairly often and there's been lots of good stuff posted about it

 

basically a television (at least in the before times) draws the screen periodically (frames) including things that have to happen

periodically during the frame (lines)

the processor provides the information for the picture in real time (more or less)

only parts of those periods are visible

the visible part takes most of the period

there's a little time left over after the visible part before the next frame must start

that's when your program runs

you run some program and then tell it to wait for the next frame (with a drawscreen)

if it takes too much time (ie too many lines, they're periodoic) it'll miss the next frame

 

in order to know how much time you're using your best bet is probably to look at the assembly that bB

produces and figure it out from that

 

there's probably debugging stuff that would help

I don't know if Stella gives you clock level timing but I can't think of any reason why it couldn't

so it probably does

Edited by bogax
clarification
  • Thanks 1

Share this post


Link to post
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.

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