Assembly Language Fundamentals

Intermediate
Version:
1.1

Creating 16×16 Sprites

Introduction

The TMS9129 Video Display Processor (VDP) inside the Tatung Einstein supports both 8×8 and 16×16 sprite sizes. In the previous lesson, you learned how to define and display a single 8×8 sprite — ideal for small game elements such as icons or bullets.

However, when building larger characters or objects, 8×8 sprites can become limiting. The Einstein’s VDP makes it simple to double the sprite size by setting a single bit in Register 1. Once enabled, the VDP automatically groups four consecutive sprite patterns into a single 16×16 block, allowing for much larger and more detailed designs.

How 16×16 Sprites Work

By default, the VDP uses 8×8 sprite mode. To enable 16×16 mode, you simply set bit 1 of Register 1 to 1.

RegisterBitFunctionDescriptionRegister 1Bit 1Sprite size control0 = 8×8 sprites, 1 = 16×16 sprites

When this bit is set, the VDP automatically groups patterns in the Sprite Generator Table in sets of four to form a single 16×16 image.
However, the TMS9129 uses a specific internal layout for combining these four patterns, which differs from the more intuitive left-to-right, top-to-bottom order.

The correct mapping is as follows:

Sprite Pattern NumbersPosition in 16×16 SpritenTop-leftn+1Bottom-leftn+2Top-rightn+3Bottom-right

So if the sprite’s pattern number in its attribute block is 0, the VDP uses patterns 0, 1, 2, and 3 together in that order to form the complete 16×16 shape.

Each of these patterns still consists of 8 bytes, representing 8×8 pixel blocks.

Updating Register 1

To enable 16×16 sprites, change the value written to Register 1. In the previous lesson, you wrote:

LD A,224
OUT (9),A
LD A,129
OUT (9),A

To enable bit 1, you now use:

LD A,226      ; 224 + 2 sets bit 1 for 16×16 mode
OUT (9),A
LD A,129
OUT (9),A

That single change tells the VDP to render sprites as 16×16 instead of 8×8.

Full Program

This program displays a 16×16 smiley face composed of four linked sprite patterns. The VDP automatically groups them according to the 16×16 layout described above.

       
       ORG 256

      ; -----------------------
      ; Enable Graphics Mode II
      ; ----------------------

       ; Set Register 0 values

       LD   A,2
       OUT  (9),A
       LD   A,128
       OUT  (9),A

       ; Set Register 1 values

       LD   A,226   ; Set 16x16 sprite bit 1
       OUT  (9),A
       LD   A,129
       OUT  (9),A


      ; ---------------------------
      ; Set Foreground / Background
      ; ---------------------------

      LD   A, 240        ; 11110000 (white foreground, black background)
      OUT  (9), A        ; Send to Port 9
      LD   A, 135        ; 128 (bit 7 set) + 7 (register 7)
      OUT  (9), A        ; Send to Port 9

      ; -----------------
      ; Write Blank Tiles
      ; -----------------

      ; Top Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 64           ; Load 64 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0            ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8

      ; Middle Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 72           ; Load 72 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0            ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8

      ; Bottom Third Blank Tile

      LD   A, 0            ; Load zero into A register
      OUT  (9), A          ; Send to Port 9
      LD   A, 80           ; Load 80 into A register
      OUT  (9), A          ; Send to Port 9

      LD A, 0              ; Load 170 into A
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8
      OUT (8), A           ; Send to Port 8


      ; --------------------------------
      ; Clear Name Table with Blank Tile
      ; --------------------------------

      LD A, 0            ; Low Byte
      OUT (9), A         ; Send to Port 9
      LD A, 120          ; High Byte
      OUT (9), A         ; Send to Port 9

      LD BC, 768         ; Set Counter for table address space

