Jump to content
IGNORED

Please explain RIOT Timmers


grafixbmp

Recommended Posts

I once read a document on the RIOT and TIA chips and the part pertaining to the RIOT lead me to belive there were 4 distictively diffrent timers that can be used at any given time. I read another document recetly that lead me to belive that only one timer is used at any given time and you choose which to use efectively limiting you to just one and only one at a time but you can choose how to use it with one of the 4 incrementers of 1, 8, 64 or 1024 (I think that is the right incrementers)

 

So which is it? I want to use the timmer to keep track of scanlines. Not how many per number but how many have happend and would like to know if that is all I can do at that time with the timmers.

Link to comment
Share on other sites

I once read a document on the RIOT and TIA chips and the part pertaining to the RIOT lead me to belive there were 4 distictively diffrent timers that can be used at any given time. I read another document recetly that lead me to belive that only one timer is used at any given time and you choose which to use efectively limiting you to just one and only one at a time but you can choose how to use it with one of the 4 incrementers of 1, 8, 64 or 1024 (I think that is the right incrementers)

 

So which is it? I want to use the timmer to keep track of scanlines. Not how many per number but how many have happend and would like to know if that is all I can do at that time with the timmers.

There's only one timer, but you can set it to count down at one of four different intervals (1, 8, 64, or 1024 cycles). It can count down at only one speed (interval) at a time, so if you're using it to keep track of the scanlines, then you can't really use it for anything else, at least not as far as setting it to another value and having it count down another interval.

 

