ATAMON V1.3


Table of Contents


Foreword

The documentation for ATAMON (ATAri MONitor) has been missing for over 40 years.

What you are reading is an unofficial documentation, the result of hours of work, testing, decompilation and writing from the disassembled code of ATAMON.

I hope you enjoy it ;-)

Atarinside — March 2026

The program can be found here: https://www.atarinside.com/blog/index.php/atarinside-items/atamon/


What is ATAMON?

You have just loaded ATAMON, ATARI's machine language monitor. But what exactly does a monitor do?

Your Atari 800XL runs on a processor — the 6502 — which executes thousands of instructions per second. These instructions — called "machine language" — are stored in memory as numbers. ATAMON is the tool that lets you view, modify and execute these instructions directly, without going through an intermediate language like BASIC.

Concretely, ATAMON lets you:

Who is it for? ATAMON is aimed at programmers, enthusiasts who wish to understand the inner workings of their Atari, or anyone who wants to explore and modify machine language programs. No additional hardware is required: ATAMON runs entirely on your Atari, with your standard disk drive.

How to start? From the Atari DOS menu, select L. BINARY LOAD, type D:ATAMON and press Enter. The program installs itself in memory starting at address $4000, displays its banner, and the Atari cursor indicates it is ready to receive a command. All commands are a single letter — fast and efficient.


Technical Documentation

Machine Language Monitor for the Atari 800XL


Source file: Atamon - D7 - DXG 5724.PROATAMON Identification: *** ATAMON V1.3 - (c) 1983 by ATARI *** Type: 6502 machine language monitor / debugger Size: 4,404 bytes Format: Atari XEX (multiple segments) Load address: $4000 Run address (RUNAD): $4000 Source language: 6502 assembly Publisher: Atari Deutschland GmbH (Atari Germany) — ATAMON is an Atari Germany product, identified by catalogue number DXG 5724 Author: Not identified — the binary contains no programmer name, only (c) 1983 by ATARI Probable assembly tool: Atari Macro Assembler or MAC/65 (OSS) — inferred from the structure of 256-byte XEX segments (252 bytes of code + 4-byte header), a characteristic output format of native Atari assemblers from 1983


1. General Overview

ATAMON is a professional machine language monitor developed by ATARI in 1983. It allows a programmer to:

It belongs to the family of machine language monitors that accompanied 8-bit computers of the era on many platforms.


2. Memory Layout

Memory footprint: approximately 4.5 KB from $4000 to $52B1. ATAMON also sets MEMLO = $5300 to protect its code.


3. Startup Mechanism

The entry point at $4000 uses a classic technique: the BRK vector ($0206–$0207).

Why this technique? The BRK instruction automatically pushes the address of the next instruction (current address + 2) and the status register onto the stack. ATAMON then retrieves these values to build a complete snapshot of the processor state at the time of entry into the monitor.

Register Save Area ($401C)

Note on the entry mechanism: The Atari OS handler (ROM IRQ) pushes register A via PHA before calling the VBREAK vector. This is why SAVE_CPU performs 4 successive PLAs (A, P, PCL, PCH) even though the BRK instruction only pushes 3 (P, PCL, PCH).

Register save area ($4F3C–$4F42):

This layout is confirmed by the G (Go) handler: LDA $4F3D restores A, LDA $4F40; PHA; PLP restores P, JMP ($4F41) jumps to address $4F42:$4F41.


4. Main Command Loop

After initialisation, ATAMON displays its banner and enters the command loop ($4064):

Command Prompt and Register Display

ATAMON does not display an explicit prompt: the input prompt is the Atari screen editor cursor (blinking inverse-video block), managed automatically by the OS. When this cursor is visible, ATAMON is waiting for a command.

The *R, *B, *C headers are ATAMON display prefixes, not the prompt. The asterisk * (code $2A) is emitted by ATAMON before displaying registers, followed by a letter indicating the entry context:

Example register display (after R command or a BRK):

The value line always begins with ;. Meaning of each field:

FieldWhat it isValue in the example
PCProgram Counter: address of the next instruction to be executed. This is where your program "is" in memory.4000 → ready to execute from $4000
SPStack Pointer: indicates where the top of the stack is. The Atari stack is at $0100–$01FF; SP=FD means the top is at $01FD, stack nearly empty.FD → stack reset
ACAccumulator A: the 6502's main register, used for calculations, transfers, comparisons.00
XRX Register: index register, often used for loops and indexed addressing.00
YRY Register: second index register.00
NV\BDIZCProcessor flags (P register), displayed bit by bit in binary (0 or 1). Each bit indicates a particular state of the last calculation. See the detailed description in the ; command.00110000 → flags I and B active

This line updates automatically after each program return (following a BRK or G), to reflect the actual processor state at that instant.


5. How to Load Your Program into ATAMON

The Atari stores the auto-run address of the last loaded program in a specific memory location at address $02E0 (the system vector called RUNAD).

If you load your game, its start address will be stored at $02E0. But if you then load ATAMON right after, ATAMON's start address ($4000) will overwrite your game's!

To work around this and use ATAMON as a detective tool, we reverse the loading order using another DOS menu option (M).

The Double-Agent Plan

Step 1: Install the monitor "undercover"

  1. From the DOS menu, use L to load ATAMON.

  2. ATAMON displays on screen. Type the command X (eXit).

  3. You return to the DOS menu, but ATAMON remains hidden in memory (from $4000 onwards).

Step 2: Load your game silently

  1. Back in DOS, use L again to load your program, adding /N (no space after the filename) — example: MYPROG.BIN/N.

  2. The game loads into memory and the OS writes its start address to $02E0. Because we used /N, DOS does not jump to that address, does not execute the loaded program, and keeps you in the menu.

Step 3: Wake ATAMON up

  1. In the DOS menu, choose option M (Run at address).

  2. Type 4000 (the address where ATAMON is sleeping) and press Enter.

  3. The ATAMON banner appears — you are back in the monitor!

Step 4: Read the magic address

  1. At the ATAMON prompt, display memory at address $02E0 by typing:

  1. Press BREAK immediately to stop the scrolling.

  2. Look at the first two bytes of the displayed line.

Attention — Little Endian: the Atari stores addresses "backwards" (low byte first, then high byte).

  • If ATAMON displays :02E0 00 50 ..., your program's address is $5000.

  • If it displays :02E0 00 2A ..., the address is $2A00.

That is it! You just need to use the G command followed by the discovered address (e.g. G 5000) to launch your program, knowing that ATAMON is ready to intercept any crash or breakpoint (BRK).

Setting Breakpoints in Your Program

Warning: ATAMON does not have a guaranteed asynchronous interrupt function.

The only reliable method to hand control back to ATAMON in the middle of a program is to deliberately insert a software breakpoint:

  1. Temporarily replace one byte of your program with the BRK instruction (hex code $00).

  2. When the processor executes that $00, it will trigger a software interrupt.

  3. ATAMON has redirected the system interrupt vector (VBREAK at $0206$0207) when it loaded.

  4. The processor will jump directly into ATAMON, which will save all your registers (A, X, Y, PC...) and display its prompt with *B to signal that it has intercepted a BRK.

