SkillAgentSearch skills...

Raiders2600

Reverse Engineering Raiders of the Lost Ark for the Atari 2600

Install / Use

/learn @joshuanwalker/Raiders2600
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Raiders of the Lost Ark (Atari 2600) - Reverse Engineered Source

Original Game (1982) by Atari, Inc.
Original Designer: Howard Scott Warshaw
Disassembly & Analysis: Halkun (That's me!)


Overview

This repository contains the fully reverse-engineered and commented source code for the Atari 2600 classic, Raiders of the Lost Ark.

Project Structure

The project has been reorganized for a clean development workflow:

  • src/: Contains the main assembly source (raiders.asm) and header files (tia_constants.h).
  • bin/: Contains build tools (DASM) and emulator executable (Stella).
  • out/: Destination for compiled binaries (.bin), symbol files (.sym), and listing files (.lst). (auto generated at compile time)
  • make.bat: Windows batch script to compile the project.
  • run.bat: Windows batch script to launch the compiled game.
  • Makefile: Makefile to compile and launch the compiled in in Windows with Powershell or Linux with make.

How to Build & Run

Prerequisites

  • Windows OS

  • DASM: dasm.exe must be in the bin/ folder.

  • Stella: Stella.exe and SDL2.dll must be in the bin/ folder (optional, for running).

  • Linux

  • dasm dasm must be installed on the system.

  • stella: stella must be installed on the system.

Compiling

  • Windows

Run the build script from the root directory:

make.bat
  • Linux (or Windows PowerShell with make)

Build the rom by running make:

make

Running

  • Windows

Launch the compiled ROM in Stella:

run.bat
  • Linux (or Windows PowerShell with make)

Launch the compiled ROM in Stella:

make run

Technical Documentation

ROM Architecture

The game uses a 2-bank ROM (8KB total) with bank-switching via strobes at BANK0STROBE ($FFF8) and BANK1STROBE ($FFF9). Bank switching is done through a self-modifying code technique where opcodes are written into zero-page RAM variables and executed in-place.

  • Bank 0 (BANK0TOP = $D000): Contains game logic — collision handling, inventory management, room event handlers, scoring, movement, input processing, and sound.
  • Bank 1 (BANK1TOP = $F000): Contains the display kernels, sprite data, playfield graphics, room handler dispatch, and music frequency tables.

Game Loop

Like all Atari 2600 games, the program is structured around the NTSC television signal — one complete pass through the loop produces one frame of video (~60 fps). The frame is divided into four phases: VSYNC, VBLANK, Kernel (visible picture), and Overscan. Game logic is split across VBLANK and Overscan to stay within the CPU time budgets of each phase, and the two ROM banks are switched in and out at specific points every frame.

Frame Overview

newFrame ──spin on INTIM──►
startNewFrame
  ├── VSYNC (3 scanlines)
  │     Timers, weapon clamp, RESET check
  │
  ├── VBLANK (~37 scanlines of CPU time)
  │   ├── [Bank 0] Main game logic
  │   │     Ark Room / title ── Snake AI ──
  │   │     Event checks ── Input & movement ──
  │   │     Inventory ── Item use ──
  │   │     Sprite animation ── Mesa scroll
  │   │
  │   ├── [Bank 1] Room-specific handler
  │   │     Per-room AI, physics, spawning
  │   │
  │   └── [Bank 0] Pre-kernel setup
  │         Colors, NUSIZ, CTRLPF from tables
  │         setThievesPosX: position all 5 TIA objects
  │         Spin on INTIM until VBLANK expires
  │
  ├── VISIBLE KERNEL (~192 scanlines)
  │   ├── [Bank 1] drawScreen preamble (5 lines)
  │   │     Clear collisions, set initial PF, enable TIA
  │   │
  │   ├── [Bank 1] Room kernel dispatch (160 lines)
  │   │     staticSpriteKernel ──── rooms 0–5
  │   │     scrollingPlayfieldKernel ── rooms 6–10
  │   │     multiplexedSpriteKernel ── rooms 11–12
  │   │     arkPedestalKernel ──── room 13
  │   │
  │   └── [Bank 1] drawInventoryKernel (~27 lines)
  │         6-item inventory strip, selection cursor
  │
  └── OVERSCAN (~30 scanlines of CPU time)
      ├── [Bank 1] Post-kernel logic
      │     Sound/music ── Timepiece sprite ──
      │     Event animation ── Death sequence ──
      │     Inventory cycling ── Grapple state
      │
      └── [Bank 0] Collision handling
            Weapon hits ── Indy vs objects ──
            Room-specific pickups ── Idle handlers
            ──► newFrame (loop closes)

Phase 1: VSYNC (3 Scanlines)

Entry point: startNewFrame

The CPU asserts the VSYNC signal for exactly 3 scanlines, during which it performs lightweight housekeeping:

| Scanline | Work | | -------- | ---- | | 1 | Assert VSYNC. Clamp weapon position — if weaponPosY$50, center weaponPosX. Increment frameCount; every 64th frame (and #$3f) increments timeOfDay. If eventTimer is negative, decrement it (paralysis/cutscene countdown). | | 2 | Check for game restart — if arkRoomStateFlag bit 7 is set (endgame state) AND the RESET switch is pressed, jump to startGame. | | 3 | De-assert VSYNC. Arm the VBLANK timer: TIM64T = VBLANK_TIME (44). This gives 44 × 64 = 2,816 cycles ≈ 37 scanlines of CPU time for game logic. |

Phase 2: VBLANK (~37 Scanlines of CPU Time)

All game logic runs while the TIA outputs a blank screen. Work is spread across both ROM banks with two bank switches during this phase.

Bank 0 — Main Game Logic

This is the largest block of game code, executing in order every frame:

| Step | Label | Description | | ---- | ----- | ----------- | | 1 | checkGameOver | Start logic on first scan line: if indyStatus overflows to 0, call getFinalScore and transition to the Ark Room. | | 2 | checkForArkRoom | Ark Room / title screen: if in the Ark Room, play Raiders March, check Yar bonus for HSW initials easter egg. Otherwise skip. | | 3 | (Ark Room only) | Pedestal elevator: slowly lower Indy to his score height. Check fire button for restart. Set arkRoomStateFlag to enable RESET. | | 4 | checkScreenEvent | Cutscene check: if screenEventState bit 6 is set, advance the Ark reveal sequence. | | 5 | updateSnakeAI | Snake AI: every 4th frame, grow snake sprite, steer toward Indy using snakePosXOffsetTable, update ballPosX/ballPosY and kernelRenderState. | | 6 | configSnake | Snake kernel setup: load kernelDataPtrLo/Hi and kernelDataIndex from snakeMotionTable0snakeMotionTable3 for the wiggling ball sprite. | | 7 | checkIndyStatus | If indyStatus bit 7 is set (death in progress), skip to dispatchRoomHandler — bypass normal input. | | 8 | checkGameScriptTimer | If eventTimer is negative (Indy paralyzed/frozen), force standing sprite and skip input. | | 9 | branchOnFrameParity | Frame parity split: even frames run full input processing. Odd frames skip to clearItemUseOnButtonRelease. | | 10 | handleWeaponAim | Weapon aiming: joystick moves the weapon crosshair (missile 1) with boundary clamping. | | 11 | handleIndyMove | Movement: read SWCHAgetMoveDir → move Indy → check room boundary override tables (checkRoomOverrideCondition) → trigger room transitions if a boundary is crossed. | | 12 | handleInventorySelect | Left controller: fire button cycles through inventory slots. Handles item drop, bullet reload (+3), and shovel placement. | | 13 | clearItemUseOnButtonRelease | Clears USING_GRENADE_OR_PARACHUTE flag on right fire button release. | | 14 | handleItemUse | Right controller: fire button dispatches the selected item — grenade throw/cook timer, parachute deploy, grapple hook launch, shovel dig, Ankh warp, revolver fire, whip strike. This is the largest single block in VBLANK. | | 15 | selectIndySprite | Sprite selection: choose Indy's current sprite pointer — parachute sprite, standing sprite, or walk-cycle animation (advances frame on a timer). | | 16 | handleMesaScroll | Vertical scrolling: in Mesa Field or Valley of Poison, shift the camera offset (roomObjectVar) and adjust all object Y positions to scroll the world. |

Bank Switch → Bank 1: Room Handlers

At dispatchRoomHandler, the code writes selectRoomHandler as the target address and jumps through the jumpToBank1 trampoline.

selectRoomHandler dispatches via roomHandlerJmpTable — each room has its own handler that runs room-specific AI and physics:

| Room | Handler | Key Logic | | ---- | ------- | --------- | | Treasure Room | treasureRoomHandler | Item cycle timer, treasure availability, treasure spawning | | Marketplace | (none — immediate return) | — | | Entrance Room | entranceRoomHandler | Sets screenEventState = $40 | | Black Market | blackMarketRoomHandler | Lunatic/blocker positioning, bribe check | | Map Room | mapRoomHandler | Sun height from timeOfDay, Head of Ra beam, movement constraints | | Mesa Side | mesaSideRoomHandler | Parachute/freefall physics, gravity, horizontal input | | Temple Entrance | templeEntranceRoomHandler | Timepiece placement, room graphics from entranceRoomEventState | | Spider Room | spiderRoomHandler | Spider AI (passive→aggressive), web positioning, animation | | Shining Light | roomOfShiningLightHandler | Chase AI, dungeon secret exit check | | Mesa Field | mesaFieldRoomHandler | Pins P0/M0/Ball to center Y ($7F) for scrolling | | Valley of Poison | valleyOfPoisonRoomHandler | Thief chase/escape AI, tsetse swarm spawning | | Thieves' Den | thievesDenRoomHandler | Moves 5 thieves with left/right boundary bounce | | Well of Souls | wellOfSoulsRoomHandler | Sets mesa landing bonus, then shares thief-movement code |

All handlers exit via jmpSetupNewRoom, which bank-switches back to Bank 0.

Bank Switch → Bank 0: Pre-Kernel Setup

setupNewRoom prepares the TIA for display:

  1. If screenInitFlag ≠ 0, call updateRoomEventState (one-shot room initialization), then clear the flag.
  2. Set NUSIZ0 from the per-room hmoveTable entry.
  3. Set CTRLPF from `roomP
View on GitHub
GitHub Stars167
CategoryDevelopment
Updated21d ago
Forks5

Languages

Assembly

Security Score

80/100

Audited on Mar 14, 2026

No findings