-
Notifications
You must be signed in to change notification settings - Fork 2
Small but useful 6502 assembler in ~3K lines of ANSI C code.
License
boeckmann/asm6502
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
ASM6502 Assembler Manual ======================== Copyright 2022-2023 by Bernd Boeckmann 1 Where to get -------------- Latest source and binaries may be found at: - https://github.com/boeckmann/asm6502 - https://codeberg.org/boeckmann/asm6502 Binaries are provided for Windows, DOS, and OS/2. A Unix make file is provided which should be easily adaptable to any ANSI C conformant compiler. There is a port of the Commodore 64 KERNAL v3 and BASIC v2 source to ASM6502. If it is of interest to you, get it at: - https://github.com/boeckmann/c64krnl 2 Introduction -------------- ASM6502 is a small two-pass assembler for the MOS Technology 6502 microprocessor used in many home computers of the 8-bit era. It consists of under 3K lines of C code and can be built with compilers conformant to the C89 standard. ASM6502 implements some advanced features, like local labels, conditional assembly, optimization of opcodes, and the ability to produce listing files. It is powerful enough to produce byte-exact replicas of the Commodore C64 KERNAL and BASIC ROMs. The assembler outputs plain binary files. The following listing contains a small sample program. It is the classic hello world for the Commodore C64. To assemble it, invoke the assembler with the source, output, and listing files as arguments: asm6502 helloc64.a65 -o helloc64.prg -l helloc64.lst 1: ; C64 Hello World 2: 3: LOAD_ADDR = $0801 4: 5: .word LOAD_ADDR 6: .org LOAD_ADDR 7: 8: CHROUT = $FFD2 9: SYS = $9E 10: 11: basic_upstart: 12: .word @end, 10 13: .byte SYS, " 2062",0 14: @end .word 0 15: 16: start: 17: ldx #0 18: @l lda hello@msg,x 19: jsr CHROUT 20: inx 21: cpx #hello@len 22: bne @l 23: rts 24: 25: hello: 26: @msg .byte "HELLO, WORLD!", CR, LF 27: @len = @ - @msg 28: 29: CR = 13 30: LF = %1010 3 Concepts and Terminology -------------------------- The task of an assembler is to translate an _assembler source_ containing human-readable _instructions_ to a _binary representation_ the processor understands. This binary representation is called _machine code_. There is a one-to-one mapping between the human-readable instructions contained in the assembler source and the generated machine code. Each instruction a processor understands is given a name, called _mnemonic_. This name is chosen so that it describes to the programmer what the instruction does. Every instruction is also assigned a numeric value, called the operation code or _opcode_. This is what gets written by the assembler as machine code to an output file. The opcode is interpreted by the processor to decide what to do. Beside the instruction itself, additional information may be required to process it. The additional information is provided in the source by one or more _arguments_ following the mnemonic. In machine code this additional information is encoded in binary form following the opcode. `ADC #42' is an example of an instruction, where ADC is the mnemonic identifying the instruction, and #42 is the argument. This particular instruction, understood by the MOS6502 processor, adds the value 42 to the value stored in processor register A. It then writes the result back to A. The set of instructions a CPU understands is called _instruction set_. There are many different kinds of CPUs and instruction sets. As a result, there is not something like `the assembler', but there are many of them, all adapted to one or more specific instruction sets. ASM6502 is an assembler that is adapted to the instruction set of the MOS6502 processor family. Beside generating an output file containing machine code, ASM6502 may also generate a _listing file_ containing a _program listing_. This listing shows the lines of the assembler source side-by-side to the generated machine code in hexadecimal notation. Pos Addr Code Line# Assembler text 0000 0000 69 2A 1: ADC #42 0002 0002 E8 2: INX Pos indicates the position of the instructions regarding the output file. Addr represents the _location_ or _address_ of the code while it is executed. Let's further elaborate what the arguments to instructions may be. In the example above, `#42' is a numeric value that is directly used to do the addition. It is therefore called an _immediate_ value, and the mode the processor operates in is called _immediate addressing_ mode. The `INX' instruction above implicitly operates on a register called X. For this reason it is called _implicit addressing_. Often, the argument specifies a memory location. This memory location may be given relative to the start of the address space. This it is called _absolute addressing_. If the memory location is specified relative to some other location, we call it _relative addressing_. Sometimes one does not want to encode a fixed memory location into the machine instruction, but instead use the content of some memory location as the address to operate on. This is called _indirect addressing_. The sequence of instructions executed by the processor may be altered by the programmer utilizing special machine instructions. Some of these instructions modify this sequence unconditionally, and some alter it if a special condition is met. The instructions are called _jump_ or _branching_ instructions. The information of the _jump target_ is encoded as address within the instruction. The assembler supports the programmer by letting him specify a jump target by giving it a name, called _label_. In the introductory example, `basic_upstart', `start', and `hello' are labels. One task of an assembler is to assign an address to every label the programmer defines. Sometimes this is not that easy. For example, a label may be used as a jump target before it is defined in the assembler source. This is called _forward-reference_. That is the reason ASM6502 is a so called two-pass assembler. In the first pass, the assembler processes the whole file to determine the addresses of all labels. In the second pass all required information is provided, and the machine code is generated. Using forward-references can sometimes lead to non-optimal machine code, because the assembler has to guess the size of the forward- referenced `thing'. In that case the programmer can support the assembler by giving a hint what type the referenced `thing' is. Some _multi-pass assemblers_ process the source more than two times and can optimize the machine code even in cases they encounter forward- referenced labels. But for the sake of simplicity ASM6502 does not do that. Beside labels the programmer may also define _variables_. Variables may be assigned any kind of mathematical expression. Variables and labels are also called _symbols_, and the name identifying them is referred to as _identifier_. Character sequences which by itself provide a value to the assembler, like the character sequence `42' that represents the numeric value 42, are considered to be _literals_. 4 Syntax and Semantics ---------------------- The following chapter describes the data model and the syntax accepted by the assembler. 4.1 Input Files Input files should have .a65 as extension to distinguish it from files written for other assemblers. The files should be encoded in the ASCII or UTF-8 character sets. 4.2 Data Types Two data types are known to the assembler: - _byte_: 8-bit, storing whole numbers between 0 and 255. - _word_: 16-bit, storing positive whole numbers between 0 and 65535. Negative numbers may also be stored in the range from - 32768 to -1. Negative numbers are stored in two-complement representation. There is no distinct boolean type. In a boolean context, any value other than zero is considered to be a boolean true. 4.3 Symbols The assembler distinguishes two types of case-sensitive symbols: _labels_ and _variables_. A label stores the address of the instruction or data that is currently assembling. It is defined at the beginning of a line by appending its name with a colon. The colon may be left out if the label name is not an instruction mnemonic. A variable is defined by assigning an expression to it. In the following example, hello is a label, and CHROUT is a variable. CHROUT = $ffd2 hello: jmp CHROUT Labels and variables may be of type byte or word. A label is of type byte if it is assigned an address within the first 256 bytes (zero- page). Otherwise, it is of type word. The data type of a variable is that of the expression assigned to it, unless it is forward- referenced. Forward-referenced means that a symbol is used in an expression before it is defined. Forward-referenced symbols are _always_ of type word, regardless of what is assigned to them. If a variable is assigned a value multiple times, it must be the same value. Otherwise, it is an illegal redefinition. Labels may not be defined more than once. Variables and labels may be defined locally by prepending their name with @. They are then associated with the previous non-local label defined. They may be referenced within expressions locally by @name or with their qualified name label@name outside their local scope. Example: jmp hello@l ; fully qualified label reference hello: @l jmp @l ; local label reference 4.4 Expressions There are many places where expressions may occur, for example on the right side of a variable definition or as an argument to a machine instruction. Every expression is either of type byte, word or of unknown type. Every expression also has a defined numeric value or an undefined value. 4.4.1 Primitive Expressions The most primitive form of an expression is a numeric constant, which can be given in decimal, hexadecimal, or binary. The value of the constant determines its type. 5 ; decimal byte constant $a ; hexadecimal byte constant $4711 ; hexadecimal word constant %1011 ; binary byte constant -1 ; word constant $FFFF (2-complement) A byte-sized value can be forced to be of type word by prepending zeros. $00a ; hex word constant because more than 2 digits 0123 ; decimal word constant because more than 3 digits A character enclosed by ' is evaluated to its ASCII value. 'x' ; byte typed ASCII character code of x The address counter symbol @ returns the address of the currently assembled instruction. The symbol .? returns an undefined value of unknown type. .? ; undefined value @ ; current address Label and variable names evaluate to their respective value. LOAD_ADDR = 2048 lda LOAD_ADDR ; load memory cell 2048 into accumulator 4.4.2 Operator Precedence Expressions may be composed of arithmetic sub-expressions. Operator precedence is respected. The supported operations from highest to lowest precedence are: - Expressions enclosed by parentheses () - bit-wise complement ~, logical negation !, and is-defined ? operators, all unary and right-associative - Multiplication *, division /, bit-wise and &, logical left << and right >> shift - Unary plus and binary addition +, unary minus and subtraction -, bit-wise or |, exclusive or ^ - Unary low < and high > byte select, lossless unary conversion operators [b] and [w] - The comparison operators are ==, !=, <, >, <=, >= - Logical and && - Logical or || - Defined-or-else ?: 4.4.3 Arithmetic and bit-wise Operators The usual semantics for the arithmetic operators apply. If there is an undefined argument to one of the arithmetic operators, the result value is undefined. Type inference is performed as such that if any of the arguments is of type word, the result is of type word. The result is also of type word if it would otherwise overflow the range of type byte. The bit-wise complement operator ~ respects the data type when toggling bits. This means that ~1 becomes $FE, and not $FFFE. Examples: 2+3*5 ; value 17 $4700 | $11 ; is $4711 255+255 ; of type word because >255 4.4.4 Byte-select and Conversion Operators The low-byte select operator < returns the low byte of a word-sized expression, or the unmodified value of a byte-sized expression. The high-byte select operator > returns the high byte of a word-sized expression shifted eight bits to the right. It returns zero for byte-sized expressions. The resulting data type of both operators is byte. If applied to an undefined argument, the result is undefined. The convert to byte [b] and convert to word [w] operators change the data type of their expression. If the expression does not fit into a byte, [b] raises an error. The operators also change the type of the undefined value while retaining undefined as a value. 4.4.5 Comparison and Logical Operators The comparison operators return 1 as true value, and 0 as false value. They return an undefined value if at least one of their arguments is undefined, with the logical or || operator being an exception. It returns true if at least one of its arguments is true. The logical negation operator ! is right-associative. 4.4.6 Is-defined Operator The unary, right associative is-defined operator ? returns true, if its argument is defined. Otherwise, false is returned. The result is of type byte. 4.4.7 Defined-or-else Operator The defined-or-else operator ?: returns its first argument if it is defined, otherwise its second. It is left-associative. Example: Y = .? Z = X ?: Y ?: $DEAD ; assign $DEAD to Z ; because X and Y are undefined 4.5 Line Format A line may either contain a statement or a conditional statement or none of them. A statement it either a variable definition, an instruction or a directive. Instructions and directives may be preceded by a label definition. Also, a label definition may stand for its own. Conditional statements are .IF, .ELSE, and .ENDIF. These may not be preceded by a label. Each line may end with a comment. Comments are started by semicolon and ignored by the assembler. start: ; line consisting only of a label loop: BNE loop ; label and instruction msg .byte "Hello" ; label followed by a directive X = 42 ; variable definition .if X == 42 ; conditional statement 4.6 Directives Directives instruct the assembler to do certain things. They may or may not produce output data. Names of directives start with a dot. The directives currently known to the assembler are: 4.6.1 .ALIGN Aligns the address counter to the next multiple of the first argument, and emits the number of bytes necessary to perform the alignment. If the second byte-sized argument is given, this value is used as fill-byte. Otherwise, the fill-byte is zero. .ALIGN 4 ; align @ to a multiple of 4 .ALIGN $100, $EA ; align to next page, fill with $EA 4.6.2 .ASSERT and .ASSERT1 Tests if the expression given first argument returns a true value, otherwise terminates with an error. .ASSERT runs on pass two, and ASSERT1 runs on pass one. The arguments following the first are handled like the .ECHO directive does it. Example: .ASSERT 2 > 1, "arithmetic implementation is flawed" 4.6.3 .BINARY Directive Copies binary data from a file to the output file. Numeric expressions specifying a start offset and a length may be given as arguments. If a length is given, the start offset must also be specified. Example: .BINARY "SPRITE.DAT". ; copies the whole file .BINARY "SPRITE.DAT", $10 ; skip the first 16 bytes .BINARY "SPRITE.DAT", $10, 64 ; copy 64 bytes from offset 16 4.6.4 .BYTE Directive Produces one or more output bytes. The arguments are separated by a comma. Numeric expressions or strings may be used as arguments. The values of numeric expressions must fit into a byte. Strings must be enclosed by ". Example: .BYTE 1 .BYTE 47, 11 .BYTE "Hello, World", 13, 10 4.6.5 .CPU Directive Selects the instruction set the assembler understands depending on the given CPU type. .CPU 6502 ; targets the NMOS 6502 CPU .CPU 65C02 ; targets the CMOS 65C02 CPU (experimental) 4.6.6 .ECHO and .ECHO1 Directives Print the arguments to standard output. .ECHO does it on the second assembler pass, while .ECHO1 does it on the first pass. The arguments may either be strings or numeric expressions, separated by comma. Numeric expressions may be prefixed by the format specifier [$] to output the number in hexadecimal format. Otherwise, it is printed in decimal. Example: .ECHO "hexadecimal representation of ", 4711, " is ", [$]4711 4.6.7 .ERROR directive Aborts the assembly along with file name and line number information. Accepts the same parameters as .ECHO for the error message. 4.6.8 .FILL Directive Starting from the current position of the output file, emits as many bytes as given by the first argument. If the second argument is given, the region is filled with its byte-sized value. Otherwise, it is filled with zero. The address counter @ is increased accordingly. Example: .FILL 100 ; fill 100 bytes with zero .FILL 16, $EA ; insert 16 NOPs ($EA) into the code 4.6.9 .IF, .IFN, .ELSE and .ENDIF Directives Conditionally assembles code if the condition of the argument to .IF or .IFN is met. For .IF, the condition is met if the argument yields a defined value other than zero. For .IFN, the condition is met if the argument does not yield a defined value or the value is zero. If the condition is met, the code between .IF or .IFN and .ELSE is assembled, or between .IF or .IFN and .ENDIF, if .ELSE is not given. If the argument to .IF or .IFN is not met and .ELSE is specified, the code between .ELSE and .ENDIF is assembled. .IF and .IFN treat an undefined expression like a boolean false. The conditional directives may _not_ be preceded by a label. Example: C64 = 1 .IF C64 .ECHO "I am assembled for the C64" .ELSE .ECHO "I am assembled for the PET" .ENDIF In listing files, the unprocessed lines are indicated by a minus after the line number instead of a colon. 4.6.10 .IFDEF and .IFNDEF Directives An argument to .IFDEF is considered true, if its value is defined. An argument to .IFNDEF is considered true, if its value is undefined. Otherwise, the directives behave like their .IF and .IFN counterparts. 4.6.11 .INCLUDE Directive Substitutes the directive with the assembler source contained in the file given as argument. Example: .INCLUDE "c64prg.i65" 4.6.12 .LIST and .NOLIST Directives If a listing file is given via command line, listing generation is initially enabled. If the user wants some parts of the code to be excluded from the listing, the region can be surrounded by .NOLIST and .LIST statements. If listing generation is disabled when an .INCLUDE statement is processed, .LIST inside the included file has no effect. A .NOLIST inside an include file does not propagate to the parent file. 4.6.13 .ORG Directive Sets the address counter to the numeric value of the argument. Does not modify the offset into the output file. This means that .ORG can not be used to `jump around' in the output file. May be used multiple times. Example: .ORG $0801 4.6.14 .REPEAT and .ENDREP Repeats the block of code enclosed by .REPEAT and .ENDREP the number of times given by the argument to .REPEAT. Example: .REPEAT 30 ; ...for 30 overscan scanlines... sta WSYNC .ENDREP 4.6.15 .SYM and .NOSYM Directives Selectively enables or disables the inclusion of defined labels and variables in the symbol map for specific code regions. .SYM enables it (default), and .NOSYM disables it. The symbol map is part of the program listing. 4.6.16 .WARNING Directive Prints a warning along with file name and line number information. Accepts the same parameters as .ECHO for the warning message. 4.6.17 .WORD Directive Produces one or more output words. Example: .WORD $0801, 4711 4.7 Addressing Modes Every assembler instruction consists of a mnemonic identifying the machine instruction followed by at most one numeric argument including addressing mode specifiers. Instruction mnemonics are case-insensitive. The assembler supports all MOS6502 addressing modes: 4.7.1 Implicit and Accumulator Addressing Either no argument or accumulator is implicitly assumed by the instruction CLC ; clear carry ROR ; rotate accumulator right 4.7.2 Immediate Addressing The byte-sized argument is encoded in the byte following the opcode. The argument for the assembler instruction is prefixed by # to indicate an immediate value. The argument may be any expression yielding a byte-sized numeric value. LDA #42 ; load value 42 into the accumulator 4.7.3 Relative Addressing Relative addressing is only used by branch instructions. The branch offset in the range of -128 to 127 is encoded by the byte following the opcode. The assembler interprets the argument, which may be any numeric expression, relative to the current address counter. loop: BNE loop 4.7.4 Absolute Addressing A word-sized address is encoded following the opcode byte. The assembler interprets any word-sized expression following an instruction mnemonic as an absolute address. LDA $4711 ; load contents of address $4711 into the accumulator 4.7.5 Zero-page Addressing A byte-sized address is encoded following the opcode byte. The assembler interprets any byte-sized expression following an instruction mnemonic as a zero-page address. LDA $47 ; load contents of address $47 into the accumulator LDA >$4711 ; load contents of address $47 into the accumulator 4.7.6 Absolute X and Absolute X Addressing The address is encoded in the word following the opcode and displaced by the contents of the X or Y register. LDA $4711,X ; load contents of address $4711 displaced by X LDA $4711,Y ; load contents of address $4711 displaced by Y 4.7.7 Zero-page X and Zero-page Y Addressing The address is encoded in the byte following the opcode displaced by the contents of the X or Y register. LDA $47,X ; A = contents of address $47 displaced by X LDX $11,Y ; X = load contents of address $47 displaced by Y 4.7.8 Indirect Addressing The word-sized address is stored in the memory location given by the word-sized argument. In assembler syntax, an indirect address is indicated by enclosing the argument in parentheses, like in the following. JMP ($4711) The following one is a syntax error, because the assembler assumes indirect addressing mode instead of a sub-expression grouped by parentheses: JMP (2+3)*1000 To correct this, you may rewrite it as: JMP +(2+3)*1000 or JMP ((2+3)*1000) 4.7.9 Indexed Indirect by X and Indirect Indexed by Y Addressing Indexed indirect by X addresses the byte referenced by the contents of the word stored at zero-page address b + X. Indirect indexed by Y adds Y to the address word stored in zero-page address b to calculate the address to operate on. b = 15 ORA (b,X) ORA (b),Y A Command Line Syntax --------------------- Usage: asm6502 input -o output [options]... [VAR=number]... Options: -q be quiet, unless an error occurred -o output set output file name -l listing set optional listing file name -w0 disable all warnings -w1 enable warnings (default) -w2 enable warnings and hints Variables defined from the command line are known to the assembler when assembling files. The numbers are parsed like number literals in the source code. B Instruction Reference ----------------------- In the following instruction list, #$42 is a representative for a byte-sized immediate value. This value may be substituted by any other byte-sized value. $15 is a representative for a zero-page memory address, and $4711 is a representative for a word-sized memory address. The first hexadecimal value on a line is the instruction opcode followed by at most two bytes of additional data defined by the instruction argument. The syntax of the different addressing modes is described in one of the previous chapters. B.1 ADC - add with carry Flags: N Z C V 69 42 adc #$42 65 15 adc $15 75 15 adc $15,x 6D 11 47 adc $4711 7D 11 47 adc $4711,x 79 11 47 adc $4711,y 61 15 adc ($15,x) 71 15 adc ($15),y B.2 AND - bit-wise and with accumulator Flags: N Z 29 42 and #$42 25 15 and $15 35 15 and $15,x 2D 11 47 and $4711 3D 11 47 and $4711,x 39 11 47 and $4711,y 21 15 and ($15,x) 31 15 and ($15),y B.3 ASL - arithmetic shift left Flags: N Z C 0A asl 06 15 asl $15 16 15 asl $15,x 0E 11 47 asl $4711 1E 11 47 asl $4711,x B.4 BCC - branch if carry cleared 90 FE bcc @ B.5 BCS - branch if carry set B0 FE bcs @ B.6 BEQ - branch if equal F0 FE beq @ B.7 BIT - test bits Negative flag becomes the bit 7 of the operand, overflow flag becomes bit 6 of the operand. Zero flag is set if the bit-wise and operation between the accumulator and the operand is zero, otherwise it is cleared. Flags: N Z V 24 15 bit $15 2C 11 47 bit $4711 B.8 BMI - branch if negative 30 FE bmi @ B.9 BNE - branch if not equal D0 FE bne @ B.10 BPL - branch if positive 10 FE bpl @ B.11 BRK - force break BRK cannot be masked by setting interrupt disable flag. Forces the processor to continue at the address stored in the IRQ vector $FFFE. Pushes the flags with set break (B) flag to differentiate from a hardware interrupt. RTI and PLP ignore the break flag. Flags: I=1 00 brk B.12 BVC - branch if overflow flag cleared 50 FE bvc @ B.13 BVS - branch if overflow flag set 70 FE bvs @ B.14 CLC - clear carry flag Flags: C=0 18 clc B.15 CLD - clear decimal flag Flags: D=0 D8 cld B.16 CLI - clear interrupt disable flag Flags: I=0 58 cli B.17 CLV - clear overflow flag Flags: V=0 B8 clv B.18 CMP - compare with accumulator Flags: N Z C C9 42 cmp #$42 C5 15 cmp $15 D5 15 cmp $15,x CD 11 47 cmp $4711 DD 11 47 cmp $4711,x D9 11 47 cmp $4711,y C1 15 cmp ($15,x) D1 15 cmp ($15),y B.19 CPX - compare with X register Flags: N Z C E0 42 cpx #$42 E4 15 cpx $15 EC 11 47 cpx $4711 B.20 CPY - compare with Y register Flags: N Z C C0 42 cpy #$42 C4 15 cpy $15 CC 11 47 cpy $4711 B.21 DEC - decrement Flags: N Z C6 15 dec $15 D6 15 dec $15,x CE 11 47 dec $4711 DE 11 47 dec $4711,x B.22 DEX - decrement X register Flags: N Z CA dex B.23 DEY - decrement Y register Flags: N Z 88 dey B.24 EOR - exclusive or Flags: N Z 49 42 eor #$42 45 15 eor $15 55 15 eor $15,x 4D 11 47 eor $4711 5D 11 47 eor $4711,x 59 11 47 eor $4711,y 41 15 eor ($15,x) 51 15 eor ($15),y B.25 INC - increment Flags: N Z E6 15 inc $15 F6 15 inc $15,x EE 11 47 inc $4711 FE 11 47 inc $4711,x B.26 INX - increment X register Flags: N Z E8 inx B.27 INY - increment Y register Flags: N Z C8 iny B.28 JMP - jump 4C 11 47 jmp $4711 6C 11 47 jmp ($4711) B.29 JSR - call subroutine 20 11 47 jsr $4711 B.30 LDA - load accumulator Flags: N Z A9 42 lda #$42 A5 15 lda $15 B5 15 lda $15,x AD 11 47 lda $4711 BD 11 47 lda $4711,x B9 11 47 lda $4711,y A1 15 lda ($15,x) B1 15 lda ($15),y B.31 LDX - load X register Flags: N Z A2 42 ldx #$42 A6 15 ldx $15 B6 15 ldx $15,y AE 11 47 ldx $4711 BE 11 47 ldx $4711,y B.32 LDY - load Y register Flags: N Z A0 42 ldy #$42 A4 15 ldy $15 B4 15 ldy $15,x AC 11 47 ldy $4711 BC 11 47 ldy $4711,x B.33 LSR - logical shift right Flags: N=0 Z C 4A lsr 46 15 lsr $15 56 15 lsr $15,x 4E 11 47 lsr $4711 5E 11 47 lsr $4711,x B.34 NOP - no-operation EA nop B.35 ORA - bit-wise or with accumulator Flags: N Z 09 42 ora #$42 05 15 ora $15 15 15 ora $15,x 0D 11 47 ora $4711 1D 11 47 ora $4711,x 19 11 47 ora $4711,y 01 15 ora ($15,x) 11 15 ora ($15),y B.36 PHA - push accumulator on stack 48 pha B.37 PHP - push flags on stack 08 php B.38 PLA - pop accumulator from stack Flags: N Z 68 pla B.39 PLP - pop flags from stack Flags: N Z C I D V 28 plp B.40 ROL - rotate left through carry Flags: N Z C 2A rol 26 15 rol $15 36 15 rol $15,x 2E 11 47 rol $4711 3E 11 47 rol $4711,x B.41 ROR - rotate right through carry Flags: N Z C 6A ror 66 15 ror $15 76 15 ror $15,x 6E 11 47 ror $4711 7E 11 47 ror $4711,x B.42 RTI - return from interrupt Flags: N Z C I D V 40 rti B.43 RTS - return from subroutine 60 rts B.44 SBC - subtract from accumulator with carry Flags: N Z C V E9 42 sbc #$42 E5 15 sbc $15 F5 15 sbc $15,x ED 11 47 sbc $4711 FD 11 47 sbc $4711,x F9 11 47 sbc $4711,y E1 15 sbc ($15,x) F1 15 sbc ($15),y B.45 SEC - set carry flag Flags: C=1 38 sec B.46 SED - set decimal flag Flags: D=1 F8 sed B.47 SEI - set interrupt disable flag Flags: I=1 78 sei B.48 STA - store accumulator 85 15 sta $15 95 15 sta $15,x 8D 11 47 sta $4711 9D 11 47 sta $4711,x 99 11 47 sta $4711,y 81 15 sta ($15,x) 91 15 sta ($15),y B.49 STX - store X register 86 15 stx $15 96 15 stx $15,y 8E 11 47 stx $4711 B.50 STY - store Y register 84 15 sty $15 94 15 sty $15,x 8C 11 47 sty $4711 B.51 TAX - transfer accumulator to X AA tax Flags: N Z B.52 TAY - transfer accumulator to Y A8 tay Flags: N Z B.53 TSX - transfer stack pointer to X Flags: N Z BA tsx B.54 TXA - transfer X to accumulator Flags: N Z 8A txa B.55 TXS - transfer X to stack pointer Flags: N Z 9A txs B.56 TYA - transfer Y to accumulator Flags: N Z 98 tya [14.11.2023 16:15:10]
About
Small but useful 6502 assembler in ~3K lines of ANSI C code.