In summary: if your program is an infinite loop with no BRK instruction, the BREAK key will not save you (unless your code calls the OS keyboard routines). You will need a Reset! Always remember to place your $00 breakpoints strategically before launching your code with G.


6. Command Reference

The command table is stored from $4F9D. Commands are entered in uppercase. When a command takes multiple arguments, they are separated by commas (no space between arguments). A space is accepted before the first argument.

Syntax Conventions


G [aaaa] — Go (Execute)

Starts execution at address aaaa. Without argument: resumes from the saved program counter (PC register). Restores all saved CPU registers before performing JMP aaaa.

Warning: G aaaa jumps directly to the given address. If that address does not contain a valid program (e.g. uninitialised RAM), the 6502 will execute garbage and the system will crash. G alone is safe: it resumes from the PC saved at the last BRK.

First use after startup: the saved PC is $400B. Typing G alone re-executes ATAMON startup.

Examples:


M aaaa — Memory (Display Memory)

Displays memory starting at aaaa, 8 bytes per line in hex. Scrolls indefinitely — press BREAK to stop.

Each line: : + address + 8 hex bytes + ATASCII column on the right.

Example:


E ssss[,aaaa[,nn]] — E (Read a Disk Sector)

Reads disk sector ssss (hex) from D1: into the buffer at aaaa (default: $0680). Displays OK on success.

Optional arguments:

Caution: ssss is a sector number, not a memory address. A single-density Atari disk (720 sectors) has sector numbers $0001 to $02D0. Exceeding this range causes an I/O error.

To view the loaded sector content: type M 0680 — ATAMON displays the bytes from $0680 scrolling (press BREAK to stop after seeing the first 128 bytes of the sector).


D aaaa[,bbbb] — Disassemble

Disassembles 6502 code from aaaa and displays the mnemonics. Uses the internal tables at $4FF9 containing the 56 6502 mnemonics and addressing mode decoding information.

Important: the end address bbbb is syntactically accepted but ignored — ATAMON disassembles indefinitely, wraps $FFFF→$0000. Press BREAK to stop.

Each line shows: address, raw bytes in memory, then the disassembled instruction.


L aaaa,bbbb,xx — Locate Non-Matching (Find Non-Matching Bytes)

Scans aaaabbbb and displays addresses where the byte is different from xx.

In practice: L identifies bytes that "do not match" a reference value. Useful for quickly spotting differences between two regions after a copy, or finding modified bytes in a program.

L alone → error ? — three arguments required.

Note: ATAMON has no file load command. Programs must be loaded from Atari DOS (option L. BINARY LOAD) before launching ATAMON.


W ssss[,aaaa[,nn]] — Write (Write a Disk Sector)

Writes the buffer at aaaa (default: $0680) to disk sector ssss (hex).

Optional arguments: same as E — source buffer address and sector count.

Caution: overwrites the sector on disk directly — irreversible. ssss is a sector number in hex (1 to $02D0 = 720 for a single-density disk).

E and W work in tandem: E to read a sector, modify with M or :, then W to rewrite.


V aaaa,bbbb,cccc — Verify (Compare Memory)

Compares aaaabbbb with the region at cccc (memory to memory). Reports addresses where bytes differ. No changes made.

Useful to verify that a copy (made with T) matches the original, or to compare two versions of a program.

Note: if the destination region is uninitialised (arbitrary RAM), almost all addresses in the range will appear in the result.


F aaaa,bbbb,bb — Fill

Fills aaaabbbb with byte bb. Displays OK.

Safe zone: use $6000+ (ATAMON occupies $4000–$52B1).


T aaaa,bbbb,cccc — Transfer (Copy Memory)

Copies aaaabbbb to cccc (memory to memory). Performs byte-by-byte verification and reports differences. Displays OK on success.

Overlapping regions: if source and destination overlap, a naive start-to-end copy would overwrite unread source bytes. ATAMON avoids this: if destination is before the source it copies left-to-right; if destination is after the source (right overlap) it copies right-to-left. The result is always correct regardless of configuration.

Safe zone: ATAMON occupies $4000–$52B1. For tests, use a region beyond $5300 (e.g. $6000–$7FFF, free RAM).


H aaaa,bbbb,DsearchD — Hunt (Search Memory)

Searches aaaabbbb for byte sequence DsearchD. Displays addresses found, then OK.

D = any delimiter character. Data: hex pairs or 'string' in single quotes. Without internal single quotes, letters are interpreted as hex digits — AT = A (valid hex) + T (invalid) → corrupted result.

Search only — no replace. Three arguments minimum required (H or H aaaa,bbbb?).


S aaaa — Scan (Real-Time Binary Monitor)

Displays 4 bytes at aaaa as binary, refreshing the same line continuously. Press BREAK to stop.

S alone → ?. No single-step: insert BRK instructions manually.

Example with a fixed address:

Example with a real-time hardware register:

Particularly useful for monitoring hardware registers (POKEY, GTIA, ANTIC, RTCLOK clock) that change in real time, or observing a variable evolve during program execution.


C aaaa,bbbbbbbb — Code (Write Binary Bytes to Memory)

Writes bytes at aaaa. Each byte = 8 binary digits (0 or 1), separated by commas.

C alone → ?

Note: Buggy in original V1.3 — writes $2C (comma) instead of parsing binary arguments and returns ?. The examples below describe the behaviour of the patched version only (Atamon - D7 - DXG 5724-Patched Atarinside.atr). See Fixing the C Command Bug.

Multiple bytes:


P [E|T] — Printer (Printer Control)

Controls the printer channel (IOCB 6, device P1:).

Note: this command is useful if you have a printer connected to your Atari. Without a printer, P E will cause an I/O error.


R — Registers (Display Registers)

Displays all saved CPU registers. Display only — no modification.

To modify registers, use the ; command (full load) or write directly to memory at $4F3C–$4F42.


X — eXit (Quit to DOS)

Returns to Atari DOS via JMP DOSVEC ($000A).


Z nn,cc[,aaaa,llll] — IOCB (CIO Channel Operation)

Executes an OS I/O operation on a CIO channel.

The CIO System in Brief

The CIO provides a single entry point ($E456) for all devices: whether writing to the screen, reading the keyboard or accessing a disk, it is always the same OS routine you call. The device handler (a specific ROM or RAM routine) does the real work.

ROM-resident handlers (always available, loaded automatically at startup):

NameDeviceAccessNote
E:Screen editor — line-by-line input with interactive editingread+writeUses K: and S: internally
K:Keyboard — direct key readingread onlyNever opened directly — used internally by E:
S:Graphics screen — bitmap access, DRAW and FILL commandsread+write 
P:Printer — on XL/XE: P1: to P8: depending on modelwrite only 
C:Cassetteread+writeDoes not use SIO protocol