I'm not certain what you're wanting to do as far as keeping track of scanlines. It sounds like you want to check the timer at any time and have it tell you (based on how much it's gone down) how many scanlines have elapsed? If that's the case, you can sort of do that, but it won't be quite that simple, because you'll need to subtract the current value from the starting value to get the difference, and then convert the difference into an equivalent number of scanlines-- but none of the four timer intervals are equal to the length of a scanline.

 

The interval that's closest to a scanline is 64 (a scanline is 76), which is why most everyone uses the 64-cycle interval for waiting until a certain number of scanlines have elapsed. Once the timer counts down to 0 and then wraps around to 255, the interval goes down to 1 cycle. And once you set the timer, it decrements right away before counting down at the desired interval. So the largest number you can set it to is 255, which will immediately go down to 254. Now, 255 is the same thing as -1, so if you set the timer to 255 with an interval of 64, and then let it count down, its value will be more or less equal to the negative number of scan lines that have elapsed. But you'll need to subtact it from 254 to get the scanline count, keeping in mind that it's really the number of 64 cycles that have elapsed, not scanlines per se:

 

   LDA #255
  STA TIM64T
 ; do some stuff, then
  SEC
  LDA #254
  SBC INTIM
 ; now A contains the *approximate* number of scanlines that have been drawn

The amount of the inaccuracy will increase rather quickly:

 

76 cycles (1 scanline) = 64 + 12

64 / 12 = about 5

 

So after about 5 scanlines, the counter will be off by 1. After about 10 scanlines, it will be off by 2, etc. Also, it takes a little time to do the subtraction, plus you can't be sure how close you are to the beginning of the next interval, so the count will be very iffy at best.

 

Michael

Link to comment
Share on other sites

I once read a document on the RIOT and TIA chips and the part pertaining to the RIOT lead me to belive there were 4 distictively diffrent timers that can be used at any given time. I read another document recetly that lead me to belive that only one timer is used at any given time and you choose which to use efectively limiting you to just one and only one at a time but you can choose how to use it with one of the 4 incrementers of 1, 8, 64 or 1024 (I think that is the right incrementers)

 

So which is it? I want to use the timmer to keep track of scanlines. Not how many per number but how many have happend and would like to know if that is all I can do at that time with the timmers.

There's only one timer, but you can set it to count down at one of four different intervals (1, 8, 64, or 1024 cycles). It can count down at only one speed (interval) at a time, so if you're using it to keep track of the scanlines, then you can't really use it for anything else, at least not as far as setting it to another value and having it count down another interval.

 

I'm not certain what you're wanting to do as far as keeping track of scanlines. It sounds like you want to check the timer at any time and have it tell you (based on how much it's gone down) how many scanlines have elapsed? If that's the case, you can sort of do that, but it won't be quite that simple, because you'll need to subtract the current value from the starting value to get the difference, and then convert the difference into an equivalent number of scanlines-- but none of the four timer intervals are equal to the length of a scanline.

 

The interval that's closest to a scanline is 64 (a scanline is 76), which is why most everyone uses the 64-cycle interval for waiting until a certain number of scanlines have elapsed. Once the timer counts down to 0 and then wraps around to 255, the interval goes down to 1 cycle. And once you set the timer, it decrements right away before counting down at the desired interval. So the largest number you can set it to is 255, which will immediately go down to 254. Now, 255 is the same thing as -1, so if you set the timer to 255 with an interval of 64, and then let it count down, its value will be more or less equal to the negative number of scan lines that have elapsed. But you'll need to subtact it from 254 to get the scanline count, keeping in mind that it's really the number of 64 cycles that have elapsed, not scanlines per se:

 

   LDA #255
  STA TIM64T
; do some stuff, then
  SEC
  LDA #254
  SBC INTIM
; now A contains the *approximate* number of scanlines that have been drawn

The amount of the inaccuracy will increase rather quickly:

 

76 cycles (1 scanline) = 64 + 12

64 / 12 = about 5

 

So after about 5 scanlines, the counter will be off by 1. After about 10 scanlines, it will be off by 2, etc. Also, it takes a little time to do the subtraction, plus you can't be sure how close you are to the beginning of the next interval, so the count will be very iffy at best.

 

Michael

The basic method I would like to use is as follows: a value is set to the timmer and it cycles a subroutine until it crosses 0 then I check the flag each time the subroutine runs and if the flag is set, it drops out of the subroutine. I can have the seting number fall at the right time for each instance of the subroutine so I don't have to keep track of the cycles but know when to quit the subroutine rounds.

Link to comment
Share on other sites

A lot of times you'll see:

 

	LDA   #$2C; value is up to you
STA   TIM64T

;do lots of stuff

.checkTimer
LDA   INTIM
BNE   checkTimer; stays in loop until timer reaches zero

 

 

You sound like you want to leave the loop after zero so use BPL instead of BNE. Not sure it's okay to load a value of $80 or greater in the timer to begin with though... If you loaded the Timer with $90 and checked it at $87 not sure if it would leave the loop or not.

 

 

Another thing to keep in mind is what flags are set not set by your branching to leave the loop. Timers are good for the Vblank and Overscan periods cause you can do lots of other stuff. There are some pretty good routines out there that accurately determine horizontal postion, and it is simple enough to know what frame you are one by incrementing a register. If you want to count scanlines in the kernal it might be a waste to use a timer though, as that is really crunch time.

Link to comment
Share on other sites

A lot of times you'll see:

 

	LDA   #$2C; value is up to you
STA   TIM64T

;do lots of stuff

.checkTimer
LDA   INTIM
BNE   checkTimer; stays in loop until timer reaches zero

 

 

You sound like you want to leave the loop after zero so use BPL instead of BNE. Not sure it's okay to load a value of $80 or greater in the timer to begin with though... If you loaded the Timer with $90 and checked it at $87 not sure if it would leave the loop or not.

 

 

Another thing to keep in mind is what flags are set not set by your branching to leave the loop. Timers are good for the Vblank and Overscan periods cause you can do lots of other stuff. There are some pretty good routines out there that accurately determine horizontal postion, and it is simple enough to know what frame you are one by incrementing a register. If you want to count scanlines in the kernal it might be a waste to use a timer though, as that is really crunch time.

One of the things nice about the timer is I won't have to increment or decrement it every time I check it which saves some cycles there. I need to figure out at what point I check it when it has cycled through the routine X amount of times that it will be 0. All the settings I come up with to load into it will need to be pre timmed just right so that the do cycle the number of times I need. I may use 8 instead of 64 so it will be more accurate. Once the flag is set and the check happens at the right time, it will work fine. One of the registers is always used for indexing PF data and the other is used exclusively for counting scanlines and sprite data.

Link to comment
Share on other sites

Yeah, but the bad news (besides the timer not having a matching step value of 76 cycles per scanline) is that checking the ram location still wastes 4 cycles, and cannot be controlled. A controlled DEC instruction uses 5 cycles. If you have a free register, DEY or DEX only uses 2.

Game kernals are already usually written in a way to know exactly how many scanlines are to be sent. Some feature unrelated code being executed while the display kernal executes (for example, setting up game vectors in the middle of a display)...so using a timer there can handle the difference between best-case and worst-case scenarios. But for the most part, the timer is usually only used to time the overscan and vertical blank periods. Set a timer, do a whole lot of stuff involving branches, then eat up the remaining time...each frame will reach the end of the loop at exactly the same time.

Link to comment
Share on other sites

There's only one timer, but you can set it to count down at one of four different intervals (1, 8, 64, or 1024 cycles). It can count down at only one speed (interval) at a time, so if you're using it to keep track of the scanlines, then you can't really use it for anything else, at least not as far as setting it to another value and having it count down another interval.

 

The intervals are 1, 16, 64, 1024.

 

There is one 8-bit counter, as well as a divide-by-16 and a divide-by-64 which may be enabled or disabled when the counter is written. I'm not sure why they didn't use a divide-by-8 instead of the divide-by-16 (yielding 1, 8, 64, 512) but I didn't design it.

Link to comment
Share on other sites

You sound like you want to leave the loop after zero so use BPL instead of BNE.

In that case, it would be best to check TIMINT instead of INTIM, because TIMINT is the timer interrupt flag, which is 0 if the timer hasn't passed 0 yet, and is set to 128 (bit 7 on) once the timer has passed 0. (Actually, the TIMINT register contains another flag in bit 6, but it isn't used by the Atari 2600, so TIMINT will either be 0 or 128.) That way, you can set the timer to a value greater than 127, and you won't have to worry about INTIM starting out with a "negative" value.

 

Michael

Link to comment
Share on other sites

There's only one timer, but you can set it to count down at one of four different intervals (1, 8, 64, or 1024 cycles). It can count down at only one speed (interval) at a time, so if you're using it to keep track of the scanlines, then you can't really use it for anything else, at least not as far as setting it to another value and having it count down another interval.

 

The intervals are 1, 16, 64, 1024.

 

There is one 8-bit counter, as well as a divide-by-16 and a divide-by-64 which may be enabled or disabled when the counter is written. I'm not sure why they didn't use a divide-by-8 instead of the divide-by-16 (yielding 1, 8, 64, 512) but I didn't design it.

So the documentation about the 6532 RIOT chip is incorrect? :? I don't know about the internal circuitry of the 6532 chip, but the data sheets say the intervals are 1 cycle, 8 cycles, 64 cycles, and 1024 cycles. And the names of the timer write registers in the vcs.h file are TIM1T, TIM8T, TIM64T, and T1024T. I can see why there's a divide-by-16 (1024 / 16 = 64), but I would have expected the other to be a divide-by-8 (64 / 8 = 8, and 8 / 8 = 1)?

 

Michael

 

mos_6532_riot.pdf

Link to comment
Share on other sites

So the documentation about the 6532 RIOT chip is incorrect?

 

Or maybe my own notes were incorrect. Sorry about that if so. Never mind me...

Don't worry, I assume you have access to some datasheets or diagrams that show the circuitry in greater detail? If so, can you post them? I like to collect stuff like that, even if it's over my head. :)

 

