GuyPerfect, these questions are mainly for you, since you’ve actually tested a VB on the bench.
I wish to time a gfx routine that I wrote, and I want to synchronize it to not necessarily what the VIP thinks is the start of a new frame, but the time immediately after the LED array turns off and the mirrors start slowing down to reverse direction- i.e. what the scanner/mirrors think is the “start of the time where it’s safe to draw”.
I’m having trouble actually visualizing the process of one frame, however (there IS a lot going on inside the scanner), but I think if we take it a few questions at a time, I’ll be able to understand the process. Let’s start with a few I have offhand.
1. If you go to page 3-3-1 of Nintendo’s manual, you’ll see a nice little graph of what the mirror’s oscillation looks like. There are 10 labelled points on this graph.
Drawing occurs between points 4-6 and 8-10. Am I supposed to interpret 4-6 and 8-10 as “the same LED array being powered on twice”, or “the left LED array is powered from 4-6” and “the right LED array is powered from 8-10”?
I’m guessing it’s the latter, but the graph is misleading. It makes me think that the graph represents only the movement for either the left or right mirror, not a composite. It also doesn’t make much sense for the same LED array to trigger twice in one, since the LED array would illuminate columns at unevenly spaced intervals as the mirror made a sweep back to its initial position.
2. On the same token however, the same page states that the mirrors oscillate 180 degrees out of phase. If the graph is a composite of both mirror movements, this seems to imply that the mirrors are NOT out of phase- just that the LEDs are triggered while one mirror is moving in one direction, and while the other mirror moves in the opposite direction.
This would be so much easier with a visual demonstration! So here’s some bad ASCII art. Which best describes the physical movement of the VB mirrors under normal conditions?
/ \\ => | | => \\ / => | | => / \\
OR
\\ \\ => | | => / / => | | => \\ \\
3. Looking at the same graph, at what points 1-10 are SCANRDY and L_SYNC and R_SYNC asserted if it is known (page 3-6-1)?
This should be enough to get me started!
I don’t own an oscilloscope, so take the following with a grain of salt. It’s just my own interpretation of the IntSys docs.
1. “the left LED array is powered from 4-6” and “the right LED array is powered from 8-10” (or vice versa).
2. The images are drawn consecutively, not concurrently. While one mirror swings left-to-right, its LED array is used to draw an image from its frame-buffer, and the other mirror is swinging right-to-left to get back into position to have its image drawn. This is the rising slope of the sine wave. The mirrors then turn around, and the process repeats with the two eyes swapped, during the falling slope of the sine wave. The first of your ASCII filmstrips is the most accurate depiction. This is the most logical assumption, since the counter-balancing effect of this arrangement would minimize vibration of the whole unit.
3. !SCANRDY (active-low or falling-edge) would be at both 4 and 8. L_SYNC is somewhere near 4, and R_SYNC is near 8 (or vice versa). I don’t know if they’re synchronized with !SCANRDY.
Anyone able to actually test any of the above should feel free to correct me.
While I don’t have eyeball precision on the millisecond level, I can at least convey what I’ve picked up from the documentation. Whether this is 100% correct remains to be seen, so make of it what you will. (-:
cr1901 wrote:
Drawing occurs between points 4-6 and 8-10. Am I supposed to interpret 4-6 and 8-10 as “the same LED array being powered on twice”, or “the left LED array is powered from 4-6” and “the right LED array is powered from 8-10”?[…]
On the same token however, the same page states that the mirrors oscillate 180 degrees out of phase. If the graph is a composite of both mirror movements, this seems to imply that the mirrors are NOT out of phase- just that the LEDs are triggered while one mirror is moving in one direction, and while the other mirror moves in the opposite direction.
It is my understanding that the left display is scanned first, followed by the right display. A high-speed camera can probably verify this.
The mirrors oscillate with a half-period offset to maintain physical balance: Newton’s first law comes into play and prevents the device from tipping over. While the specifics of the scanning hardware are a mite foggy, I get the impression that scanning only occurs during the same motion on each mirror, meaning one display is scanned before the other.
cr1901 wrote:
I wish to time a gfx routine that I wrote, and I want to synchronize it to not necessarily what the VIP thinks is the start of a new frame, but the time immediately after the LED array turns off and the mirrors start slowing down to reverse direction- i.e. what the scanner/mirrors think is the “start of the time where it’s safe to draw”.
The VIP interrupt registers at 0x0005F800, 0x0005F802 and 0x0005F804 actually accommodate this out of the box: bit 14 (0x8000) occurs when the VIP finishes drawing to the framebuffers (meaning it’s safe to reconfigure characters, windows and objects before a frame is actually displayed), and bit 2 (0x0004) occurs when the right display has finished scanning (something you’ll need to know when writing to the framebuffers yourself).
What’s the difference between FRAMESTART and XPEND? I was confusing the two, and in fact if I sync to FRAMESTART (at which point, VIP calculations should occur), virtually nothing gets drawn to the screen. Are the display buffers locked while the VIP is doing calculations?
I set: WA[31].head = WRLD_END;, so the amount of time taken for the VIP to draw should be trivial (the only thing I can think of is that the VIP doesn’t acknowledge WRLD_END before my test routine is done plotting to possibly locked display buffers). Even the VB manuals says it’s safe to manipulate framebuffers after the VIP is done drawing.
cr1901 wrote:
What’s the difference between FRAMESTART and XPEND? I was confusing the two, and in fact if I sync to FRAMESTART (at which point, VIP calculations should occur), virtually nothing gets drawn to the screen. Are the display buffers locked while the VIP is doing calculations?
FRAMESTART occurs at the beginning of frame operations. I trust you know the difference between a display frame and a game frame–FRAMESTART applies to display frames. Assuming you’re using 1 display frame per game frame (FRMCYC = 0), then FRAMESTART will also signify the beginning of drawing operations every time it’s thrown (otherwise, use GAMESTART). In this situation, writing to the framebuffers will just cause your data to be overwritten by the VIP when it carries out its drawing routines.
XPEND is thrown at the end of the VIP’s drawing routines, when it is no longer using character, map, object or window memory. Most software should use this interrupt condition to synchronize program flow with the display rate (50 times per second). If you’ll be writing to the framebuffers yourself, now’s the time to do it.
Whether framebuffer memory is locked during the drawing procedure, I haven’t tested it. Do some profiling for science!
cr1901 wrote:
I set: WA[31].head = WRLD_END;, so the amount of time taken for the VIP to draw should be trivial […]
My brain says that this should work, but make sure you’re syncing to the XPEND interrupt.
It’s important to remember that even when all windows are disabled, the VIP still writes to the framebuffers because of the clear color (set by BKCOL). Maybe this is what you want, since it gives you a blank slate and you don’t have to update every pixel in the image yourself.
If you don’t want the VIP messing with the framebuffers at all, simply disable drawing (XPCTRL.XPEN). Having said that, while I haven’t looked too deep into it, I seem to remember that the front/back bit for which framebuffers to scan doesn’t toggle each frame unless drawing is enabled, meaning you’ll have to write to the same left/right framebuffers every frame (I think the default is 0, at bus addres 0x00000000).
Of course, if you do disable drawing, then the XPEND interrupt will never be raised. In this case, FRAMESTART is the one you want.
https://github.com/cr1901/vbdemo/commit/bc7e1661e6866ed3376794c659a64428798432c3
If I compile the above unoptimized ROM and run it on a real Virtual Boy, I get different behavior compared to Mednafen. On a real VB, only whole lines are not drawn (and curiously, the lines which do not get drawn vary despite me syncing to XPEND). On Mednafen, parts of lines may be drawn before the scanner displays a column before this little program can plot the next point in a line.
I’m not sure how to interpret this, but it suggests to me that VRAM *is* in fact locked during drawing, or that the framebuffer display start is in general happening in between function calls to draw_line (plausible- create_point() spills into memory)
To save trouble compiling using SCons (it does require a bit of setup if you’re not used to it), I have provided the unoptimized ROM. The lack of optimizations is deliberate for now- there are more lines drawn than there is time to draw them (works fine on -O2; there are also plenty of optimization opportunities in O0). Also, create_point() isn’t exactly lightweight in its current form. When -O2 is enabled, the output looks like this:
I need to take hi-speed video of this. Seriously, do NOT watch on a real Virtual Boy for prolonged periods of time. I’m not responsible for any headaches.
Attachments: