
Developing a game that performs seamlessly on both vintage and modern computers presents a formidable and exciting challenge. Is it possible to succeed?
Here’s a glimpse into my journey—detailing the processes, hurdles, and solutions I encountered while crafting a roguelike dungeon crawler that can operate on machines like the Commodore 64 and Commodore PET, as well as other constrained systems.
Why Develop Games for Multiple Platforms?
Many enthusiasts cherish vintage computers for their nostalgic appeal. However, having these machines allows for interactive experiences that showcase their distinctive quirks and features.
My aim was to create a game that works across a wide range of systems, leveraging each machine’s strengths while navigating limitations like memory, graphics, and input methods.
Initial Approach: Utilizing TRSE for Multi-Target Compilation
The adventure began with TRSE, a development tool that supports many vintage computers. My plan was to build a text-based roguelike that could be compiled for different hardware.
Challenges Faced
- TRSE is primarily based on Pascal, a language I’m not entirely comfortable with, especially after long intervals of inactivity.
- Many older systems offered incomplete or inconsistent text support in TRSE libraries, as the community typically focuses on developing for each system’s graphics.
- Inconsistencies in hardware standards, such as ASCII character mapping, led to compatibility issues, particularly on machines from before the 1980s.
Key Takeaways
- Developing for multiple vintage platforms is inherently more complex than for modern systems due to varying standards and capabilities, even among machines from the same manufacturer.
- Support libraries may not align well with your vision and might require adaptations for different systems.
- TRSE is a fantastic and relatively new community-driven project, meaning support for specific machines often relies on community efforts.
Switching to C for Enhanced Flexibility
Recognizing the limitations of TRSE, I transitioned to C. With C compilers available for a wide variety of hardware, from calculators to mainframes, I aimed to create a universal codebase.
Benefits of Using C:
- Extensive cross-compiler support.
- Experience in writing portable code for diverse hardware environments.
- Control over low-level hardware interactions when necessary.
Reality Check
- Despite C’s portability, a significant amount of conditional code is required to accommodate different system architectures.
- Compiler variations (e.g., CC65, Z88DK) and differing adherence to standards (C89, C99) add to the complexity.
Addressing Platform Differences with Conditional Compilation
I utilized conditional compilation to include or exclude segments of code based on the target system.
Example:
#ifdef C64
// Specific code for Commodore 64
#endif
#ifdef PET
// Specific code for Commodore PET
#endif
This method, however, has its challenges:
- Different compilers exhibit varying syntax and capabilities.
- Each system’s memory and graphics features are not uniform.
- This can lead to bloated code that’s hard to maintain.
I began to reconsider whether I should focus on supporting multiple platforms or concentrate on just one.
Navigating Hardware Limitations
Supporting various machines means considering:
- Memory limitations: For instance, I currently target a minimum of 48K RAM but would love to expand this for my favorite Vic 20 with RAM upgrade support.
- Graphics and character sets: Some systems may use custom character graphics, while others do not.
- Input controls: Many vintage systems used keyboards, and not all supported joysticks natively.
- Loading mechanisms: Resource management varies from tapes to disks.
This complexity led me to focus on systems with similar capabilities, specifically those with at least 64K RAM, such as:
- Commodore 64, PET, Plus/4, Atari 800, BBC Micro
- CP/M-based systems like IMSAI and Altair
- Linux, Mac, Windows (terminal and graphical versions using Raylib)
Building the Core Game: Levels, Map Generation, and Graphics
From Hand-Crafted Dungeons to Procedural Map Generation
A significant achievement was developing a procedurally generated map that creates unique gameplay experiences without the burden of designing maps manually. While this process consumes RAM, the generation delay has proven shorter than anticipated, resulting in a manageable pause during gameplay.
This map generator creates random, walkable layouts and inserts enemies and collectibles based on logical guidelines.
At this stage, the game features:
- Basic player movement
- Collectibles (power-ups, health)
- Enemies with unique movement and attack patterns, such as rats and goblins
- Simplified combat and item interactions
Shifting to User-Defined Character Graphics
Initially, the game was text-based, adhering to the traditional roguelike dungeon style, but variations exist among machines, notably in text column count (e.g., the PET can support either 40 or 80 columns).
Integrating graphics entails:
- Defining user graphics based on the C64 default.
- Loading these definitions into memory banks.
- Switching character sets to accommodate the custom graphics.
- Translating standard character/object/bad-guy codes for the new graphics.
Strategic Shift: Prioritizing One Platform Initially
Considering the complexity, I’ve decided to focus on the Commodore 64 as my primary target platform for the following reasons:
- It remains one of the most sought-after retro computers in 2025.
- It boasts excellent graphics and sound capabilities.
- It’s simpler to cross-develop and test.
- The core code can later be adapted for other systems.
Once I refine the game on the C64, I can then port it to other systems with varying capabilities, minimizing distractions, code bloat, and tricky bugs.
Helpful Tips for Multi-Platform Development
- Start small and concentrate: Create a functional version for one platform before scaling up.
- Utilize conditional compilation judiciously: Maintain simplicity by focusing on widely used features.
- Emphasize core gameplay mechanics: First ensure basic movement and interaction functionalities.
- Design adaptable resources: Whenever feasible, load graphics and data from external files.
- Employ emulators with CLI parameters for testing: Choose ones that effectively simulate how your game will run on target systems without countless clicks and waits.
- Understand hardware nuances: Familiarity with each system’s specifications aids in optimizing the code, even when using cross-platform libraries and compilers.
Conclusion: Balancing Ambition with Realism
Creating games that function on numerous historic and modern systems demands a delicate balance. While broad compatibility is enticing, the technical realities of varied hardware insist on a more practical scope—beginning with a well-established platform like the Commodore 64 or PET.
The focus should be on delivering an enjoyable experience, grasping platform-specific details, and gradually adapting to additional systems. By managing code complexity and developing incrementally, it’s entirely feasible to craft engaging retro-style games that celebrate the essence of vintage computing while leveraging modern programming tools.
As always, if you’d like to delve deeper, feel free to reach out on YouTube or the socials.
Original article by retrogamecoders.com


