Jump to content
matthew180

Assembly on the 99/4A

Recommended Posts

So lets say I successfully poll the CRU for vsync and eventually detect one...

 

In my code, I now know Ive hit a 1/60th of a second NTSC (or 1/50th second, PAL) event. I believe thats it.

 

This vsync event now triggers a decision tree gate which runs a specific segment of code. Perhaps I decide to do three things when vsync is detected:

 

A. Update the player character position.

B. Hit my roll-ur-own sound routine to update background music.

C. Refresh the game scoreboard.

 

Looks good.

 

How do I know theres enough time to do these things between vsyncs?

 

You don't. We'll get to that.

 

How many tasks may I tie to the vsync event?

 

Well, if any number of tasks takes too much time, one may split things, reorder etc.

 

Im thinking about counting vsyncs and enabling sub-events based on determined vsync counts...

 

Absolutely. You would only update the score if the score has changed.

 

Am I attempting to process as much as I can before the CRT beam travels from bottom right to top left on the screen?

 

Yes. It's actually at the visible bottom just outside the 192 scanlines that makes up the display (24 character lines 32 columns), and just until the display starts again (the 24 lines, a good number of scanlines down the screen). Also called the vertical gap.

 

The vertical gap/blank goes on right after this (the 24th line is blank)

 

gallery_11132_2341_6588.jpg

 

and stops just before the colors (1st line of characters)

 

gallery_11132_2341_1855.jpg

 

I guess what Im looking for is a few tips and techniques on the topic.

 

Have a black background. This shows up in the border of the display. Change the background color to red when the vsync is detected. Change the background color to whatever for each of your major tasks (3-12). Change background back to black when all is done.

 

You will see some nervous color bands representing the time each task takes.

 

The idea of using the vsync signal as a constant interval timer is manageable.

Yep.

 

If I try to do too much after a vsync is detected Im thinking Ill miss the next vsync, and my interval timer will then become inconsistent and result in stuttering video/audio?

 

Yes, that is a concern, but try it out. Obviously one has to update the display right after the vsync (to avoid screen tearing), and game logic, positions, collisions etc. can be done after the update of display (effectively displaying what was computed in the previous frame). Depending on the use of buffers, updating the display can be rather fast (like switching the screen image table).

 

Hope this helps.

Edited by sometimes99er
  • Like 4

Share this post


Link to post
Share on other sites

^^^^ oh that Vertical Blank Interval Link was a great one! Thanks! Great read for clarity.

 

So...apparently the Atari 8-bit computer graphics systems had both horizontal and vertical blank interrupts. Powerful tools the TI system lacks.

 

https://en.wikipedia.org/wiki/Vertical_blank_interrupt

 

We’ll never know when each horizontal line has completed as with Atari’s Horizontal Blank Interrupts, so we’re polling the vsync in lieu of a true hardware vertical blank interrupt.

 

Correct? Link led to this:

 

“As the VBI will be generated at the start of every displayed frame (50 Hz for PAL, 60 Hz for NTSC), it is also a useful timebase in systems lacking an interrupt from a programmable or fixed interval timer. Regular software functions like scanning a keyboard, reading a joystick or maintaining a time or date measurement can be carried out.”

Edited by Airshack

Share this post


Link to post
Share on other sites

I remember from XB256 I can place a sprite vertically from 0 - 255, even though 0-191 was the visible range.

 

So I can hide sprites off screen (virtually in

Memory) at 192 and beyond. As long as I didn’t position them at >D0 which is 208, which terminated the sprite number and all higher sprites.

 

So for the 9918 the vertical blank interval includes drawing some x amount of lines above and below the 0-192 range, in addition to the electron beam diagonal travel time from bottom right to top left?

 

Understood all but that part I think.

 

 

Sent from my iPhone using Tapatalk Pro

Edited by Airshack

Share this post


Link to post
Share on other sites

So for the 9918 the vertical blank interval includes drawing some x amount of lines above and below the 0-192 range, in addition to the electron beam diagonal travel time from bottom right to top left?

 

 

 

History Lesson from the Analog Video Age

 

<history>

The reason that there are unused lines at the top of the "raster" as it's called, is because in the early days it was never certain that the image would be stable at the top after the electron beam was dragged back to the top of the screen, so the extra lines at the top were a safety zone and therefore no picture was applied to those lines (22 lines if I remember?) Remember, making TV actually work was at the limits of the tube technology in the very early days.

 