Non-resident handlers (loaded into RAM at boot by DOS or a peripheral ROM):

NameDeviceAccessLoaded by
D:Disk — DOS file manager (D1:–D8:)read+writeDOS, at boot
R:850 Interface — RS-232 serial ports (R1:–R4:)read+write850 interface ROM, at boot
T:1030 Modem (XM301, 835)read+writeModem ROM, at boot

These non-resident handlers are added to the handler table ($031A) at startup as soon as the corresponding device is connected and powered on. If the device is absent, the handler is not loaded and any attempt to access D:, R: or T: via CIO returns an "unknown device" error.

IOCB Channels

ChnnAddressAssignment
000$0340–$034FE: screen editor — opened by the OS at startup, always available
110$0350–$035Ffree — available for any program
220$0360–$036Ffree — available for any program
330$0370–$037Ffree — available for any program
440$0380–$038Ffree — available for any program
550$0390–$039Ffree — available for any program
660$03A0–$03AFreserved by Atari BASIC for S: (graphics modes ≠ 0)
770$03B0–$03BFreserved by Atari BASIC for P:/D:/C: (printer, disk, cassette)

Channels 1 to 5 have no fixed assignment — by design. Non-resident handlers (D:, R:, T:) do not open on a dedicated channel: the program freely chooses which channel to use for each device. For example, a program can open D:FILE.DAT on channel 1 and R1: (serial port) on channel 2 simultaneously. If BASIC is active, avoid using channels 6 and 7.

A record is a byte sequence terminated by ATASCII $9B. GET RECORD reads up to the next $9B; PUT RECORD writes and appends $9B at the end.

Z Syntax

Z alone or Z nn alone → ?cc is required.

ccCommand
03OPEN
05GET RECORD (read to $9B)
07GET BYTES
09PUT RECORD (write + $9B)
0BPUT BYTES
0CCLOSE
0DSTATUS

Z copies the current channel IOCB, replaces the supplied fields (command, address, length), calls CIO, then rewrites the modified IOCB. No result is displayed — the effect is the output of the CIO operation itself.

Example — display text on screen via PUT RECORD on channel 0 (always open on E:):

To inspect the raw content of an IOCB, use M 0340 (channel 0) or M 0350 (channel 1).


I aaaa,DdataD — Input String (Write Text to Memory)

Writes text or binary data to memory at aaaa.

