Attack of the PETSCII Robots SDL ================================ Ported by Vesa Halttunen <vesuri@jormas.com> This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. About ----- - petrobots.cpp is the main game logic ported line by line from the 6502 files PETROBOTS.ASM and BACKGROUND_TASKS.ASM - Platform.h is essentially an interface with platform specific implementation classes - Various #defines starting with PLATFORM_ can be used to build a variant with different features using the same platform implementation - To port to a new platform, create a new PlatformXYZ.cpp/h implementation based on the existing ones and instantiate it in main() (petrobots.cpp) - If the target platform version will only support a certain feature set, feel free to get rid of the unnecessary PLATFORM_ #ifdefs manually or using a preprocessor - The SDL version is a generic baseline implementation that should be customized for each actual target platform MacOS Building -------- brew install sdl2 sdl2_image make setup make cd SDL ../petrobots Windows Building -------- download SDL2 VC dev zip's, unzip contents to `SDL_src/VC` directories (SDL2_image and SDL2) open `WindowsProject.sln` with Visual Studio 2022+, press Build > Build solution. Making of --------- The first task was to convert the 6502 assembler PET source code to C++ line by line. Even though this won't produce the most elegant high level language code it ensures that the code behaves exactly like the original does. PET KERNAL calls and memory accesses were abstracted to an interface which can be implemented for different platforms. To make initial testing and verification of the ported code easier an SDL platform implementation was written first. This allowed the game logic to be tested on a modern operating system. When eveything seemed to work it was time to write an implementation of the platform interface for the Amiga. Since the game shouldn't necessarily require every last drop of the Amiga's resources to run, the interface was implemented in an AmigaOS friendly way, multitasking in it's own AmigaOS screen. The first implementation was very naive: each write to the PET screen memory would result in copying the respective bytes from the font to the Amiga bitmap memory using the CPU. This would be highly inefficient on the Amiga, so the next step was to implement tile based rendering. Tiles would be copied in 24x24 pixel blocks using the Amiga's blitter, which on 68000 systems is much faster. Initially the tile bitmaps were generated during startup using the font and the tile data but pre-drawn bitmaps could be used just as well. After adding support for four bitplanes to get 16 colors it was already possible to switch to the tiles provided by the graphics artist. Double buffering was implemented to hide artefacts caused by modifying the screen while it's being drawn on display. However, the increase in memory consumption combined with the highly inefficient manner of switching between the buffers in an AmigaOS friendly manner called for a better approach. The bitmaps were made interleaved so that instead of the four bitplanes following each other in memory, the data for each bitplane row follows each other in memory. This way any changes to the screen memory while it's being drawn on display are limited to a small area. Each tile could be copied with a single blit, improving performance. To reduce the amount of memory required, a transparency mask was only generated for tiles requiring one. The one channel PET-like sound was replaced with a ProTracker module based sound implementation. Sound effect samples were injected programmatically to each module upon loading. The module player was modified to allow the sound effects to be triggered by the game as if they were notes in the song data. This way there was no need for a separate sound effect player or a need to make the music and sound effect playback routines aware of each other. The songs were modified to leave the fourth channel free for sound effects as often as possible. A separate "no music" module was then added which has no notes at all and allows sound effects to be played on all four channels. This completely transformed the game's audio. Support for pre-drawn graphics for the intro screen, game screen and game over screen was added. Then it was time to render the current weapon, item, keys and health using bitmap graphics. Animated player and robot sprites were implemented. Hardware based screen shaking was implemented and hardware sprites were used for the cursor. Palette fading made transitions between different screens a lot smoother. It also allowed the screen to smoothly flash when taking damage or using the EMP. Suddenly the game started to look like an Amiga game! In order to fit the game on one disk, assets had to be compressed. A hand written 68000 assembler implementation of deflate was used to decompress gzipped assets. In order to fit the game in memory, these assets had to be loaded on demand. On Amigas with only 512 kilobytes of chip memory there was no way to fit both music and sound effects into memory, so a decision was made to only support sound effects on such systems. On Amigas with more memory in-game music is loaded from disk on demand. Other assets, like the intro screen, intro music, game over screen and game over screen are loaded at startup and kept in memory to make the game over experience more pleasant. When the game was mostly complete otherwise, it was time to implement the live map. While simple in principle, Amiga does not make the implementation trivial due to its planar graphics. Instead of modifying a single byte to modify the color of a single pixel, one bit in four different bytes needs to be modified, making single pixel modifications very slow. Various chunky to planar implementations were looked into, but ultimately a custom 68000 assembler chunky to planar routine was written for drawing the entire map and an another custom implementation for manipulating the pixels relevant for each unit. The end result was a map performant enough to be usable also on 68000 systems. Last but not least joystick support was implemented. Most Amiga games only support a single button Atari style joystick. However, there are Amiga games that do support the 7 button Amiga CD32 gamepad either natively or via patches, so it made sense to add support for such a pad as well. In this mode all the game commands are available through the gamepad. Getting this code right required digging up some obscure information from forum discussions and such. Atari style joysticks can also have two buttons, so support for the second button was added to the joystick mode. This way most of the game commands are available using such a joystick as well. While not optimal, at least the game is now fully playable using a traditional joystick. TODO ---- - Add screen and map size getters in Program to allow actual screen size to be determined on startup