WIZARD OF WOR
Wizard of Wor Title Screen
Atari 800 Reverse Engineering Project
Complete disassembly & analysis of the 1982 Atari 800 classic by Roklan Software
By Benj Edwards and Claude Opus 4.5
January 2026

// Overview

This project documents the complete reverse engineering of Wizard of Wor for the Atari 800, from an ATR disk image to a byte-identical reassemblable disassembly.

23,168
Bytes of Code
$1000
Load Address
$1AF2
Entry Point
100%
Byte-Identical

COMPLETE The game has been fully disassembled and verified to produce byte-identical output when reassembled with ca65/ld65.

// Screenshots

Wizard of Wor Title Screen

Title Screen

Wizard of Wor High Scores

High Scores

Wizard of Wor Monster Guide

Monster Guide & Point Values

Wizard of Wor Gameplay

Gameplay - The Dungeon Maze

// ATR Disk Structure

The original ATR disk image (92,176 bytes) contains a custom boot loader and the game binary:

Header
16 bytes - ATR format header
Sectors 1-3
384 bytes - Boot loader ($0600)
Sector 4
128 bytes - UNUSED ($DA filler)
Sectors 5-185
23,168 bytes - Game code ($1000-$6A7F)
Sectors 186+
Character sets & additional data

Boot Loader Analysis

The boot loader at $0600 performs the following operations:

// Memory Map

Main Memory Regions

Address Size Description
$0600-$077F 384 bytes Boot loader code (overwritten after boot)
$1000-$167F 1,664 bytes Graphics / character data
$1680-$6A7F 21,504 bytes Main game code
$7800-$79FF 512 bytes Custom character sets
$7F15-$7F26 18 bytes Score display buffer

Key Zero Page Variables

Address Name Purpose
$B9 busy Busy/wait flag
$E0 prev_state Previous game state
$E1 game_active Game active flag
$E3 game_phase Current game phase
$81 lives Lives remaining

Page 6 Variables ($0600-$06FF)

Address Name Purpose
$067C game_state Main state machine value
$069A dungeon Current dungeon level (1-N)
$069E-$06A3 p1_score Player 1 score (6 BCD bytes)
$06A4-$06A9 p2_score Player 2 score (6 BCD bytes)
$06AB p2_flag Two-player mode flag
$06DA disp_mode Display mode (1=title, 2=game, etc)

// Monster Types

Data extracted from the monster table at $3C20:

BURWOR
100 pts
Blue, slow, basic
GARWOR
200 pts
Yellow, medium speed
THORWOR
500 pts
Red, fast, shoots
WORLUK
1000 pts
Appears after clear
WIZARD
2500 pts
End boss, teleports

// Score System Analysis

The score system uses BCD (Binary Coded Decimal) arithmetic for proper decimal handling on the 6502 processor.

Score Storage

Address Purpose
$069E-$06A3 Player 1 score (6 BCD bytes = 6 digits max 999,999)
$06A4-$06A9 Player 2 score (6 BCD bytes)
$7F15-$7F1A Player 1 score display buffer
$7F1B-$7F20 Player 2 score display buffer

Score Addition Routine ($3489)

; add_score: Add points to player score using BCD mode
; Entry: Points to add stored at $069E-$06A3

L3489:  LDA #$00
        STA $06A4       ; Clear carry accumulator

        SED             ; *** SET DECIMAL MODE (BCD) ***

        LDA $06AB       ; Check two-player flag
        BEQ L3498       ; Branch if player 1
        LDX #$11        ; Player 2: display offset = 17
        BNE L349A       ; Always branch
L3498:  LDX #$05        ; Player 1: display offset = 5

L349A:  LDY #$05        ; Y = 5 (process 6 digits)

; Main addition loop (6 iterations for 6-digit score)
L349C:  CLC
        LDA $7F15,X     ; Load current display digit
        AND #$0F        ; Mask to get BCD value (0-9)
        ADC $06A4       ; Add carry from previous digit
        ADC $069E,Y     ; Add points digit
        PHA             ; Save result

        AND #$10        ; Check for BCD overflow
        BEQ L34AF       ; No overflow, skip
        LDA #$01        ; Overflow: set carry for next
L34AF:  STA $06A4       ; Store carry

        PLA             ; Restore result
        ORA #$10        ; Set display attribute (visible)
        STA $7F15,X     ; Store to score display
        DEX             ; Next display position
        DEY             ; Next points digit
        BPL L349C       ; Loop for all 6 digits

        CLD             ; *** CLEAR DECIMAL MODE ***

Key Observations:

// Main Game Loop

The entry point at $1AF2 implements a state machine based on the value in $067C:

; main_loop: Entry point - game state machine
; Called after boot loader transfers control

L1AF2:  BEQ L1AF5       ; If Z flag set, continue
L1AF4:  RTS             ; Otherwise return

L1AF5:  LDA $B9         ; Load busy flag
        BNE L1AF4       ; If busy, return (wait)

        LDA $067C       ; Load current game state
        CMP $E0         ; Compare with previous state
        BEQ L1B29       ; Same state, skip transition

        ; State transition code...
        STA $E0         ; Update previous state
        ; ... dispatch to state handlers

Game States

Value State Description
$00 TITLE Title screen / attract mode
$01 READY "Get Ready" / level intro
$02 PLAY Active gameplay
$03 DEATH Player death sequence
$04 CLEAR Level cleared
$05 GAMEOVER Game over sequence

// Text Strings Found

Address String
$4CE9 "DOUBLE"
$4E6B "DOUBLESCORE"
$4E77 "GAME"
$4E7B "OVER"
$4CF5 "DUNGEON"

Monster names found in data: BURWOR, GARWOR, THORWOR

// Free Space Analysis

Available space for modifications:

In Code Area

Address Size Contents
$13DD-$1407 43 bytes $00 fill (in graphics area)
$67CB-$67FE 52 bytes $00 fill
$6800-$6913 276 bytes $00 fill

Total usable free space: ~328 bytes at $67CB-$6913

On Disk

Sector 4 is unused (128 bytes, $DA/$86 filler). The boot loader skips it.

Sector 720 High scores are saved here! The game has disk I/O routines at $1F29-$1F71 that read/write sector 720 via DSKINV ($E453).

// Tools & Methodology

Successful Tools

Tool Purpose
Python 3 Custom extraction and disassembly scripts
ca65 (cc65 suite) 6502 assembler
ld65 (cc65 suite) 6502 linker
atari800 Emulator for testing
hexdump / dd Binary analysis

Tools That Failed

Tool Issue
atrcopy numpy.alen removed in newer numpy
mkatr Only handles DOS filesystems, not boot disks
atari_8bit_utils disasm macOS compatibility issues, be16toh undefined

Lessons Learned

// Downloads

Running the Game

# With Altirra OS (no real ROMs needed)
atari800 -ai -xl -xl-rev altirra -nobasic wowor_rebuilt.atr

# With real Atari OS ROMs
atari800 -xl wowor.xex

// Verification

The disassembly has been verified byte-identical to the original:

$ md5 wowor_full.bin wowor_rebuild_trimmed.bin

MD5 (wowor_full.bin) = f17911c9a28021c946a129696c073176
MD5 (wowor_rebuild_trimmed.bin) = f17911c9a28021c946a129696c073176

MATCH - Byte-identical!

VERIFIED Both files produce identical MD5 checksums, confirming a perfect disassembly.

// Project Status

Potential Future Work