Michael

Link to comment
Share on other sites

  • 2 weeks later...
What exactly is INSTAT used for. Documentation reads as timer starus, read only (undocumented) Does anyine know anything about it?

Yes-- but it *is* documented, and "INSTAT" isn't its usual name.

 

The documentation for the 6532 "RIOT" chip calls it the "interrupt flag register." The VCS.H file calls it "TIMINT" (for "timer interrupt"). It's located at address $5 of the RIOT chip. On the Atari 2600, the RIOT chip addresses begin at address $280, so that means the TIMINT register is at address $285 (and its many mirrors).

 

This register holds two RIOT interrupt flags-- the timer interrupt flag (bit 7) and the PA7 edge-detect interrupt flag (bit 6). These flags will trigger IRQ interrupts, but they have no "real" effect on an Atari 2600, because the Atari 2600 uses a 6507 CPU, and the 6507 chip doesn't have an IRQ line, therefore the Atari 2600 doesn't have IRQ interrupts.

 

I don't know if the PA7 edge-detect interrupt flag has any significance for Atari 2600 programming, so I won't try to explain it. But the timer interrupt flag can be used to tell if the timer has counted down past 0.

 

Whenever you set the interval timer by writing a value to TIM1T, TIM8T, TIM64T, or T1024T, the timer interrupt flag will be cleared automatically. The timer then counts down to 0, starting from the value you've set it to. The first decrement occurs right after you set the timer (i.e., 1 cycle later), but the subsequent decrements occur at the interval you've selected (i.e., after every 1 cycle, 8 cycles, 64 cycles, or 1024 cycles). When the timer reaches 0, it will stay 0 for the indicated interval, and will then wrap around to 255. The moment it wraps around from 0 to 255, the timer interrupt flag will be set. Once the timer has wrapped around and the timer interrupt flag has been set, the timer will decrement every 1 cycle.

 

