Z80 Assembly 40: Software Serial I/O (Bit-Banging Communication)

The Need for Bit-Banging The Z80 CPU does not have a built-in UART (Universal Asynchronous Receiver/Transmitter) for serial communication. To send data to a modem or another computer, we must bit-bang the signal: we use software and timing loops to manually control the state of an I/O pin. Key Components: I/O Port: A dedicated hardware output port (often the same as the beeper port) where one bit is wired to the serial output pin. Timing Loops: Precise timing loops (using T-states) to control the Baud Rate (bits per second). The Serial Data Format Serial communication sends one byte at a time, surrounded by fixed timing signals: ...

September 27, 2025

Z80 Assembly 39: Software Sound Synthesis (Beyond the Square Wave)

Limitations of the Beeper (Square Wave) The simple beeper (Part 28) can only produce a square wave (a harsh, buzzy tone) because it only switches the speaker ON or OFF. To create richer sounds, like music that sounds closer to a sine wave, or complex speech effects, we need to simulate varying amplitude (volume). Pulse-Width Modulation (PWM) Synthesis Since the Z80 cannot vary the voltage to the speaker, we simulate varying amplitude using Pulse-Width Modulation (PWM). ...

September 27, 2025

Z80 Assembly 38: Complex Music with the AY-3-8910 Sound Chip

The Limitation of the Beeper The simple beeper (Part 28) can only play one square wave tone at a time. The AY-3-8910 (or compatible) Programmable Sound Generator is a dedicated chip that provides three independent tone channels, a noise generator, and volume control. Communicating with the AY Chip The AY chip uses a few fixed I/O ports for communication. The Z80 talks to it using a two-step process: Select Register: Write the address (0-15) of the internal register you want to modify. Write Data: Write the actual data value to that register. Generic AY Port Example: ...

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