Z80 Assembly 69: Final Project - Building a Simple Game Engine

The Synthesis: Combining All 68 Lessons This final project demonstrates how to structure and link together all the advanced modules developed in this series (Interrupts, Graphics, I/O, and Logic) to create a working, real-time application. The Engine’s Goal: Display a sprite that moves smoothly in response to keyboard input, accelerates due to gravity, and stops when hitting a floor tile. Engine Initialization (The Setup Phase) This phase runs only once, at boot, and relies heavily on your OS and peripheral posts (Parts 52, 62). ...

September 27, 2025

Z80 Assembly 44: Sprite Multiplexing and Display Lists

The Limitation: Hardware Sprite Count Many systems with dedicated sprite hardware (like the MSX or certain arcade boards) have a severe limit on the number of sprites that can be displayed on a single horizontal scanline (e.g., 8 or 16 sprites). Exceeding this limit causes sprites to disappear or flicker. The Solution: Sprite Multiplexing Sprite Multiplexing is a software technique that dynamically reuses the hardware’s limited number of sprite slots multiple times per frame. The goal is to quickly change the sprite’s position and pattern as the display beam moves down the screen. ...

September 27, 2025

Z80 Assembly 37: Tile-Based Collision and Map Boundaries

Collision on a Tilemap In a tile-based game, collision detection isn’t just about object-to-object overlap; it’s about checking if a moving sprite is trying to occupy a tile marked as solid (e.g., a wall, water, or rock). The Core Logic: Before updating a sprite’s position, check the Tile ID at the new (target) location. Step 1: Converting Sprite Position to Tile Index The first challenge is converting the sprite’s precise pixel coordinates (X, Y) into the coarse array index needed to read the tilemap. ...

September 27, 2025

Z80 Assembly 36: Rendering Large Worlds with Tilemaps

The Tilemap Concept A tilemap is a technique that breaks a large game world (the map) into small, reusable graphic squares (tiles), typically $8\times 8$ or $16\times 16$ pixels. Why Tilemaps are Essential: Memory Saving: Instead of storing the pixel data for the entire map, you only store the data for a small set of unique tiles (the tileset) and then store the map itself as a small array of tile IDs (indices). Fast Rendering: The CPU can draw the screen by reading the tile ID from the map and using that ID as an index to quickly look up and draw the corresponding tile graphic. The Map Data Structure The map data is a simple, linear array stored in memory. ...

September 27, 2025

Z80 Assembly 35: Collision Resolution (Bouncing and Stopping Movement)

The Need for Resolution Collision Detection (Part 26) only tells you that two objects overlap. Collision Resolution is the logic that moves the objects apart and adjusts their speed (velocity) so that they don’t overlap in the next frame. Without resolution, sprites will get stuck inside walls. Step 1: Backtracking (The Safest Move) The most reliable way to resolve an overlap is to simply move the collided object back to its previous, non-colliding position. ...

September 27, 2025

Z80 Assembly 34: Simple Physics (Gravity, Velocity, and Jumping)

The Concept of Velocity and Gravity To make a sprite move realistically (fall, jump), we need two new variables in our sprite data structure: Velocity (DY): The current speed and direction of the sprite on the Y-axis. Gravity: A constant value that is continually added to the velocity every frame, causing the sprite to accelerate downward. The Calculation: The physics update is done by: Velocity ← Velocity + Gravity Y-Coordinate ← Y-Coordinate + Velocity Updating Velocity with Gravity We will need a new field in our sprite descriptor (e.g., offset +A for an 8-bit velocity) and a constant for gravity. ...

September 27, 2025

Z80 Assembly 33: Managing Multiple Sprites with a Linked List

The Need for a Dynamic List When creating a game, you don’t want to waste time checking 50 memory locations if only 5 sprites are currently active. A Linked List is the most efficient data structure in Z80 assembly for managing a variable number of objects. The Linked List Principle: Each sprite’s data block (the node) contains a pointer to the next sprite in the sequence. When the CPU finishes processing the current sprite, it simply follows the pointer to the next one. The list ends when a pointer value is zero or another terminator. ...

September 27, 2025

Z80 Assembly 32: Simple Animation and Movement (Updating Coordinates)

The Core Animation Loop Creating animation is a three-step process repeated every game frame (ideally synchronized with VSync, as discussed in Part 25): Erase: Write the background over the sprite’s current location. Update: Change the sprite’s X/Y coordinates and/or its graphical frame number. Draw: Render the sprite at its new location. Updating Sprite Coordinates To move a sprite, you modify the X-Coordinate and Y-Coordinate fields within its memory descriptor (from Part 31). ...

September 27, 2025

Z80 Assembly 31: Sprite Data Structure and Rendering Logic

Defining a Sprite Data Structure A sprite is a graphic object that moves independently on the screen. We need a memory structure to hold all its properties in one place. This structure is often called a Control Block or a Sprite Descriptor. Example Sprite Descriptor (10 Bytes): Offset Size (Bytes) Purpose +0 2 X-Coordinate (16-bit) +2 2 Y-Coordinate (16-bit) +4 2 Sprite Graphic Address (Pointer to the pixel data) +6 1 Frame Number (If sprite is animated) +7 1 Status (Bitwise flags: Active, Firing, Hit) +8 2 Pointer to Next Sprite (For a linked list) Reading Sprite Properties We use the Index Registers ($IX$ or $IY$) for fast, organized access to these properties. ...

September 27, 2025

Z80 Assembly 29: Simple Random Number Generation (RNG) for Games

The Challenge of Randomness CPUs are deterministic: they do the same thing every time. To generate a truly unpredictable sequence, we need a source of entropy (unpredictable noise) from the outside world. Method 1: Hardware Noise (Simple Seed) The fastest way to get a simple random number is to read a register or port whose value is constantly changing due to external factors. Noise Sources: I/O Port Reading: Reading from an unused or floating I/O port can return unpredictable bits of electrical noise. The Refresh Register (R): As discussed earlier, the R register increments automatically, making its low bits somewhat unpredictable depending on when it’s read. The Keyboard: Polling the keyboard port and using the lower bits of the input byte before any key is pressed. Example: Reading an Unpredictable Port ...

September 27, 2025