Category: retro games

Explore our Retro Games category and rediscover the charm of classic gaming. Here you’ll find 8-bit and 16-bit inspired projects, handcrafted with nostalgic pixel art, authentic chiptune soundtracks, and timeless gameplay. From Game Boy and NES-style games to modern indie titles with a vintage twist, this is where retro game development comes to life. Perfect for fans of old-school design, nostalgic adventures, and creative indie projects.

  • Setting up your Game Boy IDE

    Setting up your Game Boy IDE

    To start coding your own Game Boy games, the first step is setting up a comfortable IDE. In this post, I’ve put together a list of the most convenient and free tools to help you streamline your Game Boy development process and get your projects running as smoothly as possible.

    Game Boy IDE with VS Code C++ GBDK and Emulicious

    The Tools

    When it comes to Game Boy development, there are several tools out there that you can mix and match depending on your workflow and preferences. However, after trying a variety of options, I’ve found that these three — VS Code, GBDK, and Emulicious — offer the most convenient, beginner-friendly, and efficient setup. Together, they form a smooth development pipeline that takes you all the way from writing code to testing and debugging your Game Boy games with ease.

    The code editor

    When it comes to writing Game Boy games, Visual Studio Code (VS Code) is one of the best choices out there. It’s lightweight, highly customizable, and packed with useful extensions that make coding in C for GBDK much more comfortable. You can add syntax highlighting, project explorers, and even Git integration to keep your development workflow clean and organized. With a few tweaks, VS Code can feel just like a native retro game IDE tailored perfectly for your Game Boy development projects.

    The C++ Compiler

    The Game Boy Development Kit (GBDK) is the heart of your Game Boy coding setup. It’s a cross-compiler based on the Small C compiler, designed specifically for developing games and software for classic Nintendo Game Boy systems. GBDK takes your C code and converts it into Game Boy ROM files that can be played on emulators or even real hardware. It includes essential libraries for graphics, sound, and input handling, so you can focus on gameplay and creativity instead of worrying about low-level hardware instructions.

    Emulator and Debugger

    Once your code is compiled, you’ll need a reliable way to test it — that’s where Emulicious comes in. This powerful Game Boy emulator not only runs your ROMs smoothly but also includes excellent debugging tools. You can inspect memory, set breakpoints, watch variables, and step through your code in real time. These features make Emulicious invaluable for finding and fixing bugs quickly. It’s an essential tool for anyone serious about building polished, professional-quality Game Boy games.


    The Techniques

    Now that we’ve covered the essential tools, it’s time to put everything together. In this section, we’ll go through how to install VS Code, GBDK, and Emulicious, and more importantly, how to link them into a single, seamless workflow. By the end of this setup, you’ll be able to write your Game Boy code, compile it into a ROM, and run or debug it instantly—all from within your development environment. This is where your Game Boy development setup truly comes to life.

    Installations

    VS Code, GBDK, and Emulicious are cross-platform tools. That means they work across multiple different operating systems. They are easy to install and the installation process for each operating system is very similar. This blog, however, will show you how to install them on a Windows machine.

    Installing VS Code

    Installing Visual Studio Code (VS Code) on a Windows machine is quick and straightforward. Simply head over to the official VS Code website, download the Windows installer, and run it. During the installation, you can choose to add VS Code to your system PATH—this option is highly recommended, as it allows you to open files or folders directly from the command line. Once the installation is complete, launch VS Code, and you’ll be greeted with a clean, minimal interface ready for customization. You can then install useful extensions like C/C++ to enhance your Game Boy development experience.

    Installing GBDK

    Installing GBDK (Game Boy Development Kit) on a Windows machine is also a simple process. Start by visiting the official GBDK GitHub page and downloading the latest release for Windows. Once downloaded, extract the contents of the ZIP file to a convenient location on your computer, such as C:\gbdk. Next, you’ll need to add the GBDK bin folder to your system’s PATH environment variable—this allows you to run the compiler from any directory in the command prompt. To verify the installation, open a new terminal and type lcc --version; if everything is set up correctly, you’ll see the GBDK version information displayed. With that, your Game Boy compiler is ready to go.

    Installing Emulicious

    Installing Emulicious is simple and straightforward. Since it’s a Java-based emulator, you’ll first need to have Java Runtime Environment (JRE) version 8 or higher installed on your computer. Once Java is ready, visit the official Emulicious website and download the latest version for your operating system (Windows, macOS, or Linux). The download comes as a compressed ZIP file—just extract it to a convenient location on your computer, such as your development or emulator folder. There’s no installation process required; simply double-click the Emulicious.jar file to launch the emulator. You can then load your Game Boy ROMs directly or integrate Emulicious with Visual Studio Code for instant testing during development.


    Linking the tools

    With all three tools installed, the next step is to make them work together seamlessly. In this section, we’ll connect VS Code, GBDK, and Emulicious so you can build and test your Game Boy projects with a single key press. By configuring VS Code to recognize GBDK’s header files and compiler, and by setting up a custom build task, you’ll be able to compile your code and automatically launch the resulting ROM in Emulicious for instant testing. This integration not only saves time but also creates a smooth, modern workflow that makes Game Boy development feel effortless and efficient.


    First, let’s set a well-defined goal

    Before we move on, let’s set a clear goal to see how these tools work together in action. For this example, we’ll create a simple program that prints “Hello World!” on the Game Boy screen (or in Emulicious). I’ll walk you through each step to make it all come together.

    For this goal, the code is very simple:

    #include <gb/gb.h>
    #include <stdio.h>
    
    void main() {
        printf("Hello World!");
    }

    To write the code in VS Code, start by opening the program and going to File → Open Folder. Navigate to (or create) the directory where you’d like to store your project files—for example, a folder named “game_project.”

    After pressing Enter, there will be a pop-up windows asking if you trust the authors of the files in this folder? Just click yes.

    Visual Studio Code program: pop-up question

    Once the folder is open, look at the Explorer panel on the left-hand side. Right-click inside it and select “New File,” then name the file main.c.

    Visual Studio Code program: Create New File in File Explorer Panel

    If this is your first time using Visual Studio Code, it’s a good idea to install the C/C++ IntelliSense extension. This tool enhances your coding experience with smart features like auto-completion, real-time error checking, and quick navigation. It helps you jump to function definitions, view details instantly, and manage include paths seamlessly—turning VS Code into a lightweight yet powerful IDE for writing, debugging, and organizing C or C++ projects efficiently.

    To install it, click the Extensions icon on the left sidebar (or press Ctrl + Shift + X), search for “C/C++”, then select C/C++ IntelliSense from Microsoft and click Install.

    Game Boy IDE with VS Code C++ GBDK and Emulicious installing IntelliSense C/C++ extension

    Finally, in the code editor pane on the right, paste the example code you wrote earlier. Your project is now ready for compiling.

    Visual Studio Code program GBDK Game Boy Development Kit Hello World

    Setting up paths for header and compiler files

    From the code, you’ll notice an error right away. Now that the IntelliSense extension is installed, Visual Studio Code recognizes that you’re writing a C program—but it can’t find the gb.h header file, which contains the functions used to communicate with the Game Boy hardware. To fix this, we need to customize the include path settings so VS Code knows where the compiler and its libraries are located. If you click on the first line with the red squiggly underline, you’ll see a small light bulb icon appear to the left of the line number. Click on that icon, then select “Edit include path setting” to configure the correct directory.

    Visual Studio Code program GBDK Game Boy Development Kit Hello World

    At this point, you’ll see a new tab appear on the right — you can safely ignore it. Instead, go to the Explorer panel and open the .vscode folder. Inside, click on the file named c_cpp_properties.json. This is where you’ll edit the include path to link Visual Studio Code with the C compiler from GBDK, allowing IntelliSense to recognize the Game Boy libraries properly.

    Game Boy IDE with VS Code C++ GBDK and Emulicious

    Copy and paste the following code in the c_cpp_properties.json file:

    {
        "configurations": [
            {
                "name": "Win32",
                "includePath": [
                    "${workspaceFolder}\\**",
                    "${workspaceFolder}\\..\\resources\\gbdk\\include\\**",
                    "${workspaceFolder}\\..\\resources\\gbdk\\include\\gbdk\\**"
                ],
                "defines": [],
                "compilerPath": "${workspaceFolder}\\..\\resources\\gbdk\\bin\\sdcc.exe",
                "cStandard": "c17",
                "cppStandard": "gnu++17",
                "intelliSenseMode": "windows-gcc-x64"
            }
        ],
        "version": 4
    }

    The code above tells VS Code where to find the gb.h header file. This path will vary depending on your operating system. For example, on macOS, the “name” field would change to “Mac”, and the backslashes (\\) would become forward slashes (/). The “includePath” also depends on where you placed your downloaded GBDK folder. In my case, I stored it one level above my working directory (represented by ${workspaceFolder}) inside another folder named “resources”. That’s why the path begins with .. — to move up one directory before entering “resources”, then “gbdk”, and so on. In this context, the double asterisks (**) mean “include everything” within that folder.

    In addition, you’ll notice that we also tell VS Code where to find the compiler (sdcc.exe) in the setting after “compilerPath“.

    Setting up a custom build task

    After setting up the paths for the header and compiler files, the next step is to tell VS Code how to build the program using the lcc command. The basic command looks like this:

    lcc -o image.gb source.c

    Here, image.gb is the output Game Boy ROM, and source.c is your source file. You can adjust this command to fit your project structure. For example, I want my output file to be named game.gb, my source file is main.c, and I’d like all build-related files stored in a folder called build. My customized command looks like this:

    if (!(Test-Path build)) { mkdir build } ; ..\\resources\\gbdk\\bin\\lcc -debug -o build\\game.gb main.c 

    This is a Windows PowerShell command that means:
    “If there’s no folder named build, create one. Then compile the program with lcc and place all output files in that folder. If the build folder already exists, overwrite its contents with the new build.”

    Now, we just have to create a tasks.json file in .vscode folder by right-clicking at the folder name in the project explorer panel on the left and choose “New File…” and copy and paste the following code in that file:

    {
        "version": "2.0.0",
        "tasks": [
            {
              "label": "Build ROM",
              "type": "shell",
              "command": "if (!(Test-Path build)) { mkdir build } ; ..\\resources\\gbdk\\bin\\lcc -debug -o build\\game.gb main.c ",
              "problemMatcher": ["$gcc"],
              "group": {
                "kind": "build",
                "isDefault": true
              }
            }
        ]
    }

    Notice that we put the lcc command that we discussed earlier in the “command” filed.

    Linking Emulicious to VS Code

    At this stage, if you build the project by pressing Ctrl + Shift + B, VS Code will generate your game.gb file—along with all other debugging-related files—and place them neatly inside the build folder. You can then manually open Emulicious by navigating to its folder and double-clicking emulicious.exe (or, on macOS and Linux, launching emulicious.jar with Java). Once Emulicious is running, load the game.gb file to test your build. However, this multi-step process can be streamlined even further by linking Emulicious directly to VS Code, allowing you to simply press F5 to compile and automatically launch your game in Emulicious—making your development workflow fast, efficient, and effortless.

    First, we need to connect Emulicious with VS Code by installing the Emulicious Debugger extension. The process is the same as installing the C/C++ extension: click the Extensions icon on the left sidebar, type “Emulicious” in the search bar, and then click Install.

    Installing Emulicious Debugger extension

    Next, we need to manually launch Emulicious one more time. Once it’s open, navigate to Tools → Remote Debugging → Enabled and ensure there’s a ✓ checkmark next to Enabled.

    Enabling Remote Debugging in Emulicious.jpg

    Now, switch back to VS Code and press Ctrl + Shift + P to open the Command Palette. Type “Emulicious” in the search bar, then select “Emulicious: Attach to Emulicious” from the list.

    Attach VS Code to Emulicious

    Once VS Code and Emulicious are properly linked, the next step is to tell VS Code to use Emulicious as the default debugging tool. To do this, click the Run and Debug icon on the left sidebar, then open the drop-down menu at the top and select “Add Configuration…”.

    Configuring launch task with Emulicious as the default debugger

    At this point, a launch.json file will be automatically created, and its tab will open in the editor. Add the following code inside that file—it instructs VS Code to automatically launch your Game Boy program in Emulicious whenever you press F5.

    {
        "version": "0.2.0",
        "configurations": [
            
            {
                "type": "emulicious-debugger",
                "request": "launch",
                "name": "Launch in Emulicious",
                "program": "${workspaceFolder}\\build\\game.gb",
                "emuliciousPath": "D://Game Boy//resources//Emulicious//Emulicious.exe",
                "port": 58870,
                "stopOnEntry": false,
                "preLaunchTask": "${defaultBuildTask}"
            }
        ]
    }

    Finally!

    Once everything is set up, launching and debugging your Game Boy project becomes incredibly easy. Simply press F5 in VS Code, and it will automatically build your project using GBDK, then launch Emulicious with your freshly compiled ROM loaded and ready to run. You can now play, test, and debug your game directly from Emulicious’s powerful debugger tools—such as memory inspection, breakpoints, and step-by-step execution—all seamlessly connected to your VS Code environment. This setup transforms the classic Game Boy development process into a smooth, one-button experience.

    The result: Emulicious now launches automatically every time you press F5, displaying the output of your code—in this case, “Hello World!”. This setup makes debugging incredibly convenient, as any changes you make to the code are immediately reflected the next time you run the debugger. With this workflow, you can test, view results, and debug on the fly—all within a single, seamless environment.

    launching Emulicious seamlessly with pressing F5

    And that’s it—you’ve just completed your full Game Boy IDE setup! With VS Code, GBDK, and Emulicious all working together, you now have a modern, streamlined workflow that brings retro game creation into the present day. From writing and compiling code to instantly testing and debugging, everything is just a few keystrokes away. Whether you’re building your first “Hello World!” or crafting a full-fledged Game Boy adventure, this setup gives you the perfect foundation to turn your ideas into playable reality. Thank you & see you around.

  • Building a Casio F-91W Style Desk Clock on the Classic Game Boy with C and GBDK

    Building a Casio F-91W Style Desk Clock on the Classic Game Boy with C and GBDK

    Have you ever thought of turning a Nintendo Game Boy into a digital desk clock? That’s exactly what I did. By combining two of my favorite childhood gadgets—the Game Boy and the Casio F-91W watch—I built a Casio-style clock program using C and GBDK (Game Boy Development Kit).


    First Things First

    This post won’t cover every single detail—it’s more about sharing my journey, design choices, and the coding challenges I ran into. I’ll walk you through the important parts: graphics, logic, and how I structured the program with a state machine.


    Disclaimer

    This is a personal passion project. I’m not affiliated with Nintendo or Casio, and the software isn’t commercially available. It’s just something I built for fun out of nostalgia.


    Why I started this project?

    As a kid, I always wanted a Game Boy but never owned one. Years later, I finally got a few, and instead of just playing games, I thought—why not write my own software for it?

    I’ve also always loved the Casio F-91W. It’s such an iconic gadget from the same era. Combining the two felt natural: a Game Boy clock wrapped in the F-91W’s familiar look.


    Game Boy basics and limitations

    The Game Boy’s screen is tiny—just 160×144 pixels, arranged in 20×18 tiles of 8×8 pixels each. Graphics come in two parts:

    • Background → static elements (UI frame, labels).
    • Sprites → dynamic elements (digits, icons, blinking cursors).

    But there are limits:

    • Max 255 background tiles.
    • Max 40 sprites total, with no more than 10 per horizontal line.

    Sprites are either 8×8 or 8×16 pixels. Bigger objects (like digits) are built from multiple sprites (a “metasprite”).

    Because of these limits, I couldn’t rely only on sprites for large digits. Instead, I combined them with a Casio-style frame in the background, which makes the numbers stand out and avoids empty-looking space.

    The Game Boy definitely has its quirks, but that’s part of the fun. With a little creativity, there’s always a workaround. So yeah, I could’ve made a simple digital clock that only showed day, date, and digits purely out of sprites. It would’ve worked fine—but with those limits, the numbers would’ve looked tiny and a bit lonely. That’s why I wrapped them in the familiar face of the Casio F-91W. The watch frame doesn’t just “fill the empty space,” it actually makes the time display pop.

    The result: a functional Casio F-91W style desk clock running on a Game Boy.

    A classic Game Boy console running a digital desk clock program in Casio F-91W style with the actual Casio F-91W watch on its side.
    Casio F-91W desk clock on Game Boy and the Actual Casio F-91W

    Development Tools & Concepts

    • Programming Language: C
    • Game Boy SDK: GBDK (a C library with tools for retro game development)
    • Graphic Design: Aseprite (a great software for creating pixel arts)
    • IDE: Visual Studio Code (one of the best free IDEs for many programming languages)
    • State Machine Design Pattern: To systematically manage modes like time-setting, time-keeping, and alarm-setting
    • Memory Banking: To switch ROM banks to fit large graphics (or code) into the Game Boy’s limited memory
    • Hardware Interrupt: Use Game Boy’s timer interrupt for accurate clock functionality

    Features of the Game Boy Desk Clock

    The program isn’t just a visual mockup—it works like a real clock. Here are the main features:

    • Time Setting – Adjust day, date, hour, and minute.
    • Time Keeping – The clock runs in real-time with seconds, minutes, hours, day, and date.
    • Alarm Setting – Set an alarm through the time-setting state.
    • Hourly Chime – Toggle on/off at any time.

    What it doesn’t have (compared to the real F-91W):

    • No chronometer/stopwatch. – Essentially, it’s a game console, not a precision watch.
    • No 12-hour mode (only 24-hour).
    • Not meant to be ultra-accurate—the Game Boy has no RTC.

    For a bit of flair, I added blinking behavior to the separator dots.

    A screenshot of the time setting screen from a homebrew Casio F-91W desk clock program developed with GBDK and C that runs on the classic Game Boy console
    Time setting mode of my homebrew Casio F-91W style digital clock running on the Game Boy

    The Flow of the Program and Button Controls

    It’s good practice to plan a program’s flow before coding. Here’s how my desk clock works:

    • Time Setting Mode (default on start):
      • Select → cycle through fields (day, date, hour, minute).
      • Up/Down → change value (wraps around).
      • B → toggle hourly chime.
      • A → enter Alarm Setting Mode.
      • Start → begin timekeeping.
    • Alarm Setting Mode:
      • Adjust hour/minute like above.
      • A cancels, Start saves alarm.
      • If alarm already exists, A removes it.
    • Time Keeping Mode:
      • B toggles chime.
      • A removes alarm.
      • Select returns to Time Setting.

    Under the Hood: Graphics

    I designed the layout and digits in Aseprite, then exported them into .c and .h files with a plugin. Have a look at how to install it in Aseprite here.

    • Backgrounds → Casio-style watch frame and hour markers (one per hour to respect limits, swapped in with memory banking).
    • Sprites → Digits, alarm icon, chime icon, and blinking cursor.

    Why no seconds digits? The Game Boy can’t display them cleanly—each digit uses 3×3 tiles, and adding seconds would push some scanlines past the 10-sprite-per-line limit. Instead, I just sync start time with an external clock.

    A screenshot from Aseprite that shows pixel art digits that will be used as hour, minute, and second digit sprites in a Casio F-91W inspired desk clock homebrew software that runs on the classic Game Boy console
    Aseprite time digit spriters screenshot

    Sprites and backgrounds are kept in arrays and swapped in as time changes. For example, hour backgrounds are stored in an array of 24 and loaded when the hour rolls over.

    The display workflow:

    1. Load background (watch frame).
    2. Load sprites (digits/icons).
    3. Position sprites at their coordinates.

    This way, only changing parts update each second, while the rest stays fixed.

    Adding graphics information to the main code:

    #include <gb/gb.h>
    #include <stdio.h>
    
    // balnk watch face
    #include "blank_bkg_24hr.h"
    
    // sprites, including intro sprites
    #include "./trim_sprites/watchface_sprites.h"
    
    // 24HR background data
    #include "./background_24hr/watch_face_hr_00.h"
    #include "./background_24hr/watch_face_hr_01.h"
    ...
    ...
    ...
    #include "./background_24hr/watch_face_hr_22.h"
    #include "./background_24hr/watch_face_hr_23.h"

    Creating array pointers from the included graphics information:

    // big number sprite array
    const unsigned char *big_num_sprite_data[10][3] = {
    	{big_0_sprite_tileset_size, big_0_sprite_tileset, big_0_sprite_tilemap},
    	{big_1_sprite_tileset_size, big_1_sprite_tileset, big_1_sprite_tilemap},
    ...,
    ...,
    ...,
    	{big_8_sprite_tileset_size, big_8_sprite_tileset, big_8_sprite_tilemap},
    	{big_9_sprite_tileset_size, big_9_sprite_tileset, big_9_sprite_tilemap}};
    
    // small number sprite array
    const unsigned char *small_num_sprite_data[10][3] = {
    	{small_0_sprite_tileset_size, small_0_sprite_tileset, small_0_sprite_tilemap},
    	{small_1_sprite_tileset_size, small_1_sprite_tileset, small_1_sprite_tilemap},
    ...,
    ...,
    ...,
    	{small_8_sprite_tileset_size, small_8_sprite_tileset, small_8_sprite_tilemap},
    	{small_9_sprite_tileset_size, small_9_sprite_tileset, small_9_sprite_tilemap}};
    
    // background 24hr data
    const unsigned char *hr_bg_data[24][3] = {
    	{watch_face_hr_00_tileset_size, watch_face_hr_00_tileset, watch_face_hr_00_tilemap},
    	{watch_face_hr_01_tileset_size, watch_face_hr_01_tileset, watch_face_hr_01_tilemap},
    ...,
    ...,
    ...,
    	{watch_face_hr_22_tileset_size, watch_face_hr_22_tileset, watch_face_hr_22_tilemap},
    	{watch_face_hr_23_tileset_size, watch_face_hr_23_tileset, watch_face_hr_23_tilemap},
    };

    Load and display background:

    // Load and draw initial background 
    
    // load background information 
    set_bkg_data(0, blank_bkg_24hr_tileset_size, blank_bkg_24hr_tileset); 
    // draw background
    set_bkg_tiles(0, 0, 20, 18, blank_bkg_24hr_tilemap);

    The background information is loaded with the function set_bkg_data(); by setting its first tile at index 0 with the number of tiles and tile information represented by blank_bkg_24hr_tileset_size and blank_bkg_24_hr_tileset. Then, the background is drawn with the function set_bkg_tiles(); starting at tile position x= 0, y = 0 on the 20 x 18 tiles canvas according to the map information represented by blank_bkg_24hr_tilemap.

    Similarly the following code snippet show an example of how a sprite is drawn on the screen:

    Load and display sprites:

    // Load chime and alarm sprite icon information
    
    // Load Chime sprite icon data
    set_sprite_data(34, chime_sprite_tileset_size, chime_sprite_tileset); 
    
    // Load Alarm sprite icon data
    set_sprite_data(35, alarm_sprite_tileset_size, alarm_sprite_tileset); // Alarm
    
    // Display Chime sprite icon data
    set_sprite_tile(34, 34);
    move_sprite(34, 48, 72);
    
    // Display Alarm sprite icon data
    set_sprite_tile(35, 35);
    move_sprite(35, 56, 72);

    set_sprite_tile(34, 34); prepare to show the sprite indexed 34 stored in the VRAM’s tile indexed 34. The function move_sprite(34, 48, 72); shows the sprite indexed 34 at tile position x= 48, y = 72

    A helper function to draw a sprite:

    Instead of writing two functions consecutively to show sprites on the screen, we can also write up a function that will show any sprites of different sizes at any position on the screen as the following example:

    If we want to draw a sprite which occupies 3 x 3 tiles (rows and columns) with the starting sprite index of 8 and starting tile index of 8 at the tile position x = 44 and y = 88, we would used the function above like this: draw_sprite(8, 8, 3, 3, 44, 88);

    static void draw_sprite(uint8_t sprite_start, uint8_t tile_start, uint8_t row_tiles, uint8_t, col_tiles, uint8_t x, uint8_t y)
    {
        uint8_t idx = sprite_start;
        uint8_t tile = tile_start;
        for (uint8_t row = 0; row != row_tiles; row++)
    	{
    	    for (uint8_t col = 0; col != col_tiles; col++)
    	        {
    		    set_sprite_tile(idx, tile);
    		    move_sprite(idx, x + (col * 8), y + (row * 8));
    		    idx++;
    		    tile++;
    		}
    	}
    }
    A screenshot of the timekeeping screen from a homebrew Casio F-91W desk clock program developed with GBDK and C that runs on the classic Game Boy console displaying all available sprites
    Casio F-91W homebrew Game Boy classic desk clock screenshot from Emulicious with all sprites displayed

    How Backgrounds and Sprites Work Together

    Think of the screen as layers:

    • Background (Casio frame, labels).
    • Sprites (digits, icons, blinking cursor).
    • Hardware display blends them together.

    This lets me update only what changes (minutes ticking, alarm toggling) without redrawing everything each frame.


    Under the Hood: State Machine

    I structured the logic with three states:

    1. Time Setting
    2. Time Keeping
    3. Alarm Setting

    Each state has its own button mappings and update logic.

    // Defining an enumerated data type for time setting
    
    typedef enum {
        TIMESET_DAY,
        TIMESET_DATE,
        TIMESET_HOUR,
        TIMESET_MINUTE
    } TimeSetField;
    
    // Defining the variable 'timeset_field' with its 
    // default value
    
    TimeSetField timeset_field = TIMESET_DAY;
    
    // Defining the variable 'joy_read' to get input
    uint8_t joy_read = joypad();
    
    // If the 'select' button is pressed, cycle through 
    // the values in the TimeSetField data type
    
    if (joy_read & J_SELECT) {
        timeset_field++;
        if (timeset_field > TIMESET_MINUTE) {
            timeset_field = TIMESET_DAY;
        }
    }    

    Timer Accuracy

    The Game Boy doesn’t have a built-in RTC, so I used its hardware timer interrupt.

    • CPU runs at 4096 Hz.
    • An 8-bit counter overflows at 256 (28).
    • 4096 / 256 = 16 overflows → 1 second.

    I track these overflows with cpu_cyc_count. Each time it reaches 16, one second passes. From there, minutes, hours, days, and dates tick up in the main loop.

    // Timer interrupt handler
    void timer_isr(void) {
        cpu_cyc_count++;
    }
    
    void main() {
        // Timer ISR initialization
        disable_interrupts();
    
        // Timer setup: 16 Hz base
        TMA_REG = 0x00;    // reload value
        TIMA_REG = 0x00;   // start from 0
        TAC_REG = 0x04;    // enable timer, 
                           // input clock = 4096 Hz
    
        add_TIM(timer_isr);
        set_interrupts(TIM_IFLAG | VBL_IFLAG);
        enable_interrupts();
    
        // Time calculation logic
        while (1) {
            wait_vbl_done();
            // time-keeping
    	if (cpu_cyc_count >= 16) {
    	    cpu_cyc_count = 0;
    	    sec_count++;
    	    sec_tick = 1;
    	    if (sec_count >= 60) {
    	        sec_count = 0;
    		min_count++;
    		min_tick = 1;
    		if (min_count >= 60) {
    		    min_count = 0;
    		    hr_count++;
    		    hr_tick=1;
    		    if (hr_count >= 24) {
    		        hr_count = 0;
    			day_count++;
    			date_count++;
    			day_tick = 1;
    			date_tick = 1;
    			if (day_count >= 7) {
    			    day_count = 0;
    			}
    			if (date_count >= 31) {
    			    date_count = 0;
    			}
    		    }
    	        }
                }
    	}
            // The rest of the code...
            // The rest of the code...
            // The rest of the code...
        } // Close the game loop
    } // Close main function

    This provides a reliable 1 Hz tick without overloading the CPU.

    Extra details:

    Blinking Effect → toggle every 20 frames at ~60 FPS.

    void main() {
        while (1) { // main game loop
            wait_vbl_done();
    
            // code ...
            // code ...
        
            blink_counter++;
            if (blink_counter >= 40) {
                blink_counter = 0;
            }
    
            // code ...
            // code ...
            
            // draw blinking day sprite 
            // (during time setting):
    
            if (blink_counter >= 20) {
    	    draw_daydate(0, 0, 0, 0);
    	} else {
    	    draw_daydate(0, 0, 80, 72);
    	}
    	
            // code ...
            // code ...
    
        } // close game loop
    } // close main

    The Game Boy screen updates at ~59.7 FPS. When the loop is controlled by wait_vbl_done(); The counter (blink_counter) goes up to ~60 at the end of the loop. But this time, we reset it when it reaches 40. The conditional block draws the day sprite while the counter is below 20 and remove it while the counter is above 20. This way, we can create the ‘blinking effect’.

    Input Sensitivity → button delay counter prevents “jumpy” input.

    void main() {
        while(1) {
    
            // code ...
            // code ...
    
            // input delay
            if (input_delay > 0) input_delay--;
    
           // code ...
           // code ...
           
           if (input_delay == 0 && joy_read & J_START) {
               input_delay = 12; // reset input delay
               game_state = STATE_TIMEKEEPING;
           }
        
           // code ...
           // code ...
    
        } // close game loop
    } // close main

    From the code above, if the ‘start’ or any button is pressed, the input_delay is reset to 20 and no button can be pressed before it reaches 0 again. Without this mechanism, the button control will be too responsive and feel ‘jumpy’.


    What I Learned

    Working on this project taught me a lot about:

    • Embedded programming concepts with limited hardware.
    • Interrupt handling on the Game Boy.
    • Using a state machine for clean, scalable game/software design.
    • The fun of merging retro hardware with nostalgic design.

    Why This Project Matters

    For me, this isn’t just about writing code—it’s about connecting with childhood dreams. Owning a Game Boy now and programming it into something unique feels incredibly rewarding. Pairing it with the Casio F-91W aesthetic makes it even more special.

    Projects like these are proof that old consoles can be more than just collectibles. They can be platforms for learning, creativity, and fun programming experiments.

    A classic Game Boy console running a homebrew digital desk clock program in Casio F-91W style with its development code showing on a monitor in the background
    The behind-the-scene C/GBDK development code for the homebrew Casio F-91W Game Boy desk clock

    Final Thoughts

    If you’re into retro programming, C development with GBDK, or simply love the Game Boy as much as I do, I hope this project inspires you to try building something of your own.

    Stay tuned—I’ll be posting screenshots, photos of the setup, and snippets of the C code that make this Game Boy desk clock tick.