GBC IR test is an interactive test Gameboy Color ROM that allows you to test various aspects of the IR port found on the console. This can be useful if you're developing hardware or an emulator targeting the GBC's functionality. It can also be useful for device troubleshooting if you have a GBC where you think the IR circuitry is not working. Or, you might want to try it just out of curiosity. There's also some documentation of the IR hardware used by the GBC and its principle of operation.
Prerequisite: some recent version of RGBDS, preferably 0.6.1 if you experience syntax errors from deprecated syntax in newer RGBDS versions. Under Linux and presumably macOS, just run ./m.sh. Under Windows, run m.bat. No Makefile, sorry.
This document is sometimes using the binary kibi (ki) and mebi (Mi) prefix which are convenient because the GBC is using a binary clock frequency, and RAM is organized in multiples of this unit. For example: 1 kiHz = 1024 Hz. 1 MiHz = 1024*1024 = 1048576 Hz. 8 kiB = 8*1024 = 8192 bytes.
This document is referring to hexadecimal values using the $ prefix, in line with the syntax of the RGBDS assembler that was used for writing the test ROM.
- GBC = Gameboy Color.
- IR = infrared. Invisible light with a wavelength that's just slightly longer than the visible spectrum.
- LED = light-emitting diode, here referring to the LED emitting IR light on the GBC.
- TX and TXON = transmit, and transmit on.
- RX = receive, referring to the act of receiving a signal.
- RCV = also receive, but here specifially referring to the two configuration bits for the receiver circuit.
- PPU = pixel processing unit, the "GPU" of the Gameboy.
- SoC = System-on-a-chip. The GB Dev community has decided to adopt this terminology to refer to the "CPU" chip of the GB. The reason is that we thought using the term CPU was slightly incorrect seeing that the the same chip also has other peripheral units for sound, graphics and communication, similar to modern systems-on-a-chip.
- DUT = device under test, referring to the device running the test ROM.
- DC = direct current, here referring to a constant, unchanging signal.
- AC = alternating current, here referring to a signal that's changing over time.
- SRAM = save RAM, the battery backed RAM available on some game and flash cartridges.
This ROM requires a GBC, or a device/emulator capable of running GBC ROMs. Older Gameboys, as well as GBAs won't work due to lacking the IR hardware present in the GBC. If you're developing an emulator or hardware implementation of the GBC, this ROM requires certain things in your emulator/hardware implementation of the Gameboy SoC to be present and correct in order for this test ROM to work. Namely, it's expected that the CPU, PPU and interrupt handling are all generally working mostly correctly.
This is a schematic image of the GBC's IR circuitry from the GBC service schematic. It shows the LED transmitter and receiver circuitry. This circuit could be implemented in various ways that are functionally identical for the way used by a typical game, but some important points are outlined below, as well as some more obscure details.
Fairly unremarkable, except that the R0 pin seems to have internal current limiting, eliminating the need for a current limiting resistor when driving the base of Q3. Q3 drives a GL382 infrared LED that outputs 880 nm light.
The transmitter is controlled by bit 0 of the RP ($FF56) register. When bit 0 is set to 1, the transmitter outputs light.
The receiver circuit consists of two stages.
The first stage is an amplifier stage. It's using a PT380F phototransistor with a rated peak sensitivity at 860 nm. This signal is then amplified using Q4 in a common base topology, using a virtual ground created by R23 and R24 acting as a voltage divider.
The amplified signal is then fed through C11, which acts like a highpass filter, or if you will, a DC blocking, or AC coupling filter. It passes changes in voltage, but blocks constant voltage. As a result any constant background sources of IR light, such as oldschool incandescent lights or nearby stars will tend be tuned out. Some variation of a highpass filter would be considered a crucial part of the receiver circuit for achieving stable communication.
The next stage receives the highpassed signal. The circuit creates a voltage that's roughly 2.5 V, which can then be pulled up or down by the AC coupled filter. Theorized mechanism, claim not fully validated: It appears to be an inverter of some kind which takes the voltage at R2 and produces the inverse (Vout=5-Vin) on R3. When no change is detected, it naturally settles at Vout=Vin which is the center point between 5V and ground.
The two stages can be independently powered on or off through software, using bit 6 and bit 7 of the RP register. It is recommended that software running on a GBC turn off the transmitter but also the receiver to save power, as both circuits will use some amount of power when powered on.
Bit 6 controls power to the amplifier stage, whereas bit 7 controls power to the digital sensing stage. The recommended configurations for those bits are 11 (both bits set high) when receiving IR, or 00 (both bits set low) when not in use. A program could however also set this to an invalid configuration where only one of the two stages of the circuit are powered on. This can have some quirky side effects, but is useless for receiving IR communication, and no game is known to use these modes.
For example, setting this value to 10 (turning on the digital sensing stage but turning off the power to the amplifier stage) will result in bit 0 continually reading a logic low, which would normally mean receiving a signal. From my reading of the circuit, this happens because digital sensing stage needs to be biased by the R15 pullup resistor to return the default value of logic 1. If the power to the amplifier stage is turned off, there's no voltage available at the R1 bus, and the input is pulled toward 0V.
While not part of the IR circuit per se, the unused pin of the link port (pin 4) is connected to the CPU, and exposed through as a digital input. It can be read as bit 4 of IR IO register (RP). If the input pin is grounded, bit 4 reads 0. If the input pin is connected to +5V, bit 4 reads 1. If left unconnected, it also reads 1 due to an internal pullup resistor.
Exception: On CGB-CPU-06 boards, bit 4 of RP reads 0, regardless of the state on pin 4 of the link port. The pin has possibly been disconnected and grounded in this last GBC board revision.
This undocumented pin is known to be used by the homebrew music ROM Pushpin, which uses it for receiving a MIDI signal. The GBC IR test ROM doesn't contain any tests specific to this bit, but it's indirectly included when using the record function.
Press up or down to change to a different mode. All modes take effect immediately without a separate action needed to activate the mode, except the record mode which you must arm by pressing A.
Hold select and press left or right on the D pad to change the RCV bits. (See "Valid and invalid power configurations..." for more information.)
If send mode is selected, press left or right on the D pad without holding a separate button to select which signal is being transmitted.
In the top right corner, the 4 hex digit checksum is shown to denote the build version. This is the same value as in the ROM header, assuming the ROM wasn't modified and there are no issues reading the ROM. To the left of the checksum, a single letter is shown, C for GBC mode or M for monochrome mode. Note that this is simply done by checking for A==$11 at startup, meaning that if the test ROM were somehow to start in the GBC's DMG compatibility mode, it would be incorrectly detected as GBC, even though GBC functionality is not unlocked. The test ROM is only useful on a GBC (or compatible device) running in GBC mode.
This controls the bits 7 and 6 of the receiver circuit, as discussed in "Valid and invalid power configurations..." above. It can be changed by holding select and pressing left or right. This setting is applied to all modes. Normally, this setting should be left at 11 to allow data to be received, but it can be changed for testing purposes because this is a test tool.
These modes show the value detected by the IR sensor in realtime by turning an audio channel on or off, or by changing the background color. (Actually technically this is done by changing the background tile source using bit 4 of the LCDC register.) Point an IR source toward the IR window to test IR reception. This can for example be a second GBC running another copy of GBC IR Test, or a TV remote control while pressing buttons on the remote.
These modes are active at any time when a corresponding menu item is selected. There is no separate action required to enable these scope modes.
You can hold select and press left or right to modify the RCV value, which controls bits 7 and 6 of the RP register. Note that data can only be received when RCV is set to 11, the value recommended for turning on the IR circuit. However, this value can still be modified to other values for testing purposes.
Record mode records 128 kiB of data to SRAM using an unrolled loop that copies 8 kiB at a time. This can be useful for offline analysis. Press A to arm for recording. Recording starts when an IR signal is detected. The data is recorded with an unrolled sequence of reading the RP register, for the highest possible sample rate. Note that there's a small gap in time where no data is recorded during the bank switch between each 8 kiB bank.
rept 8192
ld A,[$FF00+C]
ld [HL+],A
endr
Because each sample consists of one byte read from the RP register, a value with bit 1=0 (typically a read value of $FC) means an IR signal was detected for that sample, and a value with bit 1=1 (typically $FE) means no signal was detected for that sample. Note that other values could be present.
For example:
- If the test was run with a different setting in bit 7 and 6, (RCV) that would be reflected in the value that was read. (See: Valid and invalid power configurations...)
- If the test was run on a GBC with a CGB-CPU-06 board where bit 4 always reads as 0, the values would be $EC and $EE. (See: The unused pin 4 of the link port)
You can hold select and press left or right to modify the RCV value, which controls bits 7 and 6 of the RP register. Note that data can only be received when RCV is set to 11, the value recommended for turning on the IR circuit. However, this value can still be modified to other values for testing purposes.
Same as above, but allows you to record while the DUT's own IR LED is on, to investigate the effects of this on the quality of the signal received. The LED is not turned on until you press A to start recording.
This mode reads 4096 samples at a time and performs a statistical analysis of the captured data. This data is then presented visually as a graph to allow you to test the effect of different variables. Since a lot of CPU time is spent processing the data, this mode obviously doesn't sample the IR receiver 100% of the time, but it should still give a good overview.
The analyzes the samples in the received signal and tallies them into three categories:
- Off (black) These are samples when no signal is received.
- On, positive phase (red) These are samples when IR light is detected.
- On, negative phase (blue) These are samples between pulses of IR light, denoted the off time of the carrier wave.
A period of no light detected is considered to be "on, negative phase" if it sits between two pulses of light and is shorter than ~1 ms. The purpose of this is to approximate the detected duty cycle of the carrier wave. You can for example use this with a second copy of IR Test set to the send mode, to investigate the effect distance and angle has on the received signal quality.
This test also continually prints the number of detected pulses (no light to light transitions) each measurement cycle, which can also be used to analyze the quality of the received signal.
You can hold select and press left or right to modify the RCV value, which controls bits 7 and 6 of the RP register. Note that data can only be received when RCV is set to 11, the value recommended for turning on the IR circuit. However, this value can still be modified to other values for testing purposes.
Same as above, but allows you to record while the DUT's own IR LED is on, to investigate the effects of this on the quality of the signal received. The IR LED is immediately turned on when this menu item is selected.
Allows you to send various test patterns, to be analyzed using the other modes. Press left or right to change the send mode.
- 52 kHz. Transmits an IR signal modulated with a 52428.8 Hz carrier wave.
- 38 kHz. Transmits an IR signal modulated with a 37449.1 Hz carrier wave.
- 26 kHz. Transmits an IR signal modulated with a 26214.4 Hz carrier wave.
- 13 kHz. Transmits an IR signal modulated with a 13107.2 Hz carrier wave.
- 52 kHz, Pulsed. Transmits an IR signal modulated with a 52428.8 Hz carrier wave. The signal is on in bursts of ~2.4 ms and off for ~7.3 ms between bursts.
- 38 kHz, Pulsed. Transmits an IR signal modulated with a 37449.1 Hz carrier wave. The signal is on in bursts of ~2.4 ms and off for ~7.3 ms between bursts.
- 26 kHz, Pulsed. Transmits an IR signal modulated with a 26214.4 Hz carrier wave. The signal is on in bursts of ~2.4 ms and off for ~7.3 ms between bursts.
- 13 kHz, Pulsed. Transmits an IR signal modulated with a 13107.2 Hz carrier wave. The signal is on in bursts of ~2.4 ms and off for ~7.3 ms between bursts.
- On (unmodulated). The IR LED is turned on without modulation or duty cycling.
Tests whether the device can receive it's own IR signal. The test runs continuously each frame and works by turning on the LED and waiting for a signal, then turning off the LED and waiting for the signal to stop being received. The ON and OFF counters show how many times correct operation has been detected, and TOT shows the total number of tests performed during the frame, as a hex number. If the number of correct test cycles doesn't match the total count, an exclamation mark is also shown after the value. This test runs continuously as long as the menu item is selected.
Example test results:
- ON:20 OFF:20 TOT:20 The IR transmit and receive seem to be working correctly.
- ON:00! OFF:20 TOT:20 The IR receiver is registering no signal. This could means that either the transmitter or receiver circuit is broken. However, it could also mean that the RCV value is set to 00 or 01, which is incorrect for a normal test. If so, hold select and press left or right until the RCV value is 11.
- ON:20 OFF:00! TOT:20 The IR receiver is registering an always on signal. This could means that the receive circuit is broken. However, it could also mean that the RCV value is set to 10, which is incorrect for a normal test. If so, hold select and press left or right until the RCV value is 11.
- ON:xx! OFF:xx! TOT:10 ON and OFF are jittering between different values, and the exclamation marks are flashing. This likely means there's an external IR signal that's being received. Check for any device that's transmitting an IR signal and try covering up the IR window to see if the issue resolves. You can also provoke this condition deliberately by pointing an IR signal at the device while running the loopback test.
Here are a number of tests that can be performed using this test ROM. If you're making your own FPGA hardware or emulator, some of these can be useful for thorough compatibility testing. For basic testing whether a GBC is working, it would be recommended to use the loopback test to test that the LED and receiver are working, as well as either the send mode with a camera, or a receive mode with a TV remote control or second device running the test ROM. The reason for testing with an external device and not just loopback mode, is that it wouldn't be impossible that an aftermarket shell might ship with an IR window that's actually opaque to IR light.
The GBC cannot reliably detect a constant IR light signal, so this requires help from a camera, like the one on your phone, which can often detect IR light. Activate your camera and point it to the IR window of the DUT. Better quality cameras are more likely to have an IR blocking filter to improve the image quality for photos. On a phone, the selfie camera on the front sometimes has less IR filtering. Bad for photo quality, but it makes it easier to see the IR LED. Above are two example photos with the back and front camera of my phone, a OnePlus Nord CE4 Lite.
- When a mode that is supposed to output an IR signal is activated (such as one of the send modes, or the TXON variations of record or analyze) verify that you see the glow of the IR LED.
- When a mode that is not supposed to output an IR signal is activated (such as the send: off mode, or the normal non-TXON variations of record or analyze) verify that you do not see the glow of the IR LED.
Place the DUT and an assisting device on a table, 50 mm or 2 inches apart, with the IR windows facing each other. Set the DUT to live analyze mode. Set an assisting device to send in 52 kHz, 38 kHz, 26 kHz and 13 kHz modes.
Expected outcome: the graph window should show close to a 50/50 distribution of red (positive phase) and blue (negative phase).
The "number of pulses" figure should not radically deviate from the expected numbers, which would indicate a lost connection. Note: this value is presented in hexadecimal.
- 52 kHz: 0330-0333
- 38 kHz: 0247-024A
- 26 kHz: 0198-019B
- 13 kHz: 0065-0068
Expected result: the device can receive a signal with all of the listed frequencies.
Test various relevant angles and positioning of the two devices.
For longer distances, the blue negative phase portion might become slightly bigger. This can be used as a proxy for signal strength. This does not necessarily indicate an issue for game compatibility as long as the pulse count stays consistent, but this is subject to how robustly various games are programmed.
One odd thing was observed on a real GBC. In the 52 kHz test, if the DUT and assisting device are placed very close to each other, the sensor seems to get swamped and only detect short, spurious pulses. The likely reason for this is a combination of the high amplitude of the signal, due to the close proximity, and the high frequency, which makes it easier for it to pass the highpass filter.
Set DUT to live analyze mode. Set the assisting device to send "xx kHz pls (pulsed)". The signal generated has approximately a 25% cuty cycle, and the graph should show red and blue pulses in approximately the bottom 25% of the window, with the remaining 75% showing as black (off). The ratio between red and blue may vary slightly depending on the carrier frequency.
The height of the graph and number of pulses value will be less consistent in this test because the sampling period will go in and out of phase with the transmitted pulses over time.
Repeat the above tests under all lighting conditions that are relevant.
Repeat the test with a second assisting device, running the same test ROM and set to "send: on (unmodulated)" also pointed toward the DUT, as an artificial source of ambient IR light. Repeat the test with the TXON mode, where the DUT turns on its own IR LED. (This assumes that DUT has its IR LED placed in such a way that it can illuminate its own receiver.)
If desired, repeat the test in presence of incandescent lightbulbs, which are known to emit IR radiation as part of the blackbody radiation. If desired, repeat the test outside in natural sunlight.
Expected result: The GBC has a highpass (DC blocking/AC coupling) filter that removes ambient (non-changing) IR light. The presence of ambient IR light should not have an appreciable effect on the quality of the received signal.
Details about this are described in the "Valid and invalid power configurations..." section. Check that only setting "11" can receive a signal on the DUT. This test is not expected to affect game compatibility. Rather, the point is to confirm that the IR receive circuitry can be turned off. On the GBC, not only the transmit circuitry (which is obviously power hungry) but also the receive circuitry can be powered down to save battery life.
Settings 00 and 01 will show no signal received, whereas 10, due to a quirk in the analog hardware, will detect as a constant on signal.
Put the DUT in "send: on (unmodulated)" mode and leave it running for 24-48 hours. Confirm that the IR LED has not degraded, and that no other damage, including thermal, has been occured to the IR LED or drive circuitry.