Whenever you read the timer or the timer interrupt flag, the timer interrupt flag will be cleared. However, the flag will *not* be cleared if you read it (or the timer) at the same moment that the flag is set.

 

Anyway, let's suppose you write a value of 5 to TIM8T, which will cause the timer to count down from 5 to 0 at an interval of 8 cycles. The timer and the timer interrupt flag will contain the following values:

 

Instruction: | Cycle:	 | Timer:	   | Flag:
LDA #5	   | 00, 01	 | ???, ???	 | ???, ???
STA TIM8T	| 02, ... 05 | ???, ... 005 | ???, ... 000
		 | 06, ... 13 | 004, ... 004 | 000, ... 000
		 | 14, ... 21 | 003, ... 003 | 000, ... 000
		 | 22, ... 29 | 002, ... 002 | 000, ... 000
		 | 30, ... 37 | 001, ... 001 | 000, ... 000
		 | 38, ... 45 | 000, ... 000 | 000, ... 000
		 | 46		 | 255		  | 128
		 | 47		 | 254		  | 128
		 | 48		 | 253		  | 128
		 | etc.	   | etc.		 | etc.

In other words, "LDA #5" takes 2 cycles, and we don't know what the timer or the flag are set to. "STA TIM8T" takes 4 cycles. On the last cycle of that instruction, the timer will be set to 5, and the flag will be cleared. On the very next cycle, the timer will decrement from 5 to 4, but the flag will still be 0. The timer will stay at 4 for 8 cycles, then it will decrement to 3 and stay at 3 for 8 cycles, etc. When the timer reaches 0, it will stay at 0 for 8 cycles.

 

Then the timer will decrement from 0 to 255, and the flag will be set. The timer flag is bit 7, so it has a value of 128. The timer will continue decrementing, but it will decrement once each cycle, rather than every 8 cycles as before.

 

One way to check when the timer has finished counting down is to read the timer (or INTIM), which tells us what value is in the timer at that moment. We could then test the value of the timer to see if it's equal to 0, not equal to 0, plus/positive (0 to 127), or minus/negative (128 to 255). But that method can present problems, depending on what value we set the timer to, and how we're testing its value. For example, if you set the timer to 255, and then test whether its value is minus, then it will be minus right away. Or if you set the timer to 64, and then test whether its value is plus, then it will be plus right away. A more generic test would be to see if its 0 yet-- but that would mean you aren't waiting until it's counted *past* 0.

 

