_n_________________
|_|_______________|_|
|  ,-------------.  |
| |  .---------.  | |
| |  |         |  | |
| |  |         |  | |
| |  |         |  | |
| |  |         |  | |
| |  `---------'  | |
| `---------------' |
|   _ GAME BOY      |
| _| |_         ,-. |
||_ O _|   ,-. "._,"|
|  |_|    "._,"   A | hjw
|    _  _    B      | `97
|   // //           |
|  // //    \\\\\\  |
|  `  `      \\\\\\ ,
|________...______,"

Intro

Hi, at this time nothing changed I still wanting do things for fun so I thinked why we are not playing GameBoy in terminal yet? and this idea dont disappeared from my mind until I have the ideas presented in this text. Its well know that we can render things like images or even videos in terminal which supports true color, so for our thing here I assume a terminal which supports true color, and then our problem become more simple because there is a great amount of opensource projects which help to convert/display visual media on terminal. The plan is to combine anything opensource that will enable us to play gameboy, and the strategy is very simple, we just need to have a emulator running and sending frames to some other program in terminal instead of a X11 Window. For the emulator I choose the visualboyadvance-m since its opensource and good option to ptatch, and the program which will receive the frames will be mpv.

Patching Emulator

I imagined that this task would be very difficult, but in fact the patch is a single line of code that I will show soon, but before this its important to give a tip for people which are wanting to play with patchs etc. I had no idea where in the code the emulator is rendering the GBA display, from github repository its easy to see its using libraries like SDL2, wxWidgets, libretro.. etc, but I never used any of those so how to proceed in this case?
sh-5.1$ grep -sri framebuffer src/ src/wx/panel.cpp: // currently we draw the OSD directly on the framebuffer to reduce flickering ...
Above I just show the moment of win, but I tried to grep the source with other words before like "display", "render" etc, all give a great amount of results, but when I tried framebuffer, the unique result which is not in any library code is src/wx/panel.cpp, reading the code in this file you can easily find the functions which drawthings are all here, and they are more than a single version because the emulator can also use opengl mode, to make my life easy I will patch the basic version which is the "normal mode", you can see the entire function here At the end of the function its possible to see a call to DrawImage(...), which is in fact the exact part of the code which draw each frame to the screen, it receive the frame/image as a object of class wxImage, we are luck because this class have a method SaveFile(...) which receive a string which is the filename to save the image and the format type to save. So finally here is the patched functions:
void BasicDrawingPanel::DrawArea(wxWindowDC& dc) { ... im->SaveFile(wxString("myfifo"), wxBITMAP_TYPE_BMP); DrawImage(dc, im); delete im; }
After this, I recompiled the emulator following README instructions, and it worked as expected, the frame are being saved to file 'myfifo', someone probably guessed why I named it myfifo but it bring us to the next part.

Redirecting frames

Ok cool, we have all the frame as images, but the emulator will overwrite the file every single frame being created, the first and unique way til the moment I thinked to overcome this problem is by using a FIFO, so before running the emulator I just created a fifo in the same place using this same filename. I supposed that the SaveFile() will not check if the file already exist and will just open it and dump the data, I dont checked the SaveFile() code but it really worked as expected, to create the fifo you just need to do:
# mkfifo myfifo # ls -la myfifo prw-r--r-- 1 root root 0 Jul 29 19:27 myfifo
Its simple usage of mkfifo program, ls was used just to confirm that the fifo was created, you dont need root to do this, I was root in the example because I was doing my tests using docker container. Now from another terminal, can be another panel if you use tmux or screen, you should go in the same directory where your emulator executable and 'myfifo' file are and run the following command:
$ while true;do cat myfifo ; done | mpv - -vo=tct
Get back on first terminal and just run the patched emulator passing the path to some ROM, in my case I will just run pokemon firered which I have saved as poke.gba.
# ./visualboyadvance-m ../../poke.gba

Result



Endless considerations

I tried to make this post to dont get much big, if you followed the instruction exaclty as I said you noticed that the emulator will create his window and display the graphics, but in a environment without a display it will not work, this is correct but there is a simple way to fix this using Xvfb, you just need to fake a display and run the emulator setting the display started by Xvfb. Another thing is that if you cant input after you get focus out of emulator window, its not a great problem because it have a configuration which enable you to input after focus is out, the configutarion file defaults to ~/.vbam/vbam.ini, you should change the allowKeyboardBackgroundInput option to 1. Another thing you should consider is that the number of rows x cols on your terminal matter in regard the quality of the frames being converted and displayed, I dont have I recipe for the best combination, but the simple rule is if your decrease the font size you will increase the rows x cols and it will increase the resolution of your frames, but if you increase it so much mpv will have hard time to write millions of characters/ansi sequences to terminal, then you should decrease the fontsize but also decrease the size of your panel or terminal window and you will reach the best possible configuration. Another.... there is always someway to improve something, but for now I just deserve you all have some fun :)