Jump to content
IGNORED

Smooth scrolling


Asmusr

Recommended Posts

I do have a >D0 at the end of my SAT, but I use a y value of >C0 to place sprites temporarily outside the visible screen. Will coincidence be detected between these sprites?

 

Note: On the F18A with the V1.3 firmware sprite bug, yes. You need to space out the sprites on the X coordinate too. Sorry.

 

On the F18A V1.4 firmware and real 9918A: No, collisions are resolved in the color mux of the VDP, so only sprites that have a pixel on the active display, i.e. inside the 256x192 visible pixel area. If you can't see the pixel, it won't trigger a collision. Note that a "pixel" in the 9918A is a "1" bit in the pattern and NOT related to the color. You can have a sprite with all "1" pixels but transparent color so it would not be visible to the human eye, but it will still trigger a collision.

 

Note in the F18A's enhanced color modes, the pattern data *is* the color data so only pixels with colors of "0", "00", or "000" will be transparent based on the "transparency" option for the sprite, and not cause a collision if transparency is set.

 

Waiting for VSYNC without reading the status is only possible using the interrupt, right?

 

Correct. You can only wait for VSYNC by using the interrupt, which on the 99/4A means the console's ISR will run too. However, you can disable most of the console's ISR processing and add a hook for your own code. It is not a lost cause, but it will add a little overhead to your ISR. I think I gave an example in my assembly thread of efficient interrupt usage. I can dig it up if you want some code to look over

 

Link to comment
Share on other sites

Speaking of sprite coincidence, I was looking at the TI Forth word COINC to see how the coincidence of 2 sprites was calculated, given a pixel tolerance. At first I was confused because the inter-sprite distance (d) is compared with the tolerance (t) X √2, i.e., d > t√2. The actual comparison is d 2 > 2t 2. I guess this is taking into account that pixels are considered square and the distance from one corner to the diagonally opposite corner of a pixel is the pixel width times √2; hence, the tolerance is recalculated (t√2) to describe the radius of a tolerance circle that circumscribes all pixels within the tolerance. This means that, if the top left corners (TLCs) of the two sprites are in the same column or row that the actual number of pixels considered is 41.4% higher than you might expect, e.g., if you specified a tolerance of 10 pixels and the TLCs of the two sprites are 14 pixels apart on pixel-row 100, they would be considered coincident!

 

Sorry, if this is obvious to everyone—just thinking out loud. :D Also—if you think my explanation is wrong, please feel free to disabuse me of my logic.

 

...lee

Link to comment
Share on other sites

... On the F18A V1.4 firmware and real 9918A: No, collisions are resolved in the color mux of the VDP, so only sprites that have a pixel on the active display, i.e. inside the 256x192 visible pixel area. If you can't see the pixel, it won't trigger a collision. ...

 

If you are correct, the E/A manual has it wrong on p. 269: "Coincidence flag. Set if two or more sprites have overlapping pixels, including sprites that are transparent and sprites that are off the bottom of the screen."

 

...lee

Link to comment
Share on other sites

Waiting for VSYNC without reading the status is only possible using the interrupt, right?

 

Correct. You can only wait for VSYNC by using the interrupt, which on the 99/4A means the console's ISR will run too. However, you can disable most of the console's ISR processing and add a hook for your own code. It is not a lost cause, but it will add a little overhead to your ISR. I think I gave an example in my assembly thread of efficient interrupt usage. I can dig it up if you want some code to look over

 

I do a "LIMI 0" in the very beginning of my code, and then never issuing a "LIMI 2". No console ISR will run.

 

When all my game logic/whatever is done, or sliced so it's done within 1/60th of a sec, then the following waits for the interrupt. The routine will return immediately if a frame has already been displayed since the last run/read. Call it twice to run at 30 frames a sec.

 

wait1 movb @>8802,r0 ; wait for end of VDP displaying frame (reading it will reset it)

andi r0,>8000 ; isolate bit 0 (set when end of frame)

jeq wait1

rt

Edited by sometimes99er
Link to comment
Share on other sites

I found there's a significant different between how the collision flag works on Classic99 and MESS. When you poll the status register on Classic99 you don't have to save the collision flag unless the interrupt flag is also set. On MESS you must save the flag or you will fail to detect most collisions. Looks like Classic99 is caching the flag for the following vsync period and MESS doesn't. I guess MESS works more like the real hardware?

 

I'd like to see your test for this one - that's not how the code works.

 

Byte rvdpbyte(Word x, bool rmw)
{ 
unsigned short z;

if ((x>=0x8c00) || (x&1))
{
	return(0);											// write address
}

if (x&0x0002)
{	/* read status */
	z=VDPS;				// does not affect prefetch or address (tested on hardware)
	VDPS&=0x1f;			// top flags are cleared on read (looks like 5th sprite is not)
	vdpaccess=0;		// reset byte flag
	return((Byte)z);
}

 

As you can see, Classic99 immediately masks off the status bits when the VDP status register is read. Caching it for the interrupt routine from the emulator side would be crazy complicated. :) This code has been this way for years.

 

