EDIT: Dodgamnit, PVB, do you HAVE to shrink images? Fother mucking shiece of pit… *Grumbles*
DogP wrote:
Does anyone disagree with this?
I think your oscilloscope output pretty much precludes anyone’s suppositions. (-:
Yes, it makes sense that the systems be synchronized. With one machine being clocked by the other, it guarantees that frames are the same length and that frames begin and end at the same time. This is important when two systems are interacting with each other.
Here’s the diagram mentioned in the previous post:

The goof here is that pins 5 and 6 can’t both be connected, because then both systems would try to synchronize against each other, and you’ll end up with exactly what DogP’s graph indicates: you get a constant high signal that feeds back into the machine that first generated it and then nobody gets a clock signal.
Here’s what the other pins mean:
Pin 1 Control – I believe this is the “COMCNT” line, which is a 1-bit line that can be set or cleared by either device. Help me out here DogP, because I was never clear on its purpose: isn’t communication controlled automatically when the master system sets C-Start in CCR?
Pin 2 +5V – Just as it says: a five-volt DC line. This is not connected between systems (as both systems generate voltage on this line), but can be useful if any non-Virtual Boy device were to be connected. The Game Boy Printer comes to mind.
Pin 3 Clock – This is depicted traveling in only one direction. This pin corresponds with the “COMCLK” signal and is only sent on the master system. The slave system receives this clock signal and uses it to synchronize communication transfers. Programs have to decide for themselves whether the device should be transmitting the clock signal or listening for it. Perhaps that’s where pin 1 comes in handy, but I’m still foggy on that. The only alternative I can think of is a UI menu along the lines of “Host a new game” versus “Connect to a host”
Pins 4/8 Data In/Out – What it says on the tin
Pins 5/6 Synch In/Out – Also what it says on the tin. As per DogP’s experiments, one of these pairs should be left open.
Pin 7 GND – Ground
A revised diagram is as follows:

So what do you think, DogP, is this consistent with what you know about the COM port?
Much to my dismay, disabling a window for one display did not increase the amount of pixels that could be drawn. Using one affine window for each eye effectively halves the screen real estate that can be rendered without entering overtime when compared to using one window in both eyes.
Okay, it’s official. In the interest of keeping things simple, even after two weeks of planning a new game, I’ve opted for a different project. It’s still a game, but I’m thinking it’ll remain a sekret for a while longer. (-:
It does, however, satisfy one of the suggestions in this thread.
Lester Knight wrote:
The VB needs more platformers. a 2D styled platformer would be a lot of fun.
I originally said I got ya covered, but I’m reconsidering if I’d rather go with a different idea.
-
This reply was modified 12 years, 10 months ago by
Guy Perfect.
I’ll go first! Naturally, I’ll be making a submission. It will be a game, and it will be a demo for the devkitV810 and libvue projects I’m working with dasi on. While the devkit and library aren’t secret, my project still is. (-:
Just a word of warning, I don’t let up. I want everyone to put their best effort into their projects, ’cause I’m going to with mine and I’ve done this before. (-:
As soon as we can load homebrew on the thing, there will be Virtual Boy on 3DS.
Use #4 x 1/2″ wood screws. They have a pointy tip, but they’re not so long that it’s a problem. I used ’em in all three of my units and they work just fine. Same thread density.
Smileys in text are used to indicate humor. They follow passages or messages that aren’t intended to be taken seriously.
DogP wrote:
Why did you modify the crt0, when you had such an amazing and intuitive control mechanism for it?
Configuring the ROM wait controller to use 1 wait makes sense: it speeds up cartridge reads. The register can be configured directly by the C program, but I don’t see any compelling reason to set it to 2 waits, much less have the default (via crt0) be 2 waits.
DogP wrote:
x++; is pretty archaic too, right? Is the libvueian way to do it x=addOneToThisValue(x); ? That’s too advanced for me… I’ll just stick with my stone and chisel.
Why use “HW_REGS[WCR]|=ROM1W;” when you can say “*(char *)0x02000024 = 1;”? In fact, why use a keyboard when you could just tap a couple of wires together to produce 1s and 0s? /oldjoke
DogP wrote:
On a serious note, is the last 0 saying that it’s the ROM bit, not the EXP bit? Maybe defining that would make it a bit more clear… everything else is actually descriptive, but a stray, undescriptive 0 looks arbitrary and out of place.
The macro is defined like this:
// Encodes wait controller fields into a bit-packed value. #define vueWaitControl(rom, expansion) ( (rom) | ((expansion) << 1) )
The symbolic values to use for either are as follows:
// Wait Controller Constants #define VUE_WAIT_1 1 #define VUE_WAIT_2 0
DogP wrote:
I still prefer specifically setting the bits, because I can read the dev manual, decide which bits I want to set, and set them, without digging through header files to figure out that what I want to do requires something called VUE_WAIT_CONTROL, vueWaitControl, and an argument of VUE_WAIT_1 (and apparently a 0 for some reason)...
Given the macro definition above, that zero was the parameter for configuring the expansion wait controller. Since I had no preference to how that wait controller was configured, I just set it to zero. As School House Rock always liked to say: knowledge is power!
Though it's worth bringing up that you're describing the same procedure requried to find out that there's an array called HW_REGS, which has a subscript called WCR, which can be configured with a constant called ROM1W. It's a case of the mallard calling the teal a quack. /newtwistonanoldjoke
DogP wrote:
my way (as in the way it's been done until the libvueians decended to Earth to spread their superior knowledge) has the names defined the same as the register names from the manual...
libvue does provide all of the dev manual register names as an undocumented courtesy (VUE_WCR in this case), but a design goal of libvue is to make distance from the dev manual. Partly because that manual is corporate proprietary content, partly because often times the register names aren't any easier to remember than their corresponding bus addresses.
The naming convention chosen for libvue is fairly verbose, and as a matter of aesthetics, no one's particularly fond of it. (-: However, it has been selected because at the end of the day, "VUE_CHANNELS[2].play_control" is easier to remember than "S3INT". It compiles to the same thing, either way. The existence of the vuePlayControl() macro also aids in configuration of hardware registers.
DogP wrote:
But I'm sure you're gonna say that libvue will have full documentation explaining every part of it... yay. My archaic way already does.
Scroll up a few paragraphs to the section about header files.
Not only is libvue being documented for each feature incorporated into it, but tutorials and sample programs are also in the works. It's a bit early to be knocking it based on overlooking a smiley face.
But this isn't PCM related, so look for more info when libvue and devkitV810 are closer to release. (-:
DogP wrote:
I would say “told you so”, but it’s never time wasted to prove something to yourself. 🙂
What you said was that the CPU doesn’t have enough power to make PCM feasible. What I said is that making a Virtual Boy program in C won’t get you enough power to make PCM feasible. I will continue to testify that a VB program written in assembly can be powerful enough to make some level of PCM useful as the sole source of audio in a game.
In C, every “x++” involves both a load and a store, and reaching out to system memory will be the bottleneck thanks to bus waiting. I rewrote the sampling loop in assembly. The *only* RAM access was when loading the sample; even the VIP flag was set in ADTRE to prevent bus waiting. The increase of speed was rather pronounced: roughly 15 times as fast as the C code. However, incorporating that loop into the C program introduced a function call, which drove the required delay parameter down to 8. |-:
Dodgamn, gcc, what is it about function calls that are so gosh darn hard?
The next time I take a look at this, I’m going to code the entire program in assembly to see what happens.
DogP wrote:
BTW… you don’t need to modify the crt0 to set the waitstates… you can set it in your code like: HW_REGS[WCR]|=ROM1W; (where WCR is 0x24 and ROM1W is 0x01).
We libvueians have no need for such archaic, outdated technologies. (-:
VUE_WAIT_CONTROL = vueWaitControl(VUE_WAIT_1, 0);
RunnerPack wrote:
Great work building on the groundwork laid by DogP and HorvatM!
Built nothin’. I did this all from scratch. (-:
RunnerPack wrote:
It would be nice to take a peek at your code, though 😉
What, it wasn’t good enough when I said “writes volume in a loop”? )-:
// Repeat until the VIP handler raises a flag
for (new_frame = 0; !new_frame;) {
// Increment quantum until it matches target
if (++quantum >= target) {
quantum = 0; // Reset quantum
// Write out one sample, returning to 0 when the end is reached
VUE_VSU_CHANNELS[0].volume = resSamples[sample++];
if (sample >= 1000001) sample = 0;
// The source stream happens to be 1000001 samples long
}
} // new_frame
In this case, “target” is the number that gets written out to the screen, and that the user can configure. It’s just a delay between samples. It’s the oldest trick in the book as far as time delays are concerned, but the depressing part is that it only allows for some small number of iterations.
RunnerPack wrote:
Although, given your final analysis, it seems as though, if we want to upgrade the sound hardware on the VB (which I still think hasn’t been taken anywhere near its true potential), we’ll have to take advantage of the cartridge port audio pass-through system the designers built in.
Precisely. I think (and don’t quote me on this) that the analog signal produced by the VSU first passes through the cartridge before heading out to the speakers. Whatever happens inside the cartridge is a matter of IC design, but all the commercial games just directly connect the in and out pins, with no additional audio functionality.
In today’s lesson, we learn that “pitiful territory” is in the ear of the beholder. Turns out 16-level samples aren’t necessarily bad sounds! The quantization error is more pronounced, but holy shnap, it really looks like the sampling rate is more of a factor in sound quality than the sample size.
I pulled out all the stops to accelerate processing. Firstly, I need to get dasi to configure the wait controller in crt0. Second, I used a different -O switch in the compiler, which did seem to help. Last, I sized down the audio stream to 16-level samples, just for kicks.
The result? 40KHz output with a “delay” parameter of 9. Mednafen sounds better setting it at 32. Recall that in the previous post, it was 10 for 22050 (albeit 46-level samples). Due to the reduction in bit depth, the pops and crackles are gone. It now sounds just like the source audio!
Granted, this is still, what, something like 36 “x++”s between samples for 11025hz? I’m still not sure it’s feasible to do a PCM game using C, but I’m rather convinced that the system is physically capable of pulling it off well.
Applicable files are attached to this post.
Attachments:
Okay, I have good news and bad news.
The good news is, Atlas Park Sector at 40000hz fit into the 2 MB ROM size limit that FlashBoy affords, and I got it to render on the hardware! The file is attached to this post. That is to say, the first recording of a Virtual Boy PCM stream with 46-level samples at 40KHz is attached to this post. (-:
I’ve also attached the source clip so you can hear what it sounded like after quantization but before coming out of the Virtual Boy. The hardware recording does have a stray pop and crack that wasn’t in the source clip, and I believe that was from when the output got sampled while the multiple sound channels were being configured.
ALSO attached to this post is a .zip file containing a ROM that plays the clip at 22050hz, more or less, on the hardware. I only did that to reduce the file size, as the 40KHz clip was juuuuust under 2 MB (it exceeded 2 million bytes). I can post the 40KHz ROM if need be, but there’s also the bad news to cover…
Up and Down on the left D-Pad change the sampling delay as before. But this demo doesn’t use the hardware timing; it just uses a loop. The algorithm looks roughly like this:
for (x = 0; x < delay; x++); OutputSample(left, right);
There's some extra code in there to process video frames, but you get the idea.
Now take a look at the delay in the 22050 program. It's 10. Ten. As in, an "x++" loop that runs ten times between samples. And that happens approximately 22050 times a second. Ten.
Knock it down to 11025 samples per second and that loop will run a big ol' whopping 20 times between frames. That takes, what, an addition, a comparison and a branch? Something like 5 cycles total before the C compiler adds its bloat?
I don't know what all the compiler does, but I sure as heck know that the combination of +, < and goto twenty times a second isn't going to be enough to run a game on. You can hypothetically squeeze out some better performance with a strictly assembly-coded program, but who's gonna take the time to make a game in assembly these days? So I'm gonna have to bite the bullet on this one. Unless you drive down the sampling quality (bit depth and sample rate) to pitiful territory, the Virtual Boy just doesn't have enough power in practice to use PCM as the predominant source of audio in games. )-:
Attachments:
DanB wrote:
I just don’t get how simply changing the volume on a constant tone can result in such different sounds?
The sound channel isn’t emitting a tone; it’s emitting a constant voltage. At full volume, for the sake of explanation, let’s say the value of the signal is 1. It could also be said that the signal’s maximum value is 1. Just 1, a horizontal line, infinitely extending to the right as time goes on.
When a sample from a wave comes in, it expresses some fraction of that voltage. Let’s say the sample’s value is 0.5. Therefore, the output voltage should also be 0.5 times the maximum value of the signal, so in this case, the output signal is also 0.5 (since the “max value” is 1).
Now let’s say that rather than changing the value of the signal coming out, we just set the volume to 0.5 (aka 50%) instead. What will that do to the output? It’s multiplied, just like the sample, so the result is still 0.5 * 1 = 0.5.
The input and output don’t change, but the means by which varying levels are instructed to come out of the sound channel changes.
DogP wrote:
Well… quantization noise is caused by quantization error. Quantization error is the difference between the desired value and the value that it’s represented by. So, unless you generate a signal where all the values land right on your 46 possible levels, you’re always going to have quantization noise. You can reduce the quantization error (and therefore noise) by increasing the number of bits.
But if there’s some other way to reduce the apparent quantization error, then we get the best of both worlds: 46 levels, low or non-existent noise. Perfect!
…
But my research has turned up an unfortunate truth: it’s impossible. If a sample calls for a 4, but can only express odd numbers, then the sample necessarily gets distorted. This will cause, in general, white noise at a frequency equal to the sampling rate. Whenever something gets rounded away, the increase or decrease (aka, the quantization noise) is introduced into the stream.
When it’s strictly a necessity to preserve signal when reducing the number of levels (generally described in terms of bit depth), there is a way to do it. But it also decreases the signal-to-noise ratio, since the means by which it is done is by introducing noise. How can that possibly be useful? Take a look:
* The top row is a sine wave, the source signal.
* The middle row is that same sine wave, converted into a stream that has only 3 levels for samples: -1, 0 and 1. It doesn’t much look like a sine wave. In fact, it’s two pulse waves with different duty cycles stacked on top of each other. If you listen to it, it certainly doesn’t sound like a sine wave.
* The bottom row is also a 3-level stream, but with white noise added to the sine wave prior to quantization. The rounding points are -0.5 and 0.5, so the random noise was generated between those two values. The result looks like those pulse waves from the second row, but with a bunch of garbage tossed in.
This is called dithering. While the bottom row is still only 3-level samples, it sounds pretty close to the original sine wave when you listen to it… It also sounds like a bunch of white noise on top of that. The original signal may have been preserved better than the straightforward quantization did, but at a terrible cost.
Attached to this post are sound files for each of the sounds in the image above.
In some cases, the apparent strength of the dither noise can be alleviated by carefully designing noise in the “shape” of the desired spectrum, hence the name “noise shaping”. This generally involves using the quantization error per sample as input, but also involves some hocus pocus and finger-crossing.
The unfortunate truth is this: regardless of how you dither, noise shaping or otherwise, there’s no guaranteed, tried-and-true method that will perform best for arbitrary audio streams. It’s mostly a matter of trial-and-error to see what works best on a case-for-case basis.
In terms of Virtual Boy, it means PCM will always contain noise, unless the PCM signal itself could just as well have been made using the tone generators that the VSU was designed to use. The absolute upper bound of sample levels on the hardware is 10 bits due to how samples are processed. Even if through some mystic arts we could get samples that large out to the speakers, it’s still subject to the unavoidable quantization noise.
So how many bits is enough that quantization noise is negligible? Not 16; CD audio is generally dithered before being finalized. 24 is said to be enough, and at 32 bits, quantization noise will be on the molecular scale, so it’s not worth worrying about.
Attachments:
DogP wrote:
What about the game? Does that get 0% of the CPU? 😉 Like I said… if you’re not doing anything else, it works great, but I don’t want to spend anywhere near 10% of the CPU on audio in a real application.
100% – 10% > 0%, you know. I’m not sure what you’re trying to say.
DogP wrote:
And 50 cycles/sample would be optimistic IMO, if you count all the overhead involved (assuming it’s actually playing a game, firing an interrupt ~40,000x per second)… and if you’re generating the samples on the fly, obviously that’s even more overhead.
In the program attached to the first post, I was using the hardware timer interrupt, which proved to be less than what it said on the label in terms of speed. With the interrupt breaking, stack management and other overhead, it took more than 20 microseconds to get in and out of the interrupt handler.
But that’s if you’re using the hardware timer. My next experiment won’t be using the hardware timer. (And don’t go conjecturing at me that THAT won’t work. I’ll do it, and then you’ll have to come up with an explanation. (-: )
DogP wrote:
Plus, didn’t you say your 8-bit didn’t work, because it took too long?
I said it couldn’t update four sound channel properties for each of the left and right signals without audible side-effects. This is an entirely separate issue from the sampling rate.
DogP wrote:
IMO, the advantage of PCM is for realistic sounds that wouldn’t be generated on the fly, like speech… not for creating a better VSU using the CPU). But in the end, if you make a game that uses PCM for everything, and it works well… that’s all that matters.
Ah ah ah, no conjecturing allowed. That is not the scientific method! If you’re creative enough, you’d be surprised just what can be accomplished, even when others say it can’t be done.
DogP wrote:
If 46 levels doesn’t have sufficient quality, why even consider it? How many bits do you need before you’re happy with the sound? It sounds like that’s the number you should be shooting for.
Did someone say 46 levels had insufficient quality while I wasn’t looking?
46 levels has beautiful quality. Check the sound file I attached to this post. The only problem is that quantization noise. If I can eliminate that, we’re in business.
Attachments:
DogP wrote:
Regarding the 46-level… you’re saying you can get 46 levels in 3 channels, but you need 6 channels to do 46 levels? I quickly skimmed through it, so maybe I’m missing what you’re saying, but are you saying because of stereo, you’re using 3 channels for each? Why wouldn’t you use the L/R levels for stereo (you get 4 bits for each channel independently, right)?
You’re right. My brain was still in “mutually exclusive” mode, which necessitating isolating left and right to their own channels when modifying the envelope and/or wave parameters. If only volume is being adjusted, then you can certainly use one sound channel for both left and right, since the input sample is always the same. Using all 6 sound channels, this gives us 91 levels in stereo!
DogP wrote:
The problem with any PCM though is that it’s processor intensive, and ROM hungry. It’s cool for short effects, where nothing else is happening… like “T&E Soft Presents”, but it’s not really practical for music or in-game special effects. I stuck with 4-bit for the wav converter because the sound is reasonable, you can do 2 samples/byte (5KB/s @ 10KSps mono), and it’s (relatively) easy on the processor.
I wouldn’t say it’s processor-intensive or ROM hungry. You’re making a couple of assumptions that don’t have to be the case. (-:
The physical upper bound on PCM sampling rate for Virtual Boy is 41700hz, which is the sampling frequency of the VSU when converting to an analog signal. Timing techniques notwithstanding, let’s look at that in terms of CPU power. The processor runs at 20000000hz, which is 479.6 times the maximum theoretical rate of the sound samples.
Now, how much CPU does it take to output a sample? Let’s say we’re using the 46-level approach on three sound channels (which is sufficient for stereo!). That’s three writes to the VSU, plus a handful of overhead cycles to prepare the samples. Let’s say, in some terrible worst-case scenario, a sample requires 50 cycles to produce. That still leaves us with 9.5 times the CPU rate compared to sampling rate, meaning 41700hz output is still ~10.4% CPU, which isn’t bad.
41700hz is approaching the rate of CD audio, 44100hz. Even chopping that in half to 20850hz, thus halving the CPU usage, still produces a good sound. Using a simpler sampling method, like 31- or 16-level, will further reduce CPU usage.
As for being ROM hungry, what novice tries to use uncompressed wave files for music on a system like Virtual Boy? It’d be far more effective to use sampled instruments and generate a PCM stream at run-time using the other 90% of the CPU. (-:
DogP wrote:
BTW, are you sure that the RAM is unsigned? It’s been a long time since I’ve looked at any of this sound stuff (I’m pretty much going by memory on all of it), but I seem to remember looking at the output on the scope, and determining that it was signed. But I could be mistaken…
Absolutely. Here’s a wave pattern I just tested:
And here’s the signal that the hardware produced:
DogP wrote:
Oh, and I’m not sure that I’d jump to blame quantization error for the static sound. I’d run a few tests first. A simple one would be to make a wav file on your computer with the same 46 levels and see how it sounds.
Jumping to conclusions would be foolish. That’s why I made a wav file on my computer with the same 46 levels to see how it sounded. It contained the same static that I heard on the Virtual Boy. (-:
So I’ve been scrounging around on the internet to find out the mysterious source of the staticy noise in my sound clips. And I’ve found what it is, what causes it, and to an extent, what to do about it!
When reducing the sample size of a stream, a process called quantization, you won’t always be able to land in good spots. Drawing samples down from 16 bits to 8 bits tends to work pretty well, since there’s an evenly-spaced multiple there. But in this case, I’m drawing a 65536-level sample down to a 46-level sample, so there’s going to be a lot of “in betweens” that get messed up due to rounding. This messed-up-ness is referred to as the quantization error.
There’s a method called noise shaping that takes the quantization error for each sample in a stream and feeds it back into the input prior to quantization, which supposedly helps reduce the apparent noise in the output. Supposedly. I found all sorts of sources that were all “You can use it as feedback yaaaay!” but not a one of them told me how to apply it, where the sample rate figures in, etc. If anyone knows more about noise shaping, I’d love to have a pointer or two.
The other thing that will help, especially in conjunction with noise shaping, is to use a low-pass filter to blot out the noise in frequencies above the maximum signal frequency. Noise shaping, I’m told, can shove the quantization noise up into the high ranges, meaning a low-pass filter can snip it right off at the neck.
But that’s neither here nor there. I won’t be able to progress until I get some form of noise shaping working.
blitter wrote:
Nice. I had not realized this. Could definitely do six independent PCM voices then!
If you control a PCM stream, you can put as many voices as you want in it. You won’t need multiple sound channels in that regard. I’m only using them here to increase my sample size.
After consulting with the scriptures, it would seem that 8-bit PCM doesn’t want to happen.
* First of all, as it turns out, the product of volume * envelope actually loses its lower 3 bits. If not both of the operands were 0, then 1 is added to the result. This drops the total number of valid products from 90, which I thought it was, down to 27 on the hardware.
* It’s still possible to generate 256 evenly-spaced levels, using all 5 wave patterns and combinations of volume and envelope. But the process of setting all of those values on the sound channels at run time is even more prohibitive than my previous attempt: the static almost completely drowns out the PCM clip.
So as far as 8-bit audio is concerned, bus latency is the limiting factor. It simply takes too long to set that many properties on the sound channels that the time between sets becomes audible.
Analyzing my resampled source clips, I discovered that the 16- and 46-level runs did indeed contain static in the source audio, and that it wasn’t being caused by playing the sounds on Virtual Boy. I unfortunately haven’t been able to craft a noiseless version of Atlas Park Sector for demonstration (once converting into 46-level samples), but other, less-noisy files sounded great.
All that being said, what should be possible is to generate sounds and music notes at run-time using a wave table, and producing sounds in that fashion is much more resilient when it comes to noise production. That’s an experiment for another day, but I’m going to go on record saying that it IS possible to do noiseless PCM in this fashion, and doing so is a feasible means of providing PCM audio to games.
HorvatM wrote:
[…] a giant C header file […]
What we’re doing with libvue is we’ve included a macro that makes use of the assembler’s .incbin directive to include resource files directly. You may want to consider doing something similar.

