Codage des instructions (Format binaire)
Chaque instruction du 6502 est codée en binaire sous la forme d'une séquence de 1, 2 ou 3 octets. Le format général est :
Le premier octet est toujours l'opcode, identifiant à la fois l'instruction et le mode d'adressage. Les octets suivants sont l'opérande (adresse ou valeur immédiate), si nécessaire.
Propriétés fondamentales du codage 6502 :
- Pas de préfixes : l'opcode est toujours le premier octet
- Pas d'alignement : les instructions peuvent commencer à n'importe quelle adresse
- Format little-endian : l'octet de poids faible d'une adresse 16 bits est stocke en premier
- La taille d'une instruction est entièrement déterminée par l'opcode (1, 2 ou 3 octets)
- Sur les 256 opcodes possibles ($00-$FF), seuls 151 sont officiellement définis
Taille des instructions selon le mode d'adressage :
| Taille | Description |
|---|---|
| 1 octet | Modes implicite et accumulateur (pas d'opérande) |
| 2 octets | Modes immédiat, page zéro, page zéro indexe (X ou Y), relatif, indirect indexe (opérande 8 bits) |
| 3 octets | Modes absolu, absolu indexe (X ou Y), indirect (opérande 16 bits en little-endian) |
Exemples de codage en mémoire :
Structure de l'octet opcode
L'opcode du 6502 encode simultanément l'instruction et le mode d'adressage. Contrairement au 68000 ou au 80386, il n'y a pas de champs réguliers dans l'opcode : le décodage est effectue par une table de correspondance (lookup table) dans le microprocesseur.
Cependant, il existe un schéma partiel dans la structure des opcodes. En décomposant un opcode en trois champs de bits :
Ou :
| Champ | Description |
|---|---|
| cc (bits 1-0) | Identifie le "groupe" d'instructions |
| bbb (bits 4-2) | Identifie le mode d'adressage |
| aaa (bits 7-5) | Identifie l'instruction dans le groupe |
Groupe cc = 01 (instructions ALU principales) :
Les instructions du groupe 01 sont les plus régulières. Le champ bbb détermine le mode d'adressage :
| bbb | Mode d'adressage | |
|---|---|---|
| 000 | (Indirect,X) | ($nn,X) |
| 001 | Page zero | $nn |
| 010 | Immediat | #$nn |
| 011 | Absolu | $nnnn |
| 100 | (Indirect),Y | ($nn),Y |
| 101 | Page zero,X | $nn,X |
| 110 | Absolu,Y | $nnnn,Y |
| 111 | Absolu,X | $nnnn,X |
Le champ aaa déterminé l'instruction :
| aaa | Instruction |
|---|---|
| 000 | ORA |
| 001 | AND |
| 010 | EOR |
| 011 | ADC |
| 100 | STA |
| 101 | LDA |
| 110 | CMP |
| 111 | SBC |
Exemples de decodage (groupe 01) :
|
$A9 = 101 010 01 → aaa=101 (LDA), bbb=010 (immediat), cc=01 $6D = 011 011 01 → aaa=011 (ADC), bbb=011 (absolu), cc=01 $D1 = 110 100 01 → aaa=110 (CMP), bbb=100 ((Ind),Y), cc=01 $85 = 100 001 01 → aaa=100 (STA), bbb=001 (page zero), cc=01 |
Exception : STA (aaa=100) n'a pas de mode immédiat (bbb=010). L'opcode $89 n'est pas défini officiellement.
Groupe cc = 10 (décalages, chargements X/Y) :
| bbb | Mode d'adressage | |
|---|---|---|
| 000 | Immediat | #$nn |
| 001 | Page zéro | $nn |
| 010 | Accumulateur | A (ou implicite) |
| 011 | Absolu | $nnnn |
| 101 | Page zero,X | $nn,X (ou Page zero,Y pour LDX/STX) |
| 111 | Absolu,X | $nnnn,X (ou Absolu,Y pour LDX) |
| aaa | Instruction |
|---|---|
| 000 | ASL |
| 001 | ROL |
| 010 | LSR |
| 011 | ROR |
| 100 | STX (modes limites) |
| 101 | LDX |
| 110 | DEC |
| 111 | INC |
Note : ce groupe est moins régulier que le groupe 01. Certaines combinaisons aaa/bbb ne sont pas définies. STX et LDX utilisent l'indexation par Y au lieu de X (Page zero,Y et Absolu,Y).
Groupe cc = 00 (branchements, comparaisons, divers) :
Ce groupe est le moins régulier. Il contient les instructions de branchement, BIT, CPX, CPY, STY, LDY et les instructions de contrôle.
| bbb | Mode d'adressage (quand applicable) | |
|---|---|---|
| 000 | Immediat | #$nn |
| 001 | Page zero | $nn |
| 011 | Absolu | $nnnn |
| 101 | Page zero,X | $nn,X |
| 111 | Absolu,X | $nnnn,X |
Les branchements conditionnels suivent un schéma séparé :
$10, $30, $50, $70, $90, $B0, $D0, $F0
soit aaa = 000 a 111, bbb = 100, cc = 00
| aaa | Branchement | Condition |
|---|---|---|
| 000 | BPL | N = 0 |
| 001 | BMI | N = 1 |
| 010 | BVC | V = 0 |
| 011 | BVS | V = 1 |
| 100 | BCC | C = 0 |
| 101 | BCS | C = 1 |
| 110 | BNE | Z = 0 |
| 111 | BEQ | Z = 1 |
Table complète des opcodes
La table suivante liste les 256 opcodes possibles du 6502. Les opcodes non documentes sont marques "---".
Format : les colonnes représentent le quartet de poids faible (bits 3-0), les lignes représentent le quartet de poids fort (bits 7-4).
| $0x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
|---|---|---|---|---|---|---|---|---|
| BRK | ORA | --- | --- | --- | ORA | ASL | --- | |
| impl | (ind,X) | zp | zp | zp | ||||
| $0x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| PHP | ORA | ASL | --- | --- | ORA | ASL | --- | |
| impl | #imm | A | abs | abs | ||||
| $1x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BPL | ORA | --- | --- | --- | ORA | ASL | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $1x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| CLC | ORA | --- | --- | --- | ORA | ASL | --- | |
| impl | abs,Y | abs,X | abs,X | |||||
| $2x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| JSR | AND | --- | --- | BIT | AND | ROL | --- | |
| abs | (ind,X) | zp | zp | zp | ||||
| $2x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| PLP | AND | ROL | --- | BIT | AND | ROL | --- | |
| impl | #imm | A | abs | abs | abs | |||
| $3x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BMI | AND | --- | --- | --- | AND | ROL | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $3x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| SEC | AND | --- | --- | --- | AND | ROL | --- | |
| impl | abs,Y | abs,X | abs,X | |||||
| $4x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| RTI | EOR | --- | --- | --- | EOR | LSR | --- | |
| impl | (ind,X) | zp | zp | zp | ||||
| $4x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| PHA | EOR | LSR | --- | JMP | EOR | LSR | --- | |
| impl | #imm | A | abs | abs | abs | |||
| $5x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BVC | EOR | --- | --- | --- | EOR | LSR | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $5x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| CLI | EOR | --- | --- | --- | EOR | LSR | --- | |
| impl | abs,Y | abs,X | abs,X | |||||
| $6x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| RTS | ADC | --- | --- | --- | ADC | ROR | --- | |
| impl | (ind,X) | zp | zp | zp | ||||
| $6x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| PLA | ADC | ROR | --- | JMP | ADC | ROR | --- | |
| impl | #imm | A | (ind) | abs | abs | |||
| $7x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BVS | ADC | --- | --- | --- | ADC | ROR | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $7x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| SEI | ADC | --- | --- | --- | ADC | ROR | --- | |
| impl | abs,Y | abs,X | abs,X | |||||
| $8x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| --- | STA | --- | --- | STY | STA | STX | --- | |
| (ind,X) | zp | zp | zp | |||||
| $8x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| DEY | --- | TXA | --- | STY | STA | STX | --- | |
| impl | impl | abs | abs | abs | ||||
| $9x | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BCC | STA | --- | --- | STY | STA | STX | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,Y | ||||
| $9x | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| TYA | STA | TXS | --- | --- | STA | --- | --- | |
| impl | abs,Y | impl | abs,X | |||||
| $Ax | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| LDY | LDA | LDX | --- | LDY | LDA | LDX | --- | |
| #imm | (ind,X) | #imm | zp | zp | zp | |||
| $Ax | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| TAY | LDA | TAX | --- | LDY | LDA | LDX | --- | |
| impl | #imm | impl | abs | abs | abs | |||
| $Bx | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BCS | LDA | --- | --- | LDY | LDA | LDX | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,Y | ||||
| $Bx | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| CLV | LDA | TSX | --- | LDY | LDA | LDX | --- | |
| impl | abs,Y | impl | abs,X | abs,X | abs,Y | |||
| $Cx | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| CPY | CMP | --- | --- | CPY | CMP | DEC | --- | |
| #imm | (ind,X) | zp | zp | zp | ||||
| $Cx | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| INY | CMP | DEX | --- | CPY | CMP | DEC | --- | |
| impl | #imm | impl | abs | abs | abs | |||
| $Dx | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BNE | CMP | --- | --- | --- | CMP | DEC | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $Dx | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| CLD | CMP | --- | --- | --- | CMP | DEC | --- | |
| impl | abs,Y | abs,X | abs,X | |||||
| $Ex | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| CPX | SBC | --- | --- | CPX | SBC | INC | --- | |
| #imm | (ind,X) | zp | zp | zp | ||||
| $Ex | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| INX | SBC | NOP | --- | CPX | SBC | INC | --- | |
| impl | #imm | impl | abs | abs | abs | |||
| $Fx | $x0 | $x1 | $x2 | $x3 | $x4 | $x5 | $x6 | $x7 |
| BEQ | SBC | --- | --- | --- | SBC | INC | --- | |
| rel | (ind),Y | zp,X | zp,X | zp,X | ||||
| $Fx | $x8 | $x9 | $xA | $xB | $xC | $xD | $xE | $xF |
| SED | SBC | --- | --- | --- | SBC | INC | --- | |
| impl | abs,Y | abs,X | abs,X |
Résumé statistique des opcodes :
- Opcodes définis : 151
- Opcodes indéfinis : 105
- Les colonnes $x3, $x7, $xB et $xF sont entièrement indéfinies (sauf quelques opcodes non documentés)
- La colonne $x2 est presque entièrement indéfinie (sauf $A2 = LDX #imm)
Calcul de la taille des instructions
La taille d'une instruction du 6502 est entièrement déterminée par son opcode. Contrairement au 80386, il n'y a pas de préfixes ni de champs modulaires a décoder.
Tableau de la taille par opcode
La taille de chaque instruction peut être entreposée dans une table de 256 octets indexée par l'opcode :
Taille 1 (1 octet : opcode seul) :
Toutes les instructions implicites et accumulateur :
$00 BRK* $08 PHP $0A ASL A $18 CLC $28 PLP$2A ROL A $38 SEC $40 RTI $48 PHA $4A LSR A
$58 CLI $60 RTS $68 PLA $6A ROR A $78 SEI
$88 DEY $8A TXA $98 TYA $9A TXS $A8 TAY
$AA TAX $B8 CLV $BA TSX $C8 INY $CA DEX
$D8 CLD $E8 INX $EA NOP $F8 SED
* BRK est officiellement 1 octet mais consomme 2 octets (l'octet suivant est saute)
Taille 2 (2 octets : opcode + operande 8 bits) :
Toutes les instructions avec mode immediat (#$nn), page zero ($nn), page zero indexe ($nn,X ou $nn,Y), indirect indexe (($nn,X) et ($nn),Y), et relatif :
$01 $05 $06 $09 $10 $11 $15 $16 $19 $21 $24 $25 $26$29 $30 $31 $35 $36 $39 $41 $45 $46 $49 $50 $51 $55
$56 $59 $61 $65 $66 $69 $70 $71 $75 $76 $79 $81 $84
$85 $86 $90 $91 $94 $95 $96 $A0 $A1 $A2 $A4 $A5 $A6
$A9 $B0 $B1 $B4 $B5 $B6 $B9 $C0 $C1 $C4 $C5 $C6 $C9
$D0 $D1 $D5 $D6 $D9 $E0 $E1 $E4 $E5 $E6 $E9 $F0 $F1
$F5 $F6 $F9
Taille 3 (3 octets : opcode + adresse 16 bits little-endian) :
Toutes les instructions avec mode absolu ($nnnn), absolu indexe ($nnnn,X et $nnnn,Y), et indirect (($nnnn)) :
$0D $0E $19 $1D $1E $20 $2C $2D $2E $39 $3D $3E $4C$4D $4E $59 $5D $5E $6C $6D $6E $79 $7D $7E $8C $8D
$8E $99 $9D $AC $AD $AE $B9 $BC $BD $BE $CC $CD $CE
$D9 $DD $DE $EC $ED $EE $F9 $FD $FE
Détermination rapide de la taille
Pour un désassembleur ou un décodeur, la taille peut être déterminée par une simple table de correspondance :
| taille[opcode] := table_256_octets[opcode] |
Si on ne dispose pas d'une table, on peut utiliser le schéma des groupes décrit plus haut :
Pour le groupe cc=01 :
- bbb=000,001,100,101 : taille 2 (opérande 8 bits)
- bbb=010 : taille 2 (immédiat 8 bits)
- bbb=011,110,111 : taille 3 (opérande 16 bits)
Pour les branchements (bbb=100, cc=00) :
- Toujours taille 2 (opcode + déplacement 8 bits)
Pour les instructions implicites/accumulateur :
- Toujours taille 1
Format de l'opérande 16 bits (little-endian)
Lorsqu'une instruction contient une adresse 16 bits, elle est entreposée en format little-endian (poids faible d'abord) :
| Position | Description |
|---|---|
| Octet 1 | opcode |
| Octet 2 | poids faible de l'adresse (bits 7-0) |
| Octet 3 | poids fort de l'adresse (bits 15-8) |
Exemples :
LDA $1234 s'encode : $AD $34 $12
- $AD = opcode LDA absolu
- $34 = poids faible de $1234
- $12 = poids fort de $1234
JMP $C000 s'encode : $4C $00 $C0
- $4C = opcode JMP absolu
- $00 = poids faible de $C000
- $C0 = poids fort de $C000
JSR $FFD2 s'encode : $20 $D2 $FF
- $20 = opcode JSR absolu
- $D2 = poids faible de $FFD2
- $FF = poids fort de $FFD2
Format du déplacement relatif
Pour les instructions de branchement, l'opérande est un déplacement relatif signe sur 8 bits (-128 a +127).
Le déplacement est calcule a partir de l'adresse de l'instruction SUIVANT le branchement (PC + 2, car le branchement fait 2 octets).
Formule :
| Adresse cible = (adresse du branchement) + 2 + déplacement |
Exemples :
A l'adresse $1000 :
BEQ $1010 s'encode : $F0 $0E
| déplacement = $1010 - ($1000 + 2) = $0E (+14) |
A l'adresse $1000 :
BNE $0FF0 s'encode : $D0 $EE
| déplacement = $0FF0 - ($1000 + 2) = -$12 = $EE (-18) |
A l'adresse $1000 :
BEQ $1002 s'encode : $F0 $00
| déplacement = 0 (sauter a l'instruction suivante) |
A l'adresse $1000 :
BNE $1000 s'encode : $D0 $FE
| déplacement = $1000 - ($1000 + 2) = -2 = $FE |
(boucle infinie si la condition est vraie)
Portée maximale :
En arrière : -128 octets → adresse - 126
En avant : +127 octets → adresse + 129
Opcodes non documentes (illegal opcodes)
Sur les 256 opcodes possibles ($00-$FF), 105 ne sont pas officiellement définis par MOS Technology. Cependant, sur le 6502 NMOS, ces opcodes produisent des effets déterminés (bien que non garantis) qui résultent de la logique interne du décodeur d'instructions.
Le 65C02 (CMOS) transforme la plupart de ces opcodes en NOP de différentes tailles.
Pourquoi existent-ils ?
Le décodeur d'instructions du 6502 utilise un PLA (Programmable Logic Array) qui décode l'opcode en signaux de contrôle. Les opcodes non définis activent des combinaisons imprévues de ces signaux, ce qui produit des comportements résultant de la superposition de plusieurs opérations.
Par exemple :
- LAX = LDA + LDX (charge A et X en même temps)
- SAX = STA AND STX (stocke A AND X)
- DCP = DEC + CMP (décrémente et compare)
Opcodes non documentes les plus utilises
Les opcodes suivants sont les plus fiables et les plus fréquemment utilisés par les programmeurs :
LAX (Load A and X) - opcodes : $A3, $A7, $AF, $B3, $B7, $BF
Charge simultanément A et X avec la même valeur.
Equivalent a : LDA val / TAX (mais en un seul opcode)
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $A3 | 2 | 6 |
| Page zéro | $A7 | 2 | 3 |
| Absolu | $AF | 3 | 4 |
| (Indirect),Y | $B3 | 2 | 5(+1) |
| Page zero,Y | $B7 | 2 | 4 |
| Absolu,Y | $BF | 3 | 4(+1) |
SAX (Store A AND X) - opcodes : $83, $87, $8F, $97
Entrepose le résultat de A AND X en mémoire, sans modifier ni A ni X ni les drapeaux.
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $83 | 2 | 6 |
| Page zero | $87 | 2 | 3 |
| Absolu | $8F | 3 | 4 |
| Page zero,Y | $97 | 2 | 4 |
DCP (Decrement and Compare) - opcodes : $C3, $C7, $CF, $D3, $D7, $DB, $DF
Décrémente la mémoire puis compare le résultat avec A.
Équivalent a : DEC val / CMP val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $C3 | 2 | 8 |
| Page zéro | $C7 | 2 | 5 |
| Absolu | $CF | 3 | 6 |
| (Indirect),Y | $D3 | 2 | 8 |
| Page zero,X | $D7 | 2 | 6 |
| Absolu,Y | $DB | 3 | 7 |
| Absolu,X | $DF | 3 | 7 |
ISC / ISB (Increment and Subtract) - opcodes : $E3, $E7, $EF, $F3, $F7, $FB, $FF
Incrémenté la mémoire puis soustrait le résultat de A (avec retenue). Equivalent a : INC val / SBC val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $E3 | 2 | 8 |
| Page zéro | $E7 | 2 | 5 |
| Absolu | $EF | 3 | 6 |
| (Indirect),Y | $F3 | 2 | 8 |
| Page zero,X | $F7 | 2 | 6 |
| Absolu,Y | $FB | 3 | 7 |
| Absolu,X | $FF | 3 | 7 |
SLO (Shift Left and OR) - opcodes : $03, $07, $0F, $13, $17, $1B, $1F
Effectue un ASL sur la mémoire puis un ORA avec A. Équivalent a : ASL val / ORA val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $03 | 2 | 8 |
| Page zéro | $07 | 2 | 5 |
| Absolu | $0F | 3 | 6 |
| (Indirect),Y | $13 | 2 | 8 |
| Page zero,X | $17 | 2 | 6 |
| Absolu,Y | $1B | 3 | 7 |
| Absolu,X | $1F | 3 | 7 |
RLA (Rotate Left and AND) - opcodes : $23, $27, $2F, $33, $37, $3B, $3F
Effectue un ROL sur la mémoire puis un AND avec A.
Équivalent a : ROL val / AND val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $23 | 2 | 8 |
| Page zero | $27 | 2 | 5 |
| Absolu | $2F | 3 | 6 |
| (Indirect),Y | $33 | 2 | 8 |
| Page zero,X | $37 | 2 | 6 |
| Absolu,Y | $3B | 3 | 7 |
| Absolu,X | $3F | 3 | 7 |
SRE (Shift Right and EOR) - opcodes : $43, $47, $4F, $53, $57, $5B, $5F
Effectue un LSR sur la mémoire puis un EOR avec A.
Équivalent a : LSR val / EOR val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $43 | 2 | 8 |
| Page zéro | $47 | 2 | 5 |
| Absolu | $4F | 3 | 6 |
| (Indirect),Y | $53 | 2 | 8 |
| Page zero,X | $57 | 2 | 6 |
| Absolu,Y | $5B | 3 | 7 |
| Absolu,X | $5F | 3 | 7 |
RRA (Rotate Right and Add) - opcodes : $63, $67, $6F, $73, $77, $7B, $7F
Effectue un ROR sur la mémoire puis un ADC avec A.
Équivalent a : ROR val / ADC val
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| (Indirect,X) | $63 | 2 | 8 |
| Page zéro | $67 | 2 | 5 |
| Absolu | $6F | 3 | 6 |
| (Indirect),Y | $73 | 2 | 8 |
| Page zero,X | $77 | 2 | 6 |
| Absolu,Y | $7B | 3 | 7 |
| Absolu,X | $7F | 3 | 7 |
ANC (AND + set Carry) - opcodes : $0B, $2B
Effectue un AND immédiat puis copie le bit 7 du résultat dans le drapeau C. Équivalent a : AND #val, suivre N → C
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| Immediat | $0B | 2 | 2 |
| Immediat | $2B | 2 | 2 |
ALR / ASR (AND + LSR) - opcode : $4B
Effectue un AND immédiat puis un LSR sur A.
Équivalent a : AND #val / LSR A
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| Immediat | $4B | 2 | 2 |
ARR (AND + ROR) - opcode : $6B
Effectue un AND immédiat puis un ROR sur A, avec un positionnement particulier des drapeaux C et V.
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| Immediat | $6B | 2 | 2 |
SBX / AXS (A AND X - opérande → X) - opcode : $CB
Calcule (A AND X) - opérande immédiat et entrepose dans X.
Le drapeau C est positionne comme pour une comparaison.
| Mode | Opcode | Octets | Cycles |
|---|---|---|---|
| Immediat | $CB | 2 | 2 |
Opcodes instables et dangereux
Certains opcodes non documentes ont un comportement instable dépendant de l'état du bus de données, du minuterie, ou de la révision du processeur :
| Instruction | Opération |
|---|---|
| SHA ($93, $9F) | Entrepose A AND X AND (adresse haute + 1) |
Comportement imprévisible dans certaines conditions de minuterie.
| Instruction | Opération |
|---|---|
| SHX ($9E) | Entrepose X AND (adresse haute + 1) |
| SHY ($9C) | Entrepose Y AND (adresse haute + 1) |
Même problème de stabilité que SHA.
| TAS / SHS ($9B) - SP := A AND X, entrepose SP AND (adresse haute + 1) |
Modifie le pointeur de pile de manière imprévisible.
| LAS ($BB) - A := X := SP := M AND SP |
Comportement dépendant de la valeur courante de SP.
| ANE / XAA ($8B) - A := (A OR constante) AND X AND operande |
La "constante" dépend de la révision du processeur et de la température ! Totalement imprévisible sur certaines révisions.
L'opcode "KIL" / "JAM" / "HLT"
Les opcodes $02, $12, $22, $32, $42, $52, $62, $72, $82, $92, $B2, $D2, $F2 provoquent le blocage du processeur. Le 6502 entre dans un état ou il répète infiniment le même cycle de lecture sans avancer le compteur programme. Seul un signal RESET peut sortir le processeur de cet état.
Ces opcodes sont parfois utilises comme mécanisme de protection anti-copie dans les jeux : si le code est modifié, l'exécution atteint un KIL et la machine se bloque.
NOP non documentes
Plusieurs opcodes agissent comme des NOP de différentes tailles :
| Opcode | Taille | Cycles | Description |
|---|---|---|---|
| $1A | 1 | 2 | NOP implicite (comme $EA) |
| $3A | 1 | 2 | NOP implicite |
| $5A | 1 | 2 | NOP implicite |
| $7A | 1 | 2 | NOP implicite |
| $DA | 1 | 2 | NOP implicite |
| $FA | 1 | 2 | NOP implicite |
| $80 | 2 | 2 | NOP immédiat (saute 1 octet) |
| $82 | 2 | 2 | NOP immediat |
| $89 | 2 | 2 | NOP immediat |
| $C2 | 2 | 2 | NOP immediat |
| $E2 | 2 | 2 | NOP immediat |
| $04 | 2 | 3 | NOP page zéro (saute 1 octet) |
| $44 | 2 | 3 | NOP page zéro |
| $64 | 2 | 3 | NOP page zéro |
| $14 | 2 | 4 | NOP page zero,X |
| $34 | 2 | 4 | NOP page zero,X |
| $54 | 2 | 4 | NOP page zero,X |
| $74 | 2 | 4 | NOP page zero,X |
| $D4 | 2 | 4 | NOP page zero,X |
| $F4 | 2 | 4 | NOP page zero,X |
| $0C | 3 | 4 | NOP absolu (saute 2 octets) |
| $1C | 3 | 4(+1) | NOP absolu,X |
| $3C | 3 | 4(+1) | NOP absolu,X |
| $5C | 3 | 4(+1) | NOP absolu,X |
| $7C | 3 | 4(+1) | NOP absolu,X |
| $DC | 3 | 4(+1) | NOP absolu,X |
| $FC | 3 | 4(+1) | NOP absolu,X |
Ces NOP sont utiles pour :
- Sauter un nombre précis d'octets sans modifier l'état
- Ajuster le timing avec des coûts en cycles différents
- Correctif du code existant sans décaler les adresses
Compatibilité avec le 65C02
Le 65C02 (version CMOS) ne supporte PAS les opcodes non documentes du 6502 NMOS. Sur le 65C02 :
- La plupart des opcodes indéfinis sont traites comme des NOP de 1, 2 ou 3 octets
- Les opcodes KIL ne bloquent plus le processeur
- Les effets combinées (LAX, SAX, DCP,...) ne fonctionnent plus
- Le 65C02 ajoute ses propres nouvelles instructions à la place de certains opcodes indéfinis (BRA, PHX, PHY, PLX, PLY, STZ, TRB, TSB, BBR, BBS)
Résumé des opcodes non documentes par fiabilité :
| Fiabilité | Opcodes |
|---|---|
| Fiable | LAX, SAX, DCP, ISC, SLO, RLA, SRE, RRA, ANC, ALR, ARR, SBX |
| Instable | SHA, SHX, SHY, TAS, LAS |
| Imprévisible | ANE/XAA |
| Bloquant | KIL/JAM/HLT ($x2 sauf $A2) |
Avertissement : l'utilisation d'opcodes non documentés n'est recommandée que pour la programmation sur du matériel 6502 NMOS réel. Certains émulateurs ne les supportent pas correctement, et ils sont incompatibles avec le 65C02/65C816.
Comparaison avec d'autres microprocesseurs
L'existence d'opcodes non documentés est spécifique aux microprocesseurs de cette époque :
| Microprocesseur | Opcodes indéfinis | Comportement |
|---|---|---|
| MOS 6502 NMOS | 105 sur 256 | Effets déterminés mais non garantis |
| WDC 65C02 | 0 (réduit) | Nouveaux opcodes + NOP |
| Zilog Z80 | ~19 (prefixes CB/DD) | Effets prévisibles |
| Intel 8080 | ~12 | Non spécifié |
| Motorola 6800 | ~40 | Non spécifié |
| Intel 8086 | Très peu | Non spécifié |
| Motorola 68000 | Exceptions ligne A/F | Trap explicite |