In the 80's these unused picture lines were actually used for test signals and digital time code called "vertical interval time code" (VITC)

VITC was essentially a serial data string applied to 1 line of video.

 

Also the entire image was assumed to be cropped because the TV manufacturer was supposed to "overscan" the image so the top, bottom and sides were hidden behind the edge of the box that held the CRT. :-) In TV studios we used "underscanned" monitors so we could see the entire image.

</history>

  • Like 2

Share this post


Link to post
Share on other sites

 

History Lesson from the Analog Video Age.

</history>

Thanks for yet another important piece of the puzzle. Interesting.

 

 

Sent from my iPhone using Tapatalk Pro

Share this post


Link to post
Share on other sites

 

 

 

Have a black background. This shows up in the border of the display. Change the background color to red when the vsync is detected. Change the background color to whatever for each of your major tasks (3-12). Change background back to black when all is done.

 

 

A very cool idea I’ll immediately adopt. Having a why-didn’t-I-think-of-that moment. Edited by Airshack

Share this post


Link to post
Share on other sites

^^^^ oh that Vertical Blank Interval Link was a great one! Thanks! Great read for clarity.

 

So...apparently the Atari 8-bit computer graphics systems had both horizontal and vertical blank interrupts. Powerful tools the TI system lacks.

 

https://en.wikipedia.org/wiki/Vertical_blank_interrupt

 

Well never know when each horizontal line has completed as with Ataris Horizontal Blank Interrupts, so were polling the vsync in lieu of a true hardware vertical blank interrupt.

 

Correct?

Yes.

 

Link led to this:

 

As the VBI will be generated at the start of every displayed frame (50 Hz for PAL, 60 Hz for NTSC), it is also a useful timebase in systems lacking an interrupt from a programmable or fixed interval timer. Regular software functions like scanning a keyboard, reading a joystick or maintaining a time or date measurement can be carried out.

I still think the TI sets the bit when the "frame is completed" (as in the last line of characters (line 24) has been drawn) - and not "while the beam is tracing up to the upper left corner of the screen".

 

http://www.unige.ch/medecine/nouspikel/ti99/tms9918a.htm

  • Like 1

Share this post


Link to post
Share on other sites

I remember from XB256 I can place a sprite vertically from 0 - 255, even though 0-191 was the visible range.

 

So I can hide sprites off screen (virtually in

Memory) at 192 and beyond. As long as I didn’t position them at >D0 which is 208, which terminated the sprite number and all higher sprites.

 

Yes. The first vertical sprite position is >FF (Assembly not XB - there's a reason, but that's another story). Position >FE simply hides the top of a sprite (behind the top border).

 

You have to watch out for sprite magnification and size. At least position >C0 should always hide your sprite.

 

So for the 9918 the vertical blank interval includes drawing some x amount of lines above and below the 0-192 range, in addition to the electron beam diagonal travel time from bottom right to top left?

 

That's how I think I experienced it way back. And I think both MAME and Classic99 will show this if you change the background color when there's a vsync event.

Edited by sometimes99er

Share this post


Link to post
Share on other sites

A very cool idea I’ll immediately adopt. Having a why-didn’t-I-think-of-that moment.

 

I did that way back. It crossed my mind, but I've only used it since Asmusr brought it up. Quite handy.

Share this post


Link to post
Share on other sites

With the 9918A the interrupt does not coincide with the actual VSYNC signal sent to the TV/monitor. The term "VSYNC" is used loosely in conversation, but when talking about timing you should not conflate the two (interrupt and VSYNC). The interrupt happens right after the active area, which is line 192 if you think of the active area as lines 0..191. This is a good thing, because it gives you the maximum amount of time between frames to update the VRAM without conflicting with the VDP or worrying about tearing.

 

Some possibly useful calculations:

 

Total lines in one frame = 262 lines

Active area = 192 lines

Blanking area = 262 - 192 = 70 lines

 

One frame = 1/60 = 0.0166666 = ~16.6ms

One line = 16.66ms / 262 lines = 63.61us per line

Blanking period = 63.61us * 70 lines = 4.452ms

 

9900 CPU clock cycle = 3MHz = 333ns

Clocks per blanking period = 4.452ms / 333ns = 13358 cycles

 

The most common instructions in the 9900, i.e. the addition, subtraction, moves, compares, logical (except shift) average between 10 and 14 clocks, with variation depending on the access mode. If you use a lot of the heavy hitter instructions like MPY, DIV, Shift, BLWP, and others, then those will chew up more time. The 9900 datasheet shows the clock cycles for every instruction, and Classic99 can also should you actual execution time of instructions. Anyway, just say an average of 20 clocks per instruction.

 

Average instruction time: 20 * 333ns = 6.6us

Average instructions per blanking period: 13358 / 20 = 667

 

But it is best to just write your code and see how much you can get away with. ;-)

  • Like 3

Share this post


Link to post
Share on other sites

 

Yes. The first vertical sprite position is >FF (Assembly not XB - there's a reason, but that's another story). Position >FE simply hides the top of a sprite (behind the top border).

...

 

Edit: Just adding additional information. I'm pretty sure sometimes99er is aware of these details.

 

The sprite Y-position is off-by-one because the 9918A has to process sprite information *during* the scan line (as well as during the horizontal blanking period), which it then displays during the next scan line. So the sprites are processed correctly based on their Y-position, but you don't see them until the next line. There is a VDP master timing diagram floating around the 'Net that was provided by Karl Guttag (one of the 9918A engineers) if you want to know the gritty details.

Edited by matthew180

Share this post


Link to post
Share on other sites

You also have to remember that the memory in a normal TI 99/4A isn't running at full speed. Well, inside the console it is, but not memory in cartridges, DSR or expansion RAM. This has an impact on instruction timing.

I've noticed that if both code and workspace is in slow RAM (normal expansion), the execution time will increase by about 110%, compared to if both code and workspace is in fast, 16-bit wide RAM.

The most common mix is of course to run code in slow memory and have the workspace in fast memory.

  • Like 1

Share this post


Link to post
Share on other sites

 

 

Total lines in one frame = 262 lines

 

One frame = 1/60 = 0.0166666 = ~16.6ms

One line = 16.66ms / 262 lines = 63.61us per line

Blanking period = 63.61us * 70 lines = 4.452ms

 

 

 

Minor correction about the details of NTSC video.

I believe the 9918 works this way, but I am not 100% certain. For it to display on an NTSC TV I think it must work this way.

 

Total lines in one "FIELD"= 262

One FIELD = 1/60 = 0.0166666 = ~16.6ms

Fields per frame = 2

lines per FRAME=525

Frames per second=29.97 (color TV)

 