So I'm curious what you're seeing.

 

Link to comment
Share on other sites

 

 

I believe the value is D0h, not D1h. Also, you only need that value as the Y-coordinate of the lowest-numbered, undefined sprite. This will cause all higher-numbered sprites to be undefined, as well.

 

...lee

You made me to re-read the Texas Instruments TMS9928 datasheet, and you're right, only D0h stops sprite processing, D1h has no effect except putting sprite off screen.

 

Still wondering whether offscreen sprites activate collision bit, I would need to code a special program to test it over real hardware (in this case, my Colecovision)

Link to comment
Share on other sites

 

 

I'd like to see your test for this one - that's not how the code works.

 

Byte rvdpbyte(Word x, bool rmw)
{ 
   unsigned short z;

   if ((x>=0x8c00) || (x&1))
   {
       return(0);                                            // write address
   }

   if (x&0x0002)
   {    /* read status */
       z=VDPS;                // does not affect prefetch or address (tested on hardware)
       VDPS&=0x1f;            // top flags are cleared on read (looks like 5th sprite is not)
       vdpaccess=0;        // reset byte flag
       return((Byte)z);
   }

 

As you can see, Classic99 immediately masks off the status bits when the VDP status register is read. Caching it for the interrupt routine from the emulator side would be crazy complicated. :) This code has been this way for years.

 

So I'm curious what you're seeing.

 

Your code looks just right.

 

I've my doubts about continuous polling of VDP status register for collision bit and fifth sprite.

 

I'll code something this week to check for this. I have this "spine" since many years ago.

Link to comment
Share on other sites

I'd like to see your test for this one - that's not how the code works.

I don't have any good test code to show you without sending you my entire project. But I found that code that worked perfectly on Classic99 hardly detected any collisions on MESS. This was because I only saved the collision flag when the interrupt flag was also set. After changing the code always to save the collision flag, it works on both emulators.

 

My guess, and I might completely wrong, is that it's not about clearing the flag, which Classic99 indeed does. But since Classic99 is drawing the screen all at once there is no way you can read the collision flag half way down the screen. The flag will always represent the collisions of a full screen. If MESS is drawing the screen one scan line at a time you can happen to read the flag half way down the screen and thereby reset it. If you then read the flag again at VSYNC you will only get the collisions for the bottom half of the screen (unless you saved it the first time, which I didn't). Does that sound plausible?

Link to comment
Share on other sites

snapback.pngmatthew180, on Sun Jul 21, 2013 12:09 PM, said:

 

... On the F18A V1.4 firmware and real 9918A: No, collisions are resolved in the color mux of the VDP, so only sprites that have a pixel on the active display, i.e. inside the 256x192 visible pixel area. If you can't see the pixel, it won't trigger a collision. ...

 

If you are correct, the E/A manual has it wrong on p. 269: "Coincidence flag. Set if two or more sprites have overlapping pixels, including sprites that are transparent and sprites that are off the bottom of the screen."

 

...lee

 

Nope—The TMS9918A datasheet also says sprites partially to completely off the screen are included in setting/resetting the coincidence flag.

 

...lee

Link to comment
Share on other sites

If you are correct, the E/A manual has it wrong on p. 269: "Coincidence flag. Set if two or more sprites have overlapping pixels, including sprites that are transparent and sprites that are off the bottom of the screen."

 

...lee

 

I have never found a clear and accurate description of the coincidence flag. Even the 9918A datasheet can be somewhat misleading. Probably the best technical (and brief) description if from the datasheet:

 

"The VDP checks the pixel position for coincidence during the generation of the pixel regardless of where it is located on the screen."

 

A few things to note about this:

 

1. ONLY the first four visible sprites on a line will be considered for coincidence because coincidence is only detected during the generation of a pixel for display, i.e. the output of the sprite shift registers of which there are only four, and they only shift during the active part of a scan line.

 

2. The term "screen" is not clear. After testing I determined it means the active 256x192 pixel area, and does not include the border / over scan areas.

 

3. Sprites with pixels that are a "0" bit in the pattern will not cause a coincidence, however a "1" bit in the pattern, regardless of the sprite's color, will cause a coincidence.

 

I wrote a test program in XB that placed a 16x16 sprite half way off of each border. I had another sprite that would move 1-pixel at a time via the keyboard input, and I would display the coincidence status in a loop. I then moved the one sprite around (it went very slowly, being XB and all) and noted the coincidence status. For the sprite half way off the bottom of the visible screen, I could move down, over, and up around that sprite and not trigger a coincidence. However, one pixel overlapping in the visible area and the coincidence was detected.

 