ClearLoop:
      LD A, 0            ; Load zero into A register
      OUT (8), A         ; Send to Port 8
      DEC BC             ; Decrement BC Counter
      LD A, B            ; Load B into A register
      OR C               ; Bitwise OR to compare
      JR NZ, ClearLoop    ; Jump to start unless BC is zero


       ; ---------------------
       ; Define Sprite Pattern
       ; ---------------------

       LD A,0
       OUT (9),A
       LD A,88
       OUT (9),A

       LD A,%11000001 : OUT (8),A
       LD A,%10000010 : OUT (8),A
       LD A,%00000101 : OUT (8),A
       LD A,%00001011 : OUT (8),A
       LD A,%00010111 : OUT (8),A
       LD A,%00101110 : OUT (8),A
       LD A,%01011100 : OUT (8),A
       LD A,%10111000 : OUT (8),A

       LD A,%10111000 : OUT (8),A
       LD A,%01011100 : OUT (8),A
       LD A,%00101110 : OUT (8),A
       LD A,%00010111 : OUT (8),A
       LD A,%00001011 : OUT (8),A
       LD A,%00000101 : OUT (8),A
       LD A,%10000010 : OUT (8),A
       LD A,%11000001 : OUT (8),A

       LD A,%10000011 : OUT (8),A
       LD A,%01000001 : OUT (8),A
       LD A,%10100000 : OUT (8),A
       LD A,%11010000 : OUT (8),A
       LD A,%11101000 : OUT (8),A
       LD A,%01110100 : OUT (8),A
       LD A,%00111010 : OUT (8),A
       LD A,%00011101 : OUT (8),A

       LD A,%00011101 : OUT (8),A
       LD A,%00111010 : OUT (8),A
       LD A,%01110100 : OUT (8),A
       LD A,%11101000 : OUT (8),A
       LD A,%11010000 : OUT (8),A
       LD A,%10100000 : OUT (8),A
       LD A,%01000001 : OUT (8),A
       LD A,%10000011 : OUT (8),A

       ; ------------------------
       ; Define Sprite Attributes
       ; ------------------------

       LD A,0
       OUT (9),A
       LD A,123
       OUT (9),A

       ; Sprite 0

       LD A,100
       OUT (8),A
       LD A,120
       OUT (8),A
       LD A,0
       OUT (8),A
       LD A,15
       OUT (8),A


       JP $            ; JP Idefinitely

When executed, this program produces:

Because 16×16 mode groups four sprite patterns into one, you only need to set up a single sprite attribute block — the VDP handles combining the four internal patterns automatically according to the correct pattern mapping.

Designing 16×16 Sprites

Each 16×16 sprite uses four consecutive patterns, meaning each new sprite consumes 32 bytes of VRAM (8 bytes × 4 patterns).

You must define the four patterns in the correct order expected by the VDP:

  1. n → Top-left
  2. n+1 → Bottom-left
  3. n+2 → Top-right
  4. n+3 → Bottom-right

If you define them in a different sequence, your sprite may appear scrambled or misaligned on-screen.

Sprite Size and Memory Usage

Sprite TypePatterns UsedBytes per SpriteNotes8×818 bytesBest for small icons16×16432 bytesBest for main characters or large objects

Be mindful of memory: the TMS9129 still supports only 32 total sprites, regardless of size. Larger sprites simply use more VRAM per definition.

Sprite Magnification

The TMS9129 VDP also includes a sprite magnification feature, allowing you to instantly double the displayed size of all sprites without redefining any new patterns. This can be useful for testing, visual emphasis, or creating zoom-style effects.

This feature is controlled by bit 0 of Register 1.

Summary of Sprite Control Bits (Register 1)

The behaviour of sprites on the Tatung Einstein’s VDP is controlled by specific bits within Register 1. Each bit modifies how sprites are drawn or displayed.

By adjusting these bits, you can easily switch between standard 8×8 sprites, 16×16 sprites, and magnified display modes to suit your game or application’s needs.

When bit 0 is set, every sprite (8×8 or 16×16) is automatically drawn at twice its normal size. Importantly, the magnification only affects on-screen rendering — the sprite’s defined shape and VRAM data remain unchanged.

For example:

This scaling occurs uniformly in both horizontal and vertical directions, with each pixel expanded into a 2×2 block.

Enabling Sprite Magnification

To enable magnification, set bit 0 of Register 1 in addition to bit 1 for 16×16 sprites.
The new Register 1 configuration becomes:

       ; Set Register 1 values

       LD   A,227   ; Enable 16x16 sprites (bit 1) and magnification (bit 0)
       OUT  (9),A
       LD   A,129
       OUT  (9),A

This value (227) combines:

Result

With this change, your 16×16 sprite will now appear as a 32×32 sprite on-screen — four times its original area.

You’ll notice:

This hardware-level magnification makes it easy to create enlarged sprites without extra processing or data duplication.

Summary

Previous Module
Next Module