D = any delimiter (", /...). No space between comma and delimiter.

Inside the data:

This command is particularly useful for writing error messages, ATASCII strings, or code patches directly into memory.


# — Decimal to Hexadecimal Conversion

Interprets the argument as a decimal value and displays its hexadecimal equivalent.

Useful when you know an address or value in decimal and want to know its hex equivalent before using it in another command.


$ — Hexadecimal to Decimal Conversion

Interprets the argument as a hexadecimal value and displays its decimal equivalent (complement of #).

Important: $ requires exactly 4 hexadecimal digits. Use $ 00FF not $ FF.


% — Binary Input

Enters a value as 8 binary digits (0 and 1) and displays it in hexadecimal.

Useful for working directly with processor flags or any register whose bits you want to control individually — more readable than a hex byte for this type of manipulation.


; aaaa BB BB BB BB XXXXXXXX — Set (Load All Registers at Once)

Loads all CPU registers in a single command line. Syntax: ; PCHL SP AC XR YR XXXXXXXX

Useful for restoring a precise execution context in one operation, e.g. to resume debugging a program exactly in the state it stopped.

Flag bits (left to right):

BitLetterNameSet when...
7NNegativeBit 7 of last result = 1
6VoVerflowSigned overflow
5**(unused)Always 1
4BBreakEntry via BRK
3DDecimalBCD mode (non-functional on Atari)
2IInterrupt disableIRQ masked
1ZZeroLast result = 0
0CCarryCarry/borrow

Example: 00110100 = bits N=0, V=0, \=1, B=1, D=0, I=1, Z=0, C=0 — typical state at startup (I=1: interrupts disabled).


! aaaa — Binary (Hexadecimal to Binary Conversion)

Converts hex aaaa to 16-bit binary and displays it. Does not read memory — converts the argument itself.

Format: ! aaaa = bbbbbbbb bbbbbbbb (high byte then low byte).

Useful for visualising the individual bits of a value (hardware register read beforehand, operation result, flag value…).


* OP aaaa,bbbb — Arithmetic (Calculator)

Arithmetic/logical operation on two 16-bit values. Result: RRRR C=b.

Operators: + - A (AND) O (OR) E (EOR/XOR).

* + 4000 alone → ? — both operands required.

Useful for calculating addresses, offsets or verifying memory ranges without leaving the monitor.


: aaaa bb bb bb bb bb bb bb bb — Enter (Write Bytes to Memory)

Writes 8 hex bytes at aaaa, displays the line, proposes next address. BREAK to return.

: aaaa alone → nothing.

To enter a large region, continue line by line: each ENTER advances 8 bytes. To enter fewer than 8 bytes, use the I command.

Limitation: : verifies each byte after writing by reading the address back. This verification fails for hardware registers (POKEY $D200–$D20F, GTIA $D000–$D01F, ANTIC $D400–$D40F, PIA $D300–$D30F) because read and write addresses map to different physical registers. To write to hardware, store machine code in RAM and run it with G.


, aaaa bb [bb ...] — Enter with Disassembly

Writes hex bytes at aaaa and displays 6502 disassembly. Number of bytes per line is free. BREAK to return.

Ideal for entering assembly code and immediately verifying that the opcodes are correct.


7. Internal Variables (Zero Page)

AddressLocal nameRole
$DA-$DBPTR_START16-bit start address (argument 1)
$DC-$DDPTR_END16-bit end address (argument 2)
$DE-$DFPTR_DEST16-bit destination address (argument 3)
$E0MODE0 = normal mode, ≠0 = reverse mode
$E4TEMP_ATemporary / accumulator save
$E5TEMP_BCounter / temporary
$E7COL_CTRColumn counter for hex display (0-6)
$EBTEMP_CDisplay temporary
$F2BUF_PTRPointer into buffer $4E3C
$F3-$F4FP_PTRPointer to Math ROM floating-point area

8. Main Subroutines

AddressDescription
$401CSAVE_CPU: Saves 6502 state (accumulator, X, Y, flags, program counter, stack)
$4054PRINT_HEADER: Displays banner and register header
$4064CMD_LOOP: Main command read/dispatch loop
$40B5CLR_RANGE: Clears address registers DA-DF to zero
$40C5DUMP_8: Displays 8 bytes in hexadecimal
$40DBFILTER_CHAR: Filters an ATASCII character (printable / non-printable)
$4127ADD16: Adds A to the 16-bit address DA:DB
$4133DO_CIO: Calls CIO ($E456) with X=IOCB number×16
$41DDDO_SIO_DISK: Sends an SIO disk command via DSKINV ($E453)
$41E8FIND_CMD: Searches a character in the command table $4F8D
$41F7GET_CHAR: Reads the next character from the input buffer
$422ASKIP_SP: Advances buffer pointer skipping spaces
$423DREAD_BYTE: Reads 2 hex digits → byte in A
$4256READ_ADDR: Reads 4 hex digits → 16-bit address in A(hi) Y(lo)
$4280READ_BIN: Reads 8 binary digits → byte in A
$42C7SET_FNAME: Sets filename address in IOCB
$42CFSET_BUFADDR: Sets buffer address in IOCB
$42E9OPEN_EDITOR: Opens channel 0 on E: (screen editor)
$4330PRINT_HEX8: Displays a byte as 2 hex digits
$4349NEWLINE_REGS: Newline + register display
$4373NEWLINE: Sends carriage return ($9B) on E:
$4399PRINT_CHAR: Displays an ATASCII character via CIO
$44A4FP_TO_STR: Floating-point to string conversion (Math ROM IFP+FASC)
$44C5SET_FP_PTR: Initialises floating-point pointer ($4D99)

9. Atari System Usage

ATAMON uses the following OS services:

ServiceAddressUsage
CIO — Central I/O$E456 (CIOV)Screen read/write, files
SIO — Serial I/O disk$E453 (DSKINV)Disk sector access
Math ROM IFP$D9AAInteger to floating-point conversion
Math ROM FASC$D8E6Floating-point to ASCII conversion
VBREAK$0206-$0207BRK vector (monitor entry on breakpoint)

IOCBs used:

DCB structure for disk access ($0300-$030B):


10. Internal Data Tables

The string stored in memory is:

ATAMON points to the PC SP AC XR YR NV\BDIZC portion (at $4F71) to display the column header. The screen output (40 columns) is:

Hex Digit + Command Table ($4F8D)

Dispatch Table ($4FC8)

Byte pairs (address - 1) (lo, hi) for each command handler. The dispatch uses the RTS trick: the handler address minus 1 is pushed onto the stack (hi first, then lo), then RTS is executed. The 6502 retrieves the address, increments it by 1 and jumps to the handler. This avoids a JMP table and saves a few bytes.

Example: command 'G' (5th character in $4F9D) → ATAMON reads the 2 bytes at $4FC8 + (index × 2), pushes them in reverse order, and does RTS to the handler.

Built-in Disassembler Tables ($4FF9+)

ATAMON has its own mini-disassembler (command D). It uses two internal tables:

  1. 6502 Mnemonics: 3-letter encoded entries (56 official mnemonics)

  2. Addressing modes: table of operand sizes and formats for each opcode

These tables cover only the official opcodes of the 6502. Illegal opcodes are not recognised by ATAMON's disassembler and would be displayed incorrectly.

→ See Annex B at the end of this document for the complete list of the 56 recognised mnemonics.

Note: in the ATAMON binary, addresses $4F43–$52B1 contain data (text strings, character tables, vectors). A linear disassembler interprets these bytes as instructions — many of which fall into the category of undocumented 6502 opcodes ($02 *KIL, $03 *SLO, $3A *NOP, etc.). These are false positives due to the nature of the data, not intentional use of illegal opcodes in ATAMON's code.


11. How to Use ATAMON

Starting Up

From Atari DOS, select L. BINARY LOAD then type D:ATAMON. ATAMON displays its banner (*C + registers) and the Atari cursor waits for your first command.

Special Keys

KeyEffect in ATAMON
RETURNValidates the current command
BREAKInterrupts a running program and returns control to ATAMON (via VBREAK/BRK vector)
RESETHardware reset — avoid: clears memory and destroys the loaded program
DELETE / BACK S.Deletes the last character typed
ESCMay cancel current input depending on CIO/editor context

Note: ATAMON relies on CIO (screen editor E:) to read commands. The standard Atari editor functions (cursor movement, deletion) are available before validating with RETURN.

Typical Startup Display

Sample Session


12. Historical Notes

ATAMON (abbreviation for ATAri MONitor) is a machine language monitor marketed by ATARI in 1983 in Germany, the same year as the launch of the Atari 800XL.

The DXG 5724 disk also contains:

These three programs make up a complete disk: DOS + utilities + debugging monitor.

Analysis of the disk revealed no particular copy protection. Analysis of all 720 sectors confirmed the absence of phantom sectors (phantom_count = 0 for all sectors), intentional bad CRCs, or any other copy protection mechanism. The original ATAMON disk was not copy-protected.


13. Technical Notes on Disassembly

Known 6502 Illegal Opcodes

GroupOpcodesDescription
*KIL$02,$12,$22,$32,$42,$52,...Locks the CPU permanently
*NOP$1A,$3A,$5A,$7A,$DA,$FA (1 byte)Illegal no-operation
*NOP$04,$44,$64,$14,$34,$54,... (2-3 bytes)NOP with ignored operand
*SLO$03,$07,$0F,$13,$17,$1B,$1FASL memory then ORA A
*RLA$23,$27,$2F,$33,$37,$3B,$3FROL memory then AND A
*SRE$43,$47,$4F,$53,$57,$5B,$5FLSR memory then EOR A
*RRA$63,$67,$6F,$73,$77,$7B,$7FROR memory then ADC A
*LAX$A3,$A7,$AF,$B3,$B7,$BFLoads A and X simultaneously
*SAX$83,$87,$8F,$97Stores A AND X
*DCP$C3,$C7,$CF,$D3,$D7,$DB,$DFDEC memory then CMP A
*ISC$E3,$E7,$EF,$F3,$F7,$FB,$FFINC memory then SBC A
Misc$0B/$2B *ANC, $4B *ALR, $6B *ARR, $CB *SBX, $9B *TAS, $9C *SHYVarious combinations

JMP Indirect Bug

The 6502 has a hardware bug: JMP (addr) incorrectly reads the high byte if addr ends in $FF. Example: JMP ($10FF) reads $10FF (lo) and $1000 (hi) instead of $1100. In ATAMON, the two JMP (ind) present ($47BF JMP (DOSVEC) and $47E4 JMP ($4F41)) do not have addresses ending in $FF — no bug in this program. Note: JMP ($4F41) is the execution jump to the saved address (PCL at $4F41, PCH at $4F42).


Annex A — Zero Page Glossary ($00$FF)

The zero page is the first 256-byte block of RAM ($00 to $FF). On the 6502, it is special for two reasons:

The Atari OS and ATAMON use it extensively. Here are the most important locations, explained for a beginner:

Atari OS Variables (Reserved by the System)

AddressOS nameWhat it is
$08COLCRSCursor column (0 to 39). The OS updates this each time you type a character. You can modify it to move the cursor horizontally.
$09ROWCRSCursor row (0 to 24). Same principle for the row.
$0B$0CSAVMSCScreen memory address (2 bytes). Points to the first byte of the displayed image. Modifying this area directly changes the screen in real time.
$14$16RTCLOKReal-time clock (3 bytes). Automatically incremented 60 times per second by the OS (at each vertical interrupt). Useful for measuring time or creating delays.
$55ATRACTScreen saver. The OS increments this counter each second of keyboard inactivity. When it exceeds 127, colours fade to protect the screen. Reset to 0 to disable the screen saver.
$6ARAMTOPTop of available RAM. Value in pages (×256). Indicates how far RAM is usable by programs. Modifiable to reserve high memory.
$80$9FFR0Floating-point register 0 (6 bytes). Math ROM working area for floating-point calculations. Overwritten on every Math ROM call.
$A0$A5FR1Floating-point register 1 (6 bytes). Second floating-point register, used as operand in calculations.

Variables Used by ATAMON

These addresses are reserved by ATAMON while it is loaded. Modifying them with : or , may disrupt the monitor's operation.

AddressATAMON nameWhat it is
$DA$DBPTR_STARTStart address of the current operation (argument 1 of M, D, F, T... commands).
$DC$DDPTR_ENDEnd address (argument 2).
$DE$DFPTR_DESTDestination address (argument 3, used by T).
$E0MODEDisplay mode: 0 = normal, ≠ 0 = reverse (for M).
$E4TEMP_ATemporary: accumulator save between two internal operations.
$E5TEMP_BTemporary counter (internal loops).
$E7COL_CTRColumn counter for hex display (0 to 6, to align 8 bytes per line).
$F2BUF_PTRCommand buffer pointer ($4E3C). Indicates how far the input line reading has progressed.

Tip: use M 0000 00FF to display the entire zero page and observe how these values change with different operations.


Unofficial documentation reconstructed from binary XEX analysis and 6502 disassembly. References: De Re Atari (APX 90009), Atari 8-bit FAQ, Altirra 4.40 source, AtariWiki — 6502 Assembly Code, Unused Opcodes, OS ROM listing, Mapping the Atari, Atari Custom Display Lists (Atarimania), Display Lists Simplified (Atari magazines), ANTIC registers (xmission.com/~trevin).


Annex B — The 56 Official 6502 Mnemonics

ATAMON's built-in disassembler (command D) recognises exactly these 56 instructions.

Load / Store

MnemonicFull nameDescriptionExample
LDALoad AccumulatorLoads a value into accumulator ALDA #$41 → A = 'A'
LDXLoad XLoads a value into register XLDX #$00 → X = 0 (loop init)
LDYLoad YLoads a value into register YLDY #$08 → Y = 8 (counter)
STAStore AccumulatorWrites A to memorySTA $D800 → sends colour to GTIA
STXStore XWrites X to memorySTX $00 → saves X in zero page
STYStore YWrites Y to memorySTY $CB → saves Y in a variable

Register Transfers

MnemonicFull nameDescriptionExample
TAXTransfer A to XCopies A into XLDA #$10 / TAX → X = 16
TAYTransfer A to YCopies A into YTAY → Y ← A (to index a table)
TXATransfer X to ACopies X into ATXA / CLC / ADC #$40 → address calculation
TYATransfer Y to ACopies Y into ATYA / STA $CB → saves Y as value
TSXTransfer SP to XCopies stack pointer SP into XTSX → X = current stack value
TXSTransfer X to SPCopies X into stack pointer SPLDX #$FF / TXS → resets stack

Stack

MnemonicFull nameDescriptionExample
PHAPush AccumulatorPushes A (save)PHA before JSR, to preserve A
PLAPull AccumulatorPulls into A (restore)PLA after JSR, restores A
PHPPush Processor statusPushes status register PPHP / SEI / … / PLP: critical section
PLPPull Processor statusPulls into PPLP restores flags saved by PHP

Arithmetic

MnemonicFull nameDescriptionExample
ADCAdd with CarryA ← A + operand + CCLC / LDA #$10 / ADC #$05 → A = $15
SBCSubtract with CarryA ← A − operand − (1−C)SEC / LDA #$10 / SBC #$03 → A = $0D

Increment / Decrement

MnemonicFull nameDescriptionExample
INCIncrement memory+1 to a byte in memoryINC $0600 → increments score at $0600
INXIncrement XX ← X + 1INX / CPX #$10 / BNE LOOP: 16× loop
INYIncrement YY ← Y + 1INY / LDA (PTR),Y: advances through a table
DECDecrement memory−1 to a byte in memoryDEC $CB → decrements a lives counter
DEXDecrement XX ← X − 1LDX #$08 / LOOP: DEX / BNE LOOP: waits 8×
DEYDecrement YY ← Y − 1DEY / BNE LOOP: loops while Y ≠ 0

Logical Operations

MnemonicFull nameDescriptionExample
ANDLogical ANDA ← A AND operand (bitwise)AND #$0F → keeps lower 4 bits (nibble)
ORALogical ORA ← A OR operand (bitwise)ORA #$80 → forces bit 7 to 1 (inverse video)
EORExclusive ORA ← A XOR operand (bitwise)EOR #$FF → inverts all bits
BITBit testZ ← A AND mem; N,V ← bits 7,6 of memBIT $D018 → tests a register without modifying A

Shifts and Rotations

MnemonicFull nameDescriptionExample
ASLArithmetic Shift LeftShifts left (× 2); bit 7 → CASL A → A × 2
LSRLogical Shift RightShifts right (÷ 2); bit 0 → CLSR A → A ÷ 2
ROLRotate LeftLeft rotation through CROL A → × 2 + old C (16-bit multiply)
RORRotate RightRight rotation through CROR A → ÷ 2 + old C in bit 7

Comparisons

MnemonicFull nameDescriptionExample
CMPCompare AA − operand → N,Z,C (A unchanged)CMP #$9B / BEQ EOL → ATASCII end of line?
CPXCompare XX − operand → N,Z,CCPX #$10 / BNE LOOP → end at X=16
CPYCompare YY − operand → N,Z,CCPY #$00 / BEQ DONE → end at Y=0

Conditional Branches

All branches are relative (signed offset of −128 to +127 bytes from the next instruction).

MnemonicFull nameConditionExample
BCCBranch if Carry ClearC = 0ADC #$01 / BCC OK → no carry
BCSBranch if Carry SetC = 1CMP #$80 / BCS HIGH → A ≥ $80
BEQBranch if EqualZ = 1 (zero result)CMP #$41 / BEQ FOUND → found 'A'
BNEBranch if Not EqualZ = 0 (non-zero result)DEX / BNE LOOP → classic loop
BMIBranch if MinusN = 1 (bit 7 = 1)BIT $D40B / BMI VBLANK → VBlank active
BPLBranch if PlusN = 0 (bit 7 = 0)LDA $CB / BPL OK → positive value
BVCBranch if oVerflow ClearV = 0ADC #$01 / BVC OK → no signed overflow
BVSBranch if oVerflow SetV = 1ADC #$40 / BVS ERR → signed overflow detected

Jumps and Calls

MnemonicFull nameDescriptionExample
JMPJumpUnconditional jump (absolute or indirect)JMP $4000 → restarts ATAMON
JSRJump to SubroutinePushes return address, then jumpsJSR $E456 → calls CIO
RTSReturn from SubroutinePulls return address + 1 and jumps thereRTS → end of subroutine
RTIReturn from InterruptPulls P then program counterRTI → end of VBI or DLI handler

Software Interrupt

MnemonicFull nameDescriptionExample
BRKBreakTriggers a software interrupt; pushes PC+2 and P, jumps via OS vector ($0206/$0207). This is ATAMON's central breakpoint mechanism.BRK → immediate return to ATAMON

Flag Operations

MnemonicFull nameDescriptionExample
CLCClear CarryC ← 0CLC / ADC #$01 → addition without stale carry
SECSet CarryC ← 1SEC / SBC #$01 → correct subtraction
CLDClear DecimalD ← 0 (disable BCD)CLD at start of arithmetic routine
SEDSet DecimalD ← 1 (enable BCD)SED → rarely useful on Atari
CLIClear Interrupt disableI ← 0 (allow IRQ)CLI → enables VBI, POKEY, etc.
SEISet Interrupt disableI ← 1 (mask IRQ)SEI → protects a critical section
CLVClear oVerflowV ← 0CLV → start from known state before signed arithmetic

Miscellaneous

MnemonicFull nameDescriptionExample
NOPNo OperationDoes nothing; advances program counter by 1 byte. Used to neutralise an instruction or fill space in memory.NOP / NOP / NOP to replace a 3-byte JSR $xxxx

Annex C — Illegal / Undocumented 6502 Opcodes

These opcodes do not exist in the official MOS Technology 6502 specification. They result from unintended combinations of the processor's internal logic. Their behaviour is reproduced by the ATAMON disassembler (command D), which displays them preceded by a *.

Warning: most are unstable depending on the processor revision or supply voltage. Avoid in production code; useful for analysing old programs that exploit them.

Fatal Instructions

MnemonicAlt. namesDescriptionOpcodes
*KILJAM, HLTPermanently locks the CPU — the data bus freezes, only a hardware RESET can restart the machine.$02 $12 $22 $32 $42 $52 $62 $72 $92 $B2 $D2 $F2

Combined Read-Modify-Write Instructions

These opcodes perform two operations in one: a modification in memory (or on the accumulator) then a logical/arithmetic operation with A.

MnemonicAlt. namesDescriptionModesOpcodes
*SLOASOASL on memory byte, then ORA with Ainx zp abs iny zpx aby abx$03 $07 $0F $13 $17 $1B $1F
*RLAROL on memory byte, then AND with Ainx zp abs iny zpx aby abx$23 $27 $2F $33 $37 $3B $3F
*SRELSELSR on memory byte, then EOR with Ainx zp abs iny zpx aby abx$43 $47 $4F $53 $57 $5B $5F
*RRAROR on memory byte, then ADC with Ainx zp abs iny zpx aby abx$63 $67 $6F $73 $77 $7B $7F
*DCPDCMDEC on memory byte, then CMP with Ainx zp abs iny zpx aby abx$C3 $C7 $CF $D3 $D7 $DB $DF
*ISCISB, INSINC on memory byte, then SBC with Ainx zp abs iny zpx aby abx$E3 $E7 $EF $F3 $F7 $FB $FF

Combined Load / Store

MnemonicAlt. namesDescriptionModesOpcodes
*LAXLoads the same value simultaneously into A and X — equivalent to LDA + TAX in a single opcodeinx zp abs iny zpy aby$A3 $A7 $AF $B3 $B7 $BF
*SAXAXSWrites A AND X to memory (without modifying flags)inx zp abs zpy$83 $87 $8F $97

Accumulator Instructions

MnemonicAlt. namesDescriptionOpcodeStability
*ANCImmediate AND on A, then copies bit 7 into Carry flag (like ASL without modifying memory)$0B $2Bstable
*ALRASRImmediate AND on A, then LSR on A (logical right shift)$4Bstable
*ARRImmediate AND on A, then ROR on A — with complex effects on C and V (not identical to normal ROR)$6Bstable
*SBXAXS(A AND X) - imm → X, no borrow (affects C, Z, N)$CBstable
*SBCDuplicate of official SBC #imm ($E9) — identical behaviour$EBstable
*ANEXAAA = (A OR $EE) AND X AND imm — result depends on processor$8Bunstable
*LXALAX #(A OR $EE) AND imm → A and X — result depends on processor$ABunstable

Unstable Memory Instructions (depend on addr_hi)

MnemonicAlt. namesDescriptionOpcodeStability
*TASXAS, SHSSP = A AND X; writes SP AND (addr_hi+1) to memory$9Bunstable
*SHYA11YWrites Y AND (addr_hi+1) to memory$9Cunstable
*SHXA11XWrites X AND (addr_hi+1) to memory$9Eunstable
*SHAAXAWrites A AND X AND (addr_hi+1) to memory$93 $9Funstable
*LASLARA = X = SP = memory AND SP$BBunstable

Illegal NOPs (multi-byte, no effect)

These opcodes behave like NOP (do nothing visible) but consume 2 or 3 bytes — the CPU reads and ignores them.

MnemonicModeSizeOpcodes
*NOPimp (1 byte)1$1A $3A $5A $7A $DA $FA
*NOPzp (2 bytes)2$04 $44 $64
*NOPzpx (2 bytes)2$14 $34 $54 $74 $D4 $F4
*NOPabs (3 bytes)3$0C
*NOPabx (3 bytes)3$1C $3C $5C $7C $DC $FC

Annex D — Known Atari Labels

Symbolic addresses recognised by the ATAMON disassembler (command D). They appear in the disassembly in place of the raw hexadecimal address.

Zero Page — OS Variables ($00$FF)

AddressLabelDescription
$000A$000BDOSVECDOS vector — jump address to DOS (2 bytes lo/hi). JMP (DOSVEC) exits the program and returns to DOS.
$000C$000DDOSINIDOS initialisation vector — called on each warm reset.
$0010POKMSKPOKEY interrupt enable mask (IRQ). Each bit enables a type of IRQ (timer, serial...).
$0012$0014RTCLOKReal-time clock (3 bytes). Incremented 60×/s by the OS VBI.
$0020$002BZIOCBZero IOCB — working copy of the current channel's IOCB during a CIO call.
$002CSTATUSStatus code of the last SIO or CIO operation.
$002E$002FBUFRLO/HII/O buffer address (2 bytes). Used by SIO DCB and CIO.
$0030$0031BFENLO/HIEnd of buffer address (2 bytes).
$0036$0037BYTLO/HINumber of bytes transferred in the last SIO/CIO call (2 bytes).

Page 2 — Interrupt Vectors ($0200$02FF)

AddressLabelDescription
$0200$0201VDSLSTDLI vector (Display List Interrupt) — called on each DLI instruction in the ANTIC display list.
$0202$0203VPRCED"Proceed" interrupt vector (serial input).
$0204$0205VINTER"Interrupt" interrupt vector (serial input).
$0206$0207VBREAKBRK vector — called when the CPU executes BRK. ATAMON installs it to capture breakpoints.
$0208$0209VKEYBDKeyboard interrupt vector (key pressed).
$020A$020BVSERINSerial receive vector (POKEY).
$020C$020DVSERORSerial transmit vector (POKEY).
$020E$020FVSEROCSerial transmit complete vector.
$0210$0211VTIMR1POKEY timer 1 vector.
$0212$0213VTIMR2POKEY timer 2 vector.
$0214$0215VTIMR4POKEY timer 4 vector.
$0216$0217VIMIRQGeneric IRQ vector (not used by OS).
$0222$0223VVBLKIImmediate VBI vector — called at start of vertical blanking (~3,800 cycles available).
$0224$0225VVBLKDDeferred VBI vector — called after SYSVBV (~20,000 cycles available, safer).
$0226$0227CDTMA1Software timer 1 vector (decremented by VBI).
$0228$0229CDTMA2Software timer 2 vector.
$022ACDTMF3Software timer 3 flag.
$022CCDTMF4Software timer 4 flag.
$022EBRKKEYBREAK key flag: $00 if BREAK pressed, otherwise $80.
$022FSDMCTLShadow DMACTL — copied to $D400 (ANTIC) at each VBI. Controls screen width and DMA enable.
$0230$0231SDLSTL/HShadow display list (lo/hi) — copied to $D402/$D403 at VBI. Points to the active ANTIC display list.
$023BATRACTScreen saver counter. Incremented by OS each second of keyboard inactivity. Above 127, colours fade. Reset to 0 disables saver.
$026FGPRIORShadow PRIOR — copied to $D01B (GTIA) at VBI. Determines player/missile/background display priority.
$02C0$02C8COLPF0...COLPM0Playfield and player colours (shadows of GTIA colour registers).
$02C8COLBKBackground colour. Shadow of GTIA register $D01A.
$02E0$02E1RUNADRun address of loaded program (XEX). OS jumps to this address after loading.
$02E2$02E3INITADInitialisation address — called after each XEX segment marked INITAD.
$02E4RAMSIZRAM size in pages (×256). Value read at startup.
$02E5RAMTOPTop of usable RAM (in pages).
$02E7$02E8MEMLOBottom of free RAM (2 bytes). ATAMON sets it to $5300 to protect its code.
$02E9$02EAMEMTOPTop of free RAM (2 bytes).
$02F4CHBASShadow CHBASE — copied to $D408 (ANTIC) at VBI. Selects the character font (high page of address).

Page 3 — SIO DCB ($0300$035F)

AddressLabelDescription
$0300DDEVICSIO device number (e.g. $31 = disk drive D1:).
$0301DUNITUnit number (1 to 4 for D1: to D4:).
$0302DCOMNDSIO command (e.g. $52 = Read sector, $57 = Write sector).
$0303DSTATSDirection: $40 = read, $80 = write, $00 = status only.
$0304$0305DBUFLO/HIData buffer address (2 bytes lo/hi).
$0306DTIMLOSIO timeout in VBI units (~1/60 s). Typical value: $0F (15 VBIs).
$0308$0309DBYTLO/HINumber of bytes to transfer (2 bytes).
$030A$030BDAUX1/2Auxiliary parameters (e.g. sector number lo/hi for disk).
$0340$034FIOCB0IOCB channel 0 (screen editor E: — used by ATAMON for input).
$0350$035FIOCB1IOCB channel 1 (free).

Hardware Registers — GTIA ($D000)

AddressLabelDescription
$D000HPOSP0Horizontal position of player 0 (missile).
$D001HPOSP1Horizontal position of player 1.
$D002HPOSP2Horizontal position of player 2.
$D003HPOSP3Horizontal position of player 3.
$D004HPOSM0Horizontal position of missile 0.
$D01BPRIORPlayer/missile/playfield display priority. Bit 7 = special GTIA mode.
$D01DGRACTLPlayer/missile DMA enable.
$D01FCONSOLConsole keys (read): bits 2-0 = START/SELECT/OPTION (0 = pressed). Write: controls internal speaker.

Hardware Registers — POKEY ($D200)

AddressLabelDescription
$D200AUDF1Audio channel 1 frequency (0–255, frequency inversely proportional).
$D201AUDC1Channel 1 control: bits 7-4 = volume, bits 3-0 = distortion/waveform.
$D202AUDF2Audio channel 2 frequency.
$D203AUDC2Channel 2 control.
$D204AUDF3Audio channel 3 frequency.
$D205AUDC3Channel 3 control.
$D206AUDF4Audio channel 4 frequency.
$D207AUDC4Channel 4 control.
$D208AUDCTLGlobal audio control: clock frequency (64 kHz / 15 kHz / 1.79 MHz), 16-bit channels, high-pass filter.
$D20ASTIMERWrite: resets POKEY timers to zero.
$D20ESEROUTSerial output (write).
$D20FSKCTLPOKEY serial control: reset, sync mode.

Hardware Registers — PIA ($D300)

AddressLabelDescription
$D300PORTAPort A: joystick 1 (bits 3-0) and joystick 2 (bits 7-4). Bit = 0 if direction active.
$D301PORTBPort B: on 800XL, controls OS ROM bank (bit 0) and BASIC (bit 1).
$D302PACTLPort A control (direction bits, interrupts).
$D303PBCTLPort B control.

Hardware Registers — ANTIC ($D400)

AddressLabelDescription
$D400DMACTLDMA control: enable/disable DMA, screen width (narrow/normal/wide) and player/missile DMA.
$D401CHACTLCharacter control: inverse video, high/low line blanking of a character cell.
$D402$D403DLISTL/HDisplay list address (lo/hi). ANTIC reads this list to build the image.
$D404HSCROLFine horizontal scroll (0–15 colour clocks).
$D405VSCROLFine vertical scroll (0–15 lines).
$D407PMBASEPlayer/missile base (high address byte, 1 KB or 2 KB page depending on DMACTL).
$D408CHBASECharacter font base (high byte; font starts at this page × 256).
$D409WSYNCWait for sync — write: CPU stalls until end of current scan line (raster synchronisation).
$D40BVCOUNTCurrent scan line counter (read only, 0–131).
$D40ENMIENNMI enable: bit 7 = DLI, bit 6 = VBI.
$D40FNMIRESWrite: resets NMI flags. Read: identifies NMI source (bit 7 = DLI, bit 6 = VBI).

OS ROM Entry Points ($E4xx)

AddressLabelDescription
$E450DISKIVDisk handler initialisation.
$E453DSKINVSIO disk call — executes the command described in the DCB ($0300+). Used by ATAMON for E and W commands.
$E456CIOVCIO (Central I/O) — main I/O entry point. Used by ATAMON to read commands and write results.
$E459SIOVSIO (Serial I/O) — low-level serial transfer.
$E45CSETVBVInstalls a VBI handler (immediate or deferred).
$E45FSYSVBVSystem VBI handler (OS) — call from an immediate VBI to chain the deferred VBI.
$E462XITVBVVBI exit (end of a VBI handler).
$E465SIOINVSIO initialisation.
$E474WARMSVWarm restart — resets OS without clearing RAM.
$E477COLDSVCold start — full reset (clears RAM).

Math ROM ($D8xx)

AddressLabelDescription
$D800AFPASCII → floating-point (string pointed by $F3/$F4 → FR0).
$D8E6FASCFloating-point → ASCII (FR0 → string pointed by $F3/$F4).
$D9AAIFP16-bit integer → floating-point (value in $D4/$D5 → FR0).
$D9D2FPIFloating-point → 16-bit integer (FR0 → $D4/$D5).
$DA44ZFR0Clears FR0 to zero.
$DA60FSUBFloating-point subtract: FR0 ← FR0 − FR1.
$DA66FADDFloating-point add: FR0 ← FR0 + FR1.
$DADBFMULFloating-point multiply: FR0 ← FR0 × FR1.
$DB28FDIVFloating-point divide: FR0 ← FR0 / FR1.
$DD40PLYEVLPolynomial evaluation (for sin, cos, exp...).
$DD89FLD0RLoad FR0 from address in (X,Y).
$DD98FLD1RLoad FR1 from address in (X,Y).
$DDA7FSTORStore FR0 to address in (X,Y).
$DDB6FMOVEFR1 ← FR0.

Annex E — Atari 800XL Hardware Registers — Quick Reference

Summary of hardware registers for the four custom chips of the Atari 800XL, with their typical usage in programming.

GTIA — Graphic Television Interface Adaptor ($D000$D01F)

AddressNameR/WUsage
$D000$D003HPOSPnWHorizontal position of 4 players (sprites)
$D004$D007HPOSMnWHorizontal position of 4 missiles
$D008$D00BSIZEPnWPlayer size (1×, 2×, 4×)
$D00CSIZEMWMissile size
$D00D$D010GRAFPnWPlayer n shape (8 bits = 8 pixels)
$D011GRAFMW4 missile shapes (2 bits each)
$D012$D015COLPMnWPlayer n colour
$D016$D019COLPFnWPlayfield 0–3 colours
$D01ACOLBKWBackground colour
$D01BPRIORWPlayer/playfield priority, GTIA mode
$D01CVDELAYWVertical delay missile/player (fine resolution)
$D01DGRACTLWPlayer/missile DMA enable
$D01EHITCLRWWrite: clears collision registers
$D01FCONSOLR/WRead: START/SELECT/OPTION keys. Write: speaker

POKEY — Potentiometer and Keyboard ($D200$D20F)

AddressNameR/WUsage
$D200$D207AUDFn / AUDCnWFrequency and control of 4 audio channels (AUDF/AUDC pairs)
$D208AUDCTLWGlobal audio control (clock, 16-bit channels, filters)
$D209STIMERWResets timers
$D20ASKRESWResets serial status register
$D20BPOTGOWStarts potentiometer (paddle) conversion
$D200$D207POTnR8 potentiometer (paddle) values
$D208ALLPOTRStatus of ongoing potentiometer conversions
$D209KBCODERCode of pressed key (internal ATASCII)
$D20ARANDOMRRandom number generator (read only)
$D20DSERINRReceived serial data
$D20ESEROUTWSerial data to transmit
$D20FSKCTLWSerial control (init, sync, two-tone mode)

PIA — Peripheral Interface Adaptor ($D300$D303)

AddressNameR/WUsage
$D300PORTAR/WJoystick 1 (bits 3-0) and joystick 2 (bits 7-4)
$D301PORTBR/WOn 800XL: OS ROM bank (bit 0) and BASIC (bit 1)
$D302PACTLWPort A direction bits and interrupt control
$D303PBCTLWPort B direction bits and interrupt control

ANTIC — Alphanumeric Television Interface Circuit ($D400$D40F)

AddressNameR/WUsage
$D400DMACTLWScreen width (40/48 colours), ANTIC/player DMA active
$D401CHACTLWInverse video, character blanking
$D402$D403DLISTL/HWDisplay list address (lo/hi)
$D404HSCROLWFine horizontal scroll (0–15 colour clocks)
$D405VSCROLWFine vertical scroll (0–15 lines)
$D407PMBASEWPlayer/missile base page
$D408CHBASEWCharacter font base page
$D409WSYNCWWait for end of raster line (CPU/video synchronisation)
$D40AVSCROLW(read VCOUNT at address $D40B)
$D40BVCOUNTRCurrent raster line counter
$D40C$D40DPENH/VRLight pen horizontal/vertical position
$D40ENMIENWNMI enable: bit 7 = DLI, bit 6 = VBI
$D40FNMIRESR/WRead: NMI source. Write: reset NMI flags

Fixing the C Command Bug (Patch)

The Bug

The C command in ATAMON V1.3 has been broken since its publication in 1983. Regardless of the syntax used, it always returns ? and writes nothing to memory.

Cause: In ATAMON's code, every command that takes multiple arguments must, between each argument, call routine $43AE (SEPARATOR_CHECK). This routine reads and consumes the comma , that separates arguments in the command line.

The C command handler (at address $4BE9) correctly reads the destination address via JSR $4256, but then calls directly JSR $4280 (READ_BIN, which reads 8 bits) without going through $43AE. Result: READ_BIN reads the comma , (code $2C) instead of the first binary digit 0 or 1. The comma is written to memory ($2C = 44), then the program encounters an error and displays ?.

All other multi-argument commands (F, T, V, L, H, I...) correctly call $43AE between their arguments. C is the only one that omits it.

The Fix

The fix consists of adding the missing call to $43AE without modifying the existing code. Since ATAMON fully occupies the $4000–$52B1 range, a small intermediate program ("trampoline") is installed in the first free byte after the code: $52B2.

Step 1 — Redirect the READ_BIN call

At address $4BF6, replace:

with:

Step 2 — Install the trampoline at $52B2

The inner loop of the C handler (at $4C05) itself calls $43AE to consume the comma between subsequent bytes. The trampoline must therefore only consume the comma on the first call (when counter $E8 is 0), and call READ_BIN directly for subsequent calls.

Without this test on $E8, the trampoline would try to consume a comma already read by $4C05, would read the first binary bit instead, and would return ? from the second byte onwards.

Applying the Patch

The file Atamon - D7 - DXG 5724-Patched Atarinside.atr provided with this document contains ATAMON with this fix already applied. Load it in your emulator or copy it to an Atari disk in place of the original.

To regenerate the patch manually from the original XEX, the Python script patch_atamon.py automates both steps above.


References

6502 Processor

Atari 800XL Documentation

AtariWiki

.PRO Disk Format

Emulator Used for Testing