This is a very easy test to reproduce, and it has been a while since I ran it. It would be nice for someone else to do a verification on an unmodified console.

 

Link to comment
Share on other sites

You made me to re-read the Texas Instruments TMS9928 datasheet, and you're right, only D0h stops sprite processing, D1h has no effect except putting sprite off screen.

 

Still wondering whether offscreen sprites activate collision bit, I would need to code a special program to test it over real hardware (in this case, my Colecovision)

 

Off-screen sprites are, indeed, considered—reread the datasheet p. 2-11 and see post #110 above.

 

...lee

Link to comment
Share on other sites

Nope—The TMS9918A datasheet also says sprites partially to completely off the screen are included in setting/resetting the coincidence flag.

 

...lee

 

I know, I read that a dozen times or more, but it is incorrect. If that were true, my V1.3 collision bug would not have been a bug. And you would not be able to "park" sprites off screen. The "partially" off screen is correct, and I think they are just trying to say that a sprite does not have to be "completely visible" to be considered.

 

Link to comment
Share on other sites

 

 

I do a "LIMI 0" in the very beginning of my code, and then never issuing a "LIMI 2". No console ISR will run.

 

When all my game logic/whatever is done, or sliced so it's done within 1/60th of a sec, then the following waits for the interrupt. The routine will return immediately if a frame has already been displayed since the last run/read. Call it twice to run at 30 frames a sec.

 

wait1 movb @>8802,r0 ; wait for end of VDP displaying frame (reading it will reset it)

andi r0,>8000 ; isolate bit 0 (set when end of frame)

jeq wait1

rt

Link to comment
Share on other sites

Sorry for the double post. I am on my phone.

 

The above code does not work reliably on a real console. The check is virtually continuous and you will run into a situation where you ate trying to read the status reg while the vdp is trying to write to it. There. Is no way to prevent this but it can be minimized by placing a series of nop's between reads. My experience indicates about 25 works best.

Link to comment
Share on other sites

I would be surprised if that kind of race condition is possible with the real 9918A hardware, it might be though and would be a good reason to not using polling on the real VDP. Such an event would be very hard to detect externally, but soon enough the 9918A will be decapped and we can peek inside and see for sure. In the F18A I very specifically designed around that possibility.

Link to comment
Share on other sites

Well, the datasheet says one thing, the real VDP does another. Run the simple XB test and let me know what you come up with. I'll see if I can find the code, but it is a pretty simple program.

 

You're right, of course! I parked one sprite, with a solid bottom half, at the bottom of the screen with one row of pixels showing (3 rows off the bottom). I passed a solid sprite from left to right across the bottom of the screen, one pixel at a time, for a short distance that included the first sprite, waiting for a detectable VDP interrupt between sprite moves. The coincidence bit was set whenever on-pixels of the moving sprite overlaid any visible pixels of the stationary sprite. Next, I performed the same operation with the second sprite moving just off the bottom of the screen, overlapping three rows of on-pixels off the bottom of the screen with no setting of the coincidence bit. The same result obtained on both Classic99 and the TI-99/4A with 9918A. Thanks, Matthew, for setting a knucklehead straight. :-o

 

...lee

Link to comment
Share on other sites

It definitely exists and is very noticeable when using the vdp interval in music processing. If you read while the vdp is writing the bit will not get set.

 

It's not a race condition. It's a matter of constantly triggering the register to clear by continually reading it at a very high rate.

 

You will miss hits unless you reduce the rate at which you attempt to read the register, therefore inserting nop's between successive reads will reduce the odds dramaticly. Not perfectly but enough to be unnoticed.

 

The issue with the Isr is that if you are using sprites they must be updated immediately after the v-blank to insure good coincidence detection on the upper part of the screen.

Link to comment
Share on other sites

You're right, of course! I parked one sprite, with a solid bottom half, at the bottom of the screen with one row of pixels showing (3 rows off the bottom). I passed a solid sprite from left to right across the bottom of the screen, one pixel at a time, for a short distance that included the first sprite, waiting for a detectable VDP interrupt between sprite moves. The coincidence bit was set whenever on-pixels of the moving sprite overlaid any visible pixels of the stationary sprite. Next, I performed the same operation with the second sprite moving just off the bottom of the screen, overlapping three rows of on-pixels off the bottom of the screen with no setting of the coincidence bit. The same result obtained on both Classic99 and the TI-99/4A with 9918A. Thanks, Matthew, for setting a knucklehead straight. :-o...lee

 

This one is actually pretty easy to catch in practice (unfortunately none of us testers tried the easy test on the F18A)... just use sprites and collision in XB. XB positions unused sprites off the bottom of the screen but NOT disabled with 0xD0, so if you use CALL COINC(ALL) and have any sprites still uninitialized, then it triggers if you listen to that bit of the datasheet. :)

 