Another way to check when the timer has finished counting down is to read the timer flag (or TIMINT). As long as TIMINT is still 0, the timer hasn't finished counting down yet. But if TIMINT is 128, then the timer has finished counting down. If you aren't worried about the PA7 edge-detect interrupt flag (bit 6 of TIMINT), then you can just use "not equal 0."

 

So let's say you want to wait for 20 scan lines before doing something. A scan line is 76 cycles long, so 20 scan lines would be 1520 cycles. Dividing that by 64, we get 23.75. So we could do the following:

 

   LDA #23
  STA TIM64T
; do some stuff here, then start checking the timer flag
LOOP
  LDA TIMINT
  BEQ LOOP; this will keep looping as long as the flag isn't set yet
  STA WSYNC; finish off the scan line so we resume at the beginning of a scan line
; now we've waited 20 scan lines

Note that it takes time to set the timer, and it takes time to read the timer, so that needs to be factored into the amount of time we set the timer for. You might need to adjust the number you set the timer to in order to get the desired result. For example, we might need to use "LDA #22" instead of "LDA #23."

 

Michael

Link to comment
Share on other sites

Another way to check when the timer has finished counting down is to read the timer flag (or TIMINT). As long as TIMINT is still 0, the timer hasn't finished counting down yet. But if TIMINT is 128, then the timer has finished counting down. If you aren't worried about the PA7 edge-detect interrupt flag (bit 6 of TIMINT), then you can just use "not equal 0."

 

The best way to test a high-bit flag is to use the BIT instruction followed by BMI or BPL. BMI will branch if bit 7 was set, and BPL will branch if it was not.

 

Incidentally, one very handy technique for the RIOT is to load TIM64 with ~125 plus the delay time. Then wait for it to be less than or equal to 125. This approach makes it practical to divide work over multiple frames. For example, in Strat-O-Gems, it takes a lot of code to evaluate which gems should participate in a reaction. A typical loop looks like:

DiagLoop1:
 bit INTIM
 bmi Diag1NoKernel
 jsr KERNEL
Diag1NoKernel:
 ...
 ... do some stuff
 ...
 dex
 bpl DiagLoop1

The KERNEL routine itself takes care of saving registers (A, X, and S, but not Y or P), waiting for INTIM to reach 125, showing the displayed frame or vSync, and then setting things up for the next display event. Thus, the main program can do work in both vBlank and overscan, without particularly worrying about display timing.

Link to comment
Share on other sites

The best way to test a high-bit flag is to use the BIT instruction followed by BMI or BPL. BMI will branch if bit 7 was set, and BPL will branch if it was not.

Yes, that's a better suggestion than BNE, since it means we wouldn't accidentally mistake the PA7 edge-detect flag for the timer interrupt flag. And speaking of the PA7 edge-detect flag, do you know if it has any possible use on the Atari 2600? I mean, the Atari *does* use the RIOT's two peripheral ports, right? So I presume we could set up the PA7 edge-detect and then check the flag. But I also presume this might not be very useful in practice, since the flag can't trigger an interrupt. So would there be any possible value in polling TIMINT to see if bit 6 has been set?

 

Michael

Link to comment
Share on other sites

The best way to test a high-bit flag is to use the BIT instruction followed by BMI or BPL. BMI will branch if bit 7 was set, and BPL will branch if it was not.

Yes, that's a better suggestion than BNE, since it means we wouldn't accidentally mistake the PA7 edge-detect flag for the timer interrupt flag. And speaking of the PA7 edge-detect flag, do you know if it has any possible use on the Atari 2600? I mean, the Atari *does* use the RIOT's two peripheral ports, right? So I presume we could set up the PA7 edge-detect and then check the flag. But I also presume this might not be very useful in practice, since the flag can't trigger an interrupt. So would there be any possible value in polling TIMINT to see if bit 6 has been set?

 

Michael

Even though it doesn't have the interupt flag like others, you could probably use it for the intended puropse with a sort of software interupt hack that polls the flag and does a compare then drops to a functional interupt (or elsewhere ie: reset vector) if the flag is set.

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