It is an interlaced picture, showing odd lines, then even lines. This was done to reduce bandwidth requirements for over the air transmission, while maintaining approximately 30 frames per second. (it was 30 for B&W TV, but changed to 29.97 to interleave the **color information with the B&W picture. (backwards compatibility)

 

**The story I was told at RCA ,in Camdon NJ, (1981) was that when they did the first test of the color broadcast system for the FCC, their was a moiré pattern running through the picture. The engineer in charge of the demo, tweeked the control that adjusted the 3.5MHz oscillator until the moiré pattern went away and that became the new "correct" frequency for the oscillator. 3.579545MHz :D

 

(I cannot verify the validity of the story, but it sounds credible.)

Share this post


Link to post
Share on other sites

Minor correction about the details of NTSC video.

I believe the 9918 works this way, but I am not 100% certain. For it to display on an NTSC TV I think it must work this way.

 

Total lines in one "FIELD"= 262

One FIELD = 1/60 = 0.0166666 = ~16.6ms

Fields per frame = 2

lines per FRAME=525

Frames per second=29.97 (color TV)

 

It is an interlaced picture, showing odd lines, then even lines. This was done to reduce bandwidth requirements for over the air transmission, while maintaining approximately 30 frames per second. (it was 30 for B&W TV, but changed to 29.97 to interleave the **color information with the B&W picture. (backwards compatibility)

 

**The story I was told at RCA ,in Camdon NJ, (1981) was that when they did the first test of the color broadcast system for the FCC, their was a moiré pattern running through the picture. The engineer in charge of the demo, tweeked the control that adjusted the 3.5MHz oscillator until the moiré pattern went away and that became the new "correct" frequency for the oscillator. 3.579545MHz :D

 

(I cannot verify the validity of the story, but it sounds credible.)

 

Actually, the TMS9918A operates in non-interlaced mode (see Thierry’s site). I know enough about the electronics involved to get me into trouble and not enough to get me out, but I seem to recall @Tursi explaining this as the reason most modern TVs/monitors could not handle the signal properly.

 

...lee

  • Like 2

Share this post


Link to post
Share on other sites

Anyway, just say an average of 20 clocks per instruction.

 

Average instruction time: 20 * 333ns = 6.6us

Average instructions per blanking period: 13358 / 20 = 667

 

This is amazing to discover. I never looked into the numbers but guessed I probably had time for a dozen or so instructions. Makes my day.

 

 

Sent from my iPhone using Tapatalk Pro

Share this post


Link to post
Share on other sites

TMS9918A Datasheet, pg. 3-8, sec 3.6.2, third paragraph:

 

"The TMS9918A/9929A operates at 262 lines per frame and approximately 60 frames per second in a noninterlaced mode of operation. The TMS9929A operates at 313 lines per frame and approximately 50 frames per second in a noninterlaced mode of operation."

 

An NTSC video field is actually 262.5 lines, for a total of 525 lines per interlaced frame. The 9918A does not generate that final half line, which causes every field to always start at the same time after vertical refresh. Basically every field is drawn on top of the last instead of being offset by half a line (which would allow the slight vertical displacement required to interlace the fields).

 

Displays like old TVs were very tolerant of loose timing like this and would just sync to the video they were given as best they could. Modern displays (TVs) seem to have a harder time with this, and as mentioned, some can't deal with the missing half a line.

  • Like 1

Share this post


Link to post
Share on other sites

I guess it would be difficult for the 9918A to generate an interlaced picture when we know that the sprites are processed on the line before they are displayed?

 

What's interesting for me is how this affects the experience on a modern TV. If you play a game that scrolls the screen horizontally at 60 frames per second, do you only see every second frame and/or is every second line displaced?

Share this post


Link to post
Share on other sites

I don't think it affects modern TVs much at all. TVs always were (are) 60Hz (or 50Hz PAL), it is just that a full "frame" was split into two "fields" (even and odd), which means the "frame rate" is 30Hz, but the refresh of the display is still 60Hz. You see every 60Hz field the 9918A is generating. Starting the even/odd fields at half-a-line from one another is just a function of the video signal itself and not really the sync timing.

Share this post


Link to post
Share on other sites

You do get a little weirdness on displays that try to be too smart... for instance when I released the flicker color demo for Convert9918's half-multicolor mode, my LCD TV tried to de-interlace the image, and gave me alternating lines of color instead of flickering them. But it was imperfect, sometimes the deinterlace would blend instead, and then back again. In short, it looked awful. ;)

 

But for the most part, you don't see anything too weird. Sometimes a combing effect caused by the above on things that change at 60hz, not not often.

Share this post


Link to post
Share on other sites

Is there any reason these two ways of checking for vsync should give different results?

Edit: and I'm not talking about the end value of r12 but whether they wait the same time.

vsync  movb @vdpsta,r12
       andi r12,>8000
       jeq  vsync

vsync  movb @vdpsta,r12
       jgt  vsync
       jeq  vsync

Edited by Asmusr

Share this post


Link to post
Share on other sites

Will the faster of the two be more likely encounter the hardware race or hazard?

Edited by Airshack

Share this post


Link to post
Share on other sites

The second variant is what I have used in Pyjamarama. It is failing on my TI, missing a large number of interrupts, but is apparently working on other people's computers. If I change to the first variant Pyjamarama is working fine. The strange thing is that if I isolate the code for the first variant, pulling it into a test program, it works fine on the same computer. I don't know what to make of it.

Share this post


Link to post
Share on other sites

I can say for experience that reading too fast the status register misses interrupts in Z80, even more as the VDP gets hot. It's so weird that I prefer *always* to use interrupts.

 

I'm surprised that polling in TI99/4A works just fine. Glad I didn't use the 2nd variant in Borzork or still I would be crashing my head against the wall.

  • Like 2

Share this post


Link to post
Share on other sites

Is there any reason these two ways of checking for vsync should give different results?

Edit: and I'm not talking about the end value of r12 but whether they wait the same time.

vsync  movb @vdpsta,r12
       andi r12,>8000
       jeq  vsync

vsync  movb @vdpsta,r12
       jgt  vsync
       jeq  vsync

 

What happens if you leave out "jeq vsync" in the second one?

Share this post


Link to post
Share on other sites

What happens if you leave out "jeq vsync" in the second one?

 

Then, as far as I remember, it also fails on the F18A where the 5th sprite bits can be zero.

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