I tested this a LOT when I found it in Classic99. It's confirmed by myself and others.

 

Link to comment
Share on other sites

I would be surprised if that kind of race condition is possible with the real 9918A hardware, it might be though and would be a good reason to not using polling on the real VDP. Such an event would be very hard to detect externally, but soon enough the 9918A will be decapped and we can peek inside and see for sure. In the F18A I very specifically designed around that possibility.

 

Hello.. Soon enough? Really? :)

Link to comment
Share on other sites

I don't have any good test code to show you without sending you my entire project. But I found that code that worked perfectly on Classic99 hardly detected any collisions on MESS. This was because I only saved the collision flag when the interrupt flag was also set. After changing the code always to save the collision flag, it works on both emulators.

 

My guess, and I might completely wrong, is that it's not about clearing the flag, which Classic99 indeed does. But since Classic99 is drawing the screen all at once there is no way you can read the collision flag half way down the screen. The flag will always represent the collisions of a full screen. If MESS is drawing the screen one scan line at a time you can happen to read the flag half way down the screen and thereby reset it. If you then read the flag again at VSYNC you will only get the collisions for the bottom half of the screen (unless you saved it the first time, which I didn't). Does that sound plausible?

 

MESS may have changed in the last two years, but two years ago it did not draw the screen line by line. In fact, two years ago between Classic99 and MESS, only Classic99 could set the status bits mid-screen -- MESS's status register updates only occurred at the end of frame. (Classic99's mid-frame updates were not /correct/, but a question had come up about why Classic99 could have the sprite collision bit set but not the vertical interrupt bit, when MESS never had them separated).

 

So although Classic99 does not today emulate the scanlines, the status register bits are set when detected, and are not synchronized to an end of frame situation, which would be very incorrect. Because the VDP and the CPU run on different threads, the exact timing is somewhat random.

 

I can't tell you anything about what MESS does today, and without seeing your code, I can't explain your results under Classic99.

 

I'm a little confused by "I only saved the collision flag when the interrupt flag was set"... in which case? Remember that every time you read the status register, you clear /all/ the status bits. So if the collision flag is set and interrupt is not, and you throw away the collision bit, it does not get set again at the end of the frame. The collision bit, the 5-sprites-on-a-line bit, and the vertical blanking bit are not related in any way -- each triggers on the scanline that the event occurs (actually, I'm told it can delay a scanline or two, depending on the internal state, but around there.)

 

Link to comment
Share on other sites

I'm a little confused by "I only saved the collision flag when the interrupt flag was set"... in which case? Remember that every time you read the status register, you clear /all/ the status bits. So if the collision flag is set and interrupt is not, and you throw away the collision bit, it does not get set again at the end of the frame.)

 

Well that's what I did, I polled the status register with regular intervals and only saved the collision flag when the interrupt flag was set. It worked perfectly on Classic99. Could it have something to do with how long the screen redraw takes on the two emulators? I mean, if it only takes 1 ms on Classic99 and I only poll the status register every 4 ms my code would very likely work, whereas if the redraw took the full 16-17 ms my code would miss many of the collision flags.

 

Anyway, my code works now so there's no need to start a lengthy debate, I just thought it was odd.

Link to comment
Share on other sites

Well that's what I did, I polled the status register with regular intervals and only saved the collision flag when the interrupt flag was set. It worked perfectly on Classic99. Could it have something to do with how long the screen redraw takes on the two emulators? I mean, if it only takes 1 ms on Classic99 and I only poll the status register every 4 ms my code would very likely work, whereas if the redraw took the full 16-17 ms my code would miss many of the collision flags.

 

Ah.. yeah, you were just lucky. :) You definitely can not associate the bits. It's very likely in Classic99 for the collision bit to be set before the interrupt bit (and was the cause of a discussion that I was talking about in my last post). But because Classic99 draws sprites as the last operation, and because the vertical blanking bit is set after the draw is complete, there's a very narrow time between the two steps right now (and this is a case where real time matters, because of the separate threads). You would have missed now and then when the timing was just right, but it would be pretty rare.

 

It would have been more common in the past, because I used to set the vertical blanking bit at the beginning of the display draw, instead of at the end. This actually caused rare race conditions in the sprite code when the CPU was moving them immediately upon seeing blank (correctly assuming that it was safe!)

 

Anyway, my code works now so there's no need to start a lengthy debate, I just thought it was odd.

 

No intention to debate, but I can't fix the problems with Classic99 if I don't understand them! :) In this case, the fix here is already scheduled, it just led you to a bad conclusion about the behavior of those bits. This just helps bump priorities in my mind, so thanks for answering the questions!

 

 

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