This is a reconstructed and commented disassembly of the C64 implementation of Maniac Mansion, for educational and research purposes only. It includes the main engine code, as well as the special disk loaders used.
100% of the code is now commented. There are some naming and explanation improvements still pending.
I'm sure I'm not the first to analyze the code, as crackers of old necessarily had to do so to defeat copy protection. And at least one modern conversion to cartridge format exists, which forced the author to do analysis at a relatively deep level.
My goal is to preserve and document one of the culturally and technically significant videogames of its era, from the implementation side.
Corrections and improvements are welcome. Hope you have fun.
How to understand what you're looking at
To follow the logic of each routine, you need a basic understanding of 6502 assembly language. You don’t need to be able to write assembly, but you do need to recognize the instructions and understand what they do.
To fully grasp what each routine does within the C64 environment, you also need some familiarity with the hardware. In particular:
- Which chips matter: VIC-II for video, SID for sound, CIA for timers, input, and disk I/O
- How the CPU communicates with these chips through memory-mapped registers
- How interrupts work - especially the raster interrupt generated by the VIC-II
- The basic architecture and behavior of these chips, so you can see how the routines use them
If you don’t currently know much about C64 internals or 6502 assembly - and don’t have the time to learn - you can rely on the comments and technical notes. They were written to be as approachable as possible for readers without deep background knowledge. You can treat them as authoritative explanations; aside from the possibility of human error, they accurately describe what the code does.
Game entities
The game engine works with several types of in-game entities and represents them on screen. In the codebase, these are referred to collectively as resources.
The primary resource types are:
- rooms - Self-contained areas of the game world, connected to one another; they contain objects and scripts.
- costumes - Characters, both player-controlled and NPCs.
- objects - All non-character entities the player can interact with.
- scripts - Behavioral logic that governs how objects and costumes act.
- sounds - Audio assets, including sound effects and music.
This structure appears to be partially inspired by film-making concepts - locations, actors, props, and direction.
Representation entities
The hardware imposes strict limits on how many things can happen at once, such as:
- how many characters can be rendered simultaneously,
- how many sounds can be played concurrently,
- and how many scripts can execute in parallel.
To enforce and work within these constraints, the engine introduces several representational layers:
- actors - On-screen representations of costumes.
- virtual sound voices - Effective instances of sound playback.
- tasks - Active execution contexts for running scripts.
These representation entities allow the higher-level resources (rooms, objects, costumes, etc.) to be managed within the Commodore 64’s strict concurrency limits.
Game engine vs. interpreter
All of the actual game logic lives inside the script files - even the title screen, intro sequence, and save/load interface. These scripts are composed of instructions and conditions such as “make Dave walk to the door” or “if the microwave is running, play the microwave sound.” They have been fully disassembled and are available at: https://github.com/segrax/Maniac.Mansion.Disassembly
The game engine, on the other hand, is the system that reacts to those scripts (and to player input) and turns them into audiovisual behavior. This clean separation between logic and engine offers well-known advantages, including making it far easier to port the game to new hardware.
Because the engine executes script instructions one by one, it effectively behaves as an interpreter. In modern terms, it functions much like a virtual machine (VM) - similar in spirit to how the Java Virtual Machine executes bytecode.
Code layout
Once loaded, the game becomes a single monolithic block of code in memory, containing more than 500 routines. To make the code easier to understand and maintain, I reorganized it into conceptual modules, similar to how a modern software project would structure its source files.
The codebase can be viewed as two major components:
- The C64 audiovisual engine - the part that handles graphics, sound, input, resource loading, and all hardware-level behavior.
- The interpreter - the layer that translates script “operations” into concrete actions executed by the audiovisual engine.
All interpreter-related source files use the ops_ prefix to make this separation explicit.
This an index of what each file represents:
| File | Description |
|---|---|
| actor.asm | Actor ticking, motion, animation orchestration |
| actor_animation.asm | Limb animation and clip sequencing |
| actor_costume.asm | Costume–actor linking and spawning |
| actor_motion_core.asm | Motion state machine |
| actor_path_dda.asm | DDA stepping with walkbox validation |
| actor_sprites.asm | Sprite allocation, mapping, draw ordering |
| actor_targeting.asm | Target selection, snapping, path staging |
| actor_walkbox_traversal.asm | Walkbox resolution, behaviors, traversal |
| blit_cel.asm | Animation cel rendering |
| camera.asm | Camera control |
| cursor.asm | Cursor physics |
| decompressor.asm | Decompressor engine |
| disk_high_level.asm | High-level disk routines to load, write, and stream sectors |
| disk_low_level.asm | Low-level routines for serial communication with the disk drive |
| drive_setup.asm | Disk drive setup for the custom fastloader |
| entry_point.asm | Entry point of the game code |
| flashlight.asm | Flashlight beam rendering |
| init_engine.asm | Initialization of the game engine |
| irq_handlers.asm | Multistage IRQ handlers for sprite/actor drawing |
| input_scan.asm | Keyboard and joystick scanning |
| key_handler.asm | Keypress dispatcher |
| main.asm | Main game loop (outside interrupts) |
| masking.asm | Graphics masking helpers |
| memory_mgmt.asm | Memory allocator and free-space manager |
| misc.asm | Miscellaneous routines |
| music.asm | Music playback routines |
| pause.asm | Pause/unpause handling |
| random.asm | Pseudo-random number generator |
| render_actor.asm | Onscreen rendering of actors |
| render_object.asm | Onscreen rendering of objects |
| render_room.asm | Room viewport rendering and scrolling |
| room_gfx_rsrc.asm | Room rendering helpers |
| room_loader.asm | Room resource loader |
| rsrc_mgmt.asm | General resource loader (costumes, sounds, scripts, objects) |
| save_game.asm | Load/save game handler |
| script_engine.asm | Core of the script execution engine |
| script_primitives.asm | Primitive script operations |
| sentence_action.asm | Sentence execution engine |
| sentence_text.asm | Sentence text renderer |
| shutter.asm | Viewport shutter open/close effect |
| sid_voice_controller.asm | SID low-level voice register controller |
| sound_engine.asm | Sound player engine |
| start_game.asm | Game start/restart helpers |
| ui_interaction.asm | UI handlers for verbs, objects, sentence bar |
| ui_messages.asm | UI message rendering for the top bar |
| voice_allocation.asm | Virtual voice allocator |
| voice_scheduler.asm | Voice scheduler for the sound engine |
| walkbox_dfs.asm | Walkbox DFS path search |
| walkbox_helpers.asm | General helpers for walkbox modules |
| walkbox_snap.asm | Walkbox snapping |
| walkbox_waypoint_planner.asm | Waypoint planner for cross-walkbox paths |
| ops_camera.asm | Camera opcodes |
| ops_costume.asm | Costume opcodes |
| ops_cutscene.asm | Cutscene opcodes |
| ops_loading.asm | Resource loading opcodes |
| ops_locking.asm | Resource locking opcodes |
| ops_messages.asm | Text/message display opcodes |
| ops_misc.asm | Miscellaneous opcodes |
| ops_object.asm | Object-related opcodes |
| ops_primitives.asm | Comparison and bit-test/set opcodes |
| ops_sound.asm | Sound play/stop opcodes |
| ops_walking.asm | Walking-to-destination opcodes |
Helper Files Overview
| File | Description |
|---|---|
| actor_motion_constants.inc | Constants used in actor motion modules |
| constants.inc | Commonly used constant definitions |
| cursor_physics.inc | Cursor direction, acceleration, and drag tables |
| globals.inc | Global variable declarations |
| hotspots_metadata.inc | UI hotspots metadata |
| opcode_handlers.inc | Interpreter operation handlers |
| registers.inc | C64 hardware register aliases |
| rsrc_metadata.inc | Game resource metadata (disk locations) |
| sound_constants.inc | Sound engine constants |
| text_data.inc | Commonly used strings |
Where to start
A codebase of this size can feel overwhelming at first. There are several effective ways to begin exploring it:
-
Script-first approach: Start with the game scripts (NOT included in this repo, see the 'Game engine vs. interpreter' section), locate each corresponding operation in the interpreter (
ops_*.asm), and then follow the chain down into the engine routines that implement those behaviors. -
Bottom-up approach: Begin with the low-level modules - graphics, sound, input, disk I/O - and work upward toward the main loop and IRQ handlers.
-
Top-down approach: Start from the main loop, IRQ chain, and high-level systems, then drill down into the lower-level routines they call.
Each method offers a different perspective, and you can switch between them as needed.
Modules (from lower-level to higher-level)
Disk
disk_low_level- Low-level serial bus routinesdisk_high_level- Sector load/write/streaming logicdrive_setup- Drive initialization for the custom fastloader
Resource Loading
memory_mgmt- Memory allocator and free-space managerrsrc_mgmt- General resource loader (scripts, costumes, sounds, objects)room_gfx_rsrc- Helpers for room graphics resourcesroom_loader- Room asset loading
Sound
sid_voice_controller- SID register controlvoice_allocation- Virtual voice allocatorvoice_scheduler- Voice scheduler for sound playbacksound_engine- Sound effect playbackmusic- Music playback engine
Walking / Movement
walkbox_helpers- General helpers for walkbox moduleswalkbox_snap- Walkbox snappingwalkbox_dfs- Walkbox DFS path searchwalkbox_waypoint_planner- Waypoint planner for cross-walkbox pathsactor_walkbox_traversal- Walkbox resolution, behaviors, traversalactor_targeting- Target selection, snapping, path stagingactor_path_dda- DDA stepping with walkbox validationactor_motion_core- Motion state machine
UI
ui_messages- Message rendering (top bar)cursor- Cursor movement and physicsui_interaction- Verb/object/sentence bar interactionsentence_text- Sentence text renderer
Input Controls
input_scan- Keyboard and joystick scanningkey_handler- Keypress dispatcher
Rendering
decompressor- Graphics decompressionblit_cel- Cel renderingmasking- Foreground/background maskingactor_sprites- Sprite allocation, mapping, draw orderingrender_actor- Actor drawingrender_object- Object drawingrender_room- Room viewport rendering and scrollingcamera- Camera positioningflashlight- Flashlight beam renderingshutter- Shutter open/close transitions
Animation
actor_costume- Costume–actor linking and spawningactor_animation- Limb animation and clip sets
Scripts
sentence_action- Sentence execution enginescript_primitives- Primitive operations used by scriptsscript_engine- Core script interpreter
Other
misc- Miscellaneous helperspause- Pause/unpause logicrandom- PRNGsave_game- Save/load handling
Main Flow
entry_point- Program entryinit_engine- Engine initializationstart_game- Game start/restart helpersmain- Main loopirq_handlers- IRQ chains for rendering and updates
SCUMM Interpreter
ops_primitivesops_loadingops_lockingops_messagesops_cameraops_costumeops_objectops_soundops_miscops_walkingops_cutscene
The loader directory contains all components of the disk loader.
Begin by reading loader overview.txt, which explains the structure and flow of the loader code.
The diagrams directory provides visual diagrams that accompany and clarify the loader’s operation.
** Game engine components **
Even though the original engine is not perfectly modular (there is some unavoidable coupling between layers), it can be conceptually understood as a collection of subsystems:
-
Disk I/O system — Loads sectors from disk through custom fastloader routines.
-
Memory manager — Allocates and frees memory blocks for resources, tracks free space, and performs housekeeping (compaction and coalescing).
-
Resource manager — Loads resources from disk, tracks their usage, and evicts them when memory is needed.
-
Sound system — Plays sound effects and music stored as resources.
-
UI system, composed of:
- the top bar, which displays dialogue messages,
- the cursor, used for player interaction,
- the sentence bar, which shows the current action sentence,
- the interaction area, where verbs and inventory items are displayed.
-
Video rendering system, composed of:
-
an object renderer,
-
a room renderer,
- including a virtual camera subsystem for horizontal scrolling,
-
a costume renderer,
-
a flashlight subsystem for rendering the narrow beam of light when only the flashlight is active.
-
-
Animation system — Handles costume animation using limbs and cel definitions.
-
Blitter system, composed of:
- a cel-to-sprite blitter,
- a foreground/background masking system for costume–room occlusion,
- a front-to-back ordering system for correct costume–costume occlusion.
-
Walkbox-based pathfinding system — Computes realistic walking paths between two points within a room.
-
Sentence system — Represents player actions using a simple grammar: verb → direct object → preposition → indirect object.
-
Script/task subsystem — Executes game-logic scripts and manages multiple concurrent tasks.
-
Raster-IRQ sprite rendering subsystem — Uses timed raster interrupts to blit actor graphics to the screen each frame.
A slightly simplified dependency diagram, where an arrow means "depends on":
+-------------------------+
| Game Scripts / VM |
| (script_engine, ops_*) |
+-----------+-------------+
|
| (drives)
v
+-----------------------------------------------------------+
| High-Level Logic |
| (Actors / Animation, UI, Pathfinding, Sentence System) |
+-----------------------------------------------------------+
| | | |
| v v v
| +----------------+ +------------------+ +------------------+
| | Actors / | | UI System | | Pathfinding |
| | Animation | | (verbs, cursor) | | (walkboxes) |
| +-------+--------+ +---------+--------+ +---------+--------+
| | | |
+--------------+-------------------------+--------------------+---------+
| |
v v
+------------------+ +------------------+
| Rendering System |----->| Camera System |
| (rooms, objs, | | (viewport/scroll)|
| costumes, light)| +-------------------+
+--------+---------+
|
v
+------------------------+
| Blitter System |
| (cel blits, masking, |----> C64 Video
| front/back ordering) |
+-----------+------------+
|
v
+-------------------------+
| Decompressor System |
+-------------------------+
+-----------------------------------------------------------------------+
| Resource Management |
+-----------------------------------------------------------------------+
| +---------------------+ +---------------------+ |
| | Resource Manager |<------>| Memory Manager | |
| | (load, evict) | | (alloc/free/GC) | |
| +----------+----------+ +---------------------+ |
| | |
+----------------+------------------------------------------------------+
|
v
+----------------------+
| Disk I/O System |
| (high-level + low) |
+----------+-----------+
|
v
+----------------------+
| Drive Setup / HW |
+----------------------+
+---------------------+
| Sound System |
+---------------------+
| voice alloc/prims |
| sound engine |
| music |
+----------+----------+
|
v
+---------------------+
| SID Voice Controller|
+---------------------+
|
v
C64 Sound