=======MOS 6502======= Address Modes: \\ A .... Accumulator OPC A operand is AC \\ abs .... absolute OPC $HHLL operand is address $HHLL \\ abs,X .... absolute, X-indexed OPC $HHLL,X operand is address incremented by X with carry \\ abs,Y .... absolute, Y-indexed OPC $HHLL,Y operand is address incremented by Y with carry \\ # .... immediate OPC #$BB operand is byte (BB) \\ impl .... implied OPC operand implied \\ ind .... indirect OPC ($HHLL) operand is effective address; effective address is value of address \\ X,ind .... X-indexed, indirect OPC ($BB,X) operand is effective zeropage address; effective address is byte (BB) incremented by X without carry \\ ind,Y .... indirect, Y-indexed OPC ($LL),Y operand is effective address incremented by Y with carry; effective address is word at zeropage address \\ rel .... relative OPC $BB branch target is PC + offset (BB), bit 7 signifies negative offset \\ zpg .... zeropage OPC $LL operand is of address; address hibyte = zero ($00xx) \\ zpg,X .... zeropage, X-indexed OPC $LL,X operand is address incremented by X; address hibyte = zero ($00xx); no page transition \\ zpg,Y .... zeropage, Y-indexed OPC $LL,Y operand is address incremented by Y; address hibyte = zero ($00xx); no page transition \\ Instructions by Name: \\ ADC .... add with carry \\ AND .... and (with accumulator) \\ ASL .... arithmetic shift left \\ BCC .... branch on carry clear \\ BCS .... branch on carry set \\ BEQ .... branch on equal (zero set) \\ BIT .... bit test \\ BMI .... branch on minus (negative set) \\ BNE .... branch on not equal (zero clear) \\ BPL .... branch on plus (negative clear) \\ BRK .... interrupt \\ BVC .... branch on overflow clear \\ BVS .... branch on overflow set \\ CLC .... clear carry \\ CLD .... clear decimal \\ CLI .... clear interrupt disable \\ CLV .... clear overflow \\ CMP .... compare (with accumulator) \\ CPX .... compare with X \\ CPY .... compare with Y \\ DEC .... decrement \\ DEX .... decrement X \\ DEY .... decrement Y \\ EOR .... exclusive or (with accumulator) \\ INC .... increment \\ INX .... increment X \\ INY .... increment Y \\ JMP .... jump \\ JSR .... jump subroutine \\ LDA .... load accumulator \\ LDY .... load X \\ LDY .... load Y \\ LSR .... logical shift right \\ NOP .... no operation \\ ORA .... or with accumulator \\ PHA .... push accumulator \\ PHP .... push processor status (SR) \\ PLA .... pull accumulator \\ PLP .... pull processor status (SR) \\ ROL .... rotate left \\ ROR .... rotate right \\ RTI .... return from interrupt \\ RTS .... return from subroutine \\ SBC .... subtract with carry \\ SEC .... set carry \\ SED .... set decimal \\ SEI .... set interrupt disable \\ STA .... store accumulator \\ STX .... store X \\ STY .... store Y \\ TAX .... transfer accumulator to X \\ TAY .... transfer accumulator to Y \\ TSX .... transfer stack pointer to X \\ TXA .... transfer X to accumulator \\ TXS .... transfer X to stack pointer \\ TYA .... transfer Y to accumulator \\ \\ Registers: \\ PC .... program counter (16 bit) \\ AC .... accumulator (8 bit) \\ X .... X register (8 bit) \\ Y .... Y register (8 bit) \\ SR .... status register [NV-BDIZC] (8 bit) \\ SP .... stack pointer (8 bit) \\ \\ SR Flags (bit 7 to bit 0): \\ N .... Negative \\ V .... Overflow \\ - .... ignored \\ B .... Break \\ D .... Decimal (use BCD for arithmetics) \\ I .... Interrupt (IRQ disable) \\ Z .... Zero \\ C .... Carry ====Branches==== Branch instructions are in cycles, it depends on the bits of flag when the op-code is runed. Below is the branch instruction sets and hex code \\ MNEMONIC -------------------- HEX \\ BPL (Branch on PLus) --- $10 \\ BMI (Branch on MInus) -- $30 \\ BVC (Branch on oVerflow Clear)- $50 \\ BVS (Branch on oVerflow Set) - $70 \\ BCC (Branch on Carry Clear) - $90 \\ BCS (Branch on Carry Set) - $B0 \\ BNE (Branch on Not Equal) - $D0 \\ BEQ (Branch on EQual) - $F0 ====Shift==== ====Mutiplex==== XST supports different description styles for multiplexers, such as If-Then-Else or Case. When writing MUXs, using a Case statement, and you do not specify all values of the selector, you may get latches instead of a multiplexer. ===== Notes ===== ==== Memory ==== === memory() - Constructor === memory::memory() { for (int address = 0; address < 65536; address++) { for (int bit = 1; bit < 9; bit++) mem[address].set(0, bit); } } A nested for loop cycles through each memory address and as it cycles through each one, sets each bit in the address to 0. === write(int, reg6502) === void memory::write(int address, reg6502 value) { for (bit = 1; bit < 9; bit++) mem[address].set(value.getQ(bit), bit); } A single for loop gets the value of bit n from one reg6502 object and sets the same bit in a memory address to that value. === read(int) === reg6502 memory::read(int address) { return mem[address]; } returns the value stored at address n. === dump() === void memory::dump() { for (int address = 0; address < 65536; address++) { for (int bit = 1; bit < 9; bit++) printf("%d", mem[address].getQ(bit)); printf("\n"); } } A nested for loop, first, cycles through each address and then prints out all 8 bits of that address. Thus printing out every value stored in memory. ==== Registers ==== === Accumulator (A Register) === 8 bit primary register. Just about any arithmetic operations will be done in here, along with local operations. When two numbers are added together, the first number is pulled from memory into the accumulator. The second number gets added to this and that result is then stored in accumulator.The accumulator has more addressing modes than any of the other 6502 registers. This is because the accumulator is the 6502's primary register. === Processor Status (P Register) === An 8 bit register which represent flags which describe the status of the processor. This contains the following flags: ^ Flag ^ When Set ^ Bit ^ | n (negative) | 1 = Negative | 7 | | v (overflow) | 1 = Overflow | 6 | | b (break instruction) | 1 = Break caused an interrupt | 4 | | d (decimal mode) | 1 = Decimal Mode on | 3 | | i (IRQ Disable) | 1 = IRQ Disabled | 2 | | z (zero) | 1 = Result of Zero | 1 | | c (carry) | 1 = Carry | 0 | === X and Y Index Registers === These are the general purpose registers. With a single instruction the x and y registers can be incremented or decremented. This makes them ideal for being used as loop counters, moving memory, or accessing successive table locations. === Stack Pointer (S Register) === Data structure implemented in hardware known as a stack. The stack in the 6502 is limited to 256 bytes. This is located in memory between $100 and $1FF. These memory locations are accessed via push and pull instructions. A push instructions takes data from a specified register and adds it to a location on the stack pointed to by the stack pointer. === Program Counter (PC Register) === The Program Counter contains the address of the next byte in the instruction sequence. When a program begins its execution, the program counter is set to the program's entry point, the address where the program was loaded. ==== Addressing Modes ==== The 6502 has 14 different addressing modes: ^ Addressing Mode ^ Opcode ^ Operand ^ | Implied | Dex | | | Accumulator | ASL | A | | Immediate | LDA | #55 | | Absolute | LDA | $2000 | | Program Counter Relative | BEQ | LABEL12 | | Stack | PHA | | | Zero Stack | LDA | $81 | | Absolute Indexed With X | LDA | $2000,X | | Absolute Indexed With Y | LDA | $2000,Y | | Zero Page Indexed With X | LDA | $55,X | | Zero Page Indexed With Y | LDA | $55,Y | | Absolute Indirect | JMP | ($1020) | | Zero Page Indirect Indexed With Y (Postindexed) | LDA | ($55),Y | | Zero Page Indexed Indirect With X (Preindexed) | LDA | ($55,X) | Combining these addressing modes with the 6502's 56 operation mnemonics, gives a total of 151 possible programming instructions. ==== Instructions ==== ^ Mnemonic ^ Description ^ | LDA | load the accumulator | | LDX | load X register | | LDY | load Y register | | STA | store the accumulator | | STX | store X register | | STY | store Y register | | --- | ---------------- | | PHA | push the accumulator | | PHP | push the status registers | | PHX | push the X index register | | PHY | push the Y index register | | PHB | push data bank register | | PHK | push program bank register | | PHD | push direct page register | ======ToDo======== ======Who is doing what====== * bh011695 - ADD Instruction * afassett ====== Documentation ====== ====== 0x0A Arithmetic Shift Left ====== #include"r6502.h" void ins0x0A(reg6502& A, reg6502& status) { bool isNonZero = false; status.set(A.getQ(7), CARRY); First we create a boolean variable to check the result if the result of the shift is 0. Then we shift the first bit in the accumulator to the carry in the status register. for (int x = 6; x >= 0; x--) { A.set(A.getQ(x), x + 1); isNonZero = isNonZero | A.getQ(x); } Next we use a for loop to shift each bit to it's immediate left and then check the bit for a zero. if (isNonZero == true) status.set(false, ZERO); else status.set(true, ZERO); We do our check for zero in the accumulator and if we find zero turn on the ZERO flag. if (A.getQ(NEGATIVE) == true) status.set(true, NEGATIVE); else status.set(false, NEGATIVE); } Now we check the sign bit and set the NEGATIVE flag accordingly. ====== 0x69 Instruction Add with Carry ====== bool isNonZero = false; bool Asign = A.getQ(NEGATIVE); reg6502 value; int carry; Code declarations. tmp is used to check for 0s in all 8 bits. tmp is ORed with every bit in the accumulator. If there is a one somewhere in the accumulator, it will be the result of the OR operation, and then never change. So if tmp is set to true, the number is non-zero. Asign is the value of the high bit in the accumulator. value is a generic register. carry is used as the carry out of the full adder. for (int x = 0; x < 8; x++) { adder.set(A.getQ(x), value.getQ(x), carry); A.set(adder.getSum()); carry = adder.getCarryOut(); isNonZero = isNonZero | A.getQ(x); } The for loop goes through the accumulator and value, sending each bit from both as inputs to the full adder along with a carry out. The addition is carried out and the last line in the for loop performs the OR operation to check for a zero value in the accumulator. if (isNonZero == true) status.set(false, ZERO) else status.set(true, ZERO); After the OR operation, if isNonZero == true the zero flag in the status register is set. if (A.getQ(NEGATIVE) == true) status.set(true, NEGATIVE); else status.set(false, NEGATIVE); It checks the sign bit of the accumulator for 1. If it finds one, the negative flag in the status register is set. if (carry == true) status.set(true, CARRY); else status.set(false, CARRY); Checks the result of the final carry for a 1. If it finds ones, the carry bit in the status register is set. if (Asign == true && value.getQ(NEGATIVE) == true) { if (A.getQ(NEGATIVE) == true) status.set(false, OVERFLOW); } else if (Asign == false && value.getQ(NEGATIVE) == false) { if (A.getQ(NEGATIVE) == false) status.set(false, OVERFLOW); } else status.set(true, OVERFLOW); It, first, checks the sign of the two input values. If they are the same sign, and the result contains the opposite sign, an overflow has occurred. Then the overflow bit of the status register is set. ====== 0x29 Instruction AND with accumulator immediate ====== Parameters: reg6502 A(accumulator), reg6502 status(status register), reg6502 immd (immediate value) Function heading/declarations void ins0x29() { bool isNonZero = false; //Checks for a value of zero bool immediate[8]; //immediate value A for loop is used to first AND each bit of the accumulator with each bit of the immediate value. The next line performs an OR between the tmp variable and the accumulator to check weather the accumulator contains a value of zero. for (int x = 0; x < 8; x++) { accum.set(accum.getQ(x) & immediate[x], x); isNonZero = isNonZero | accum.getQ(x); } Next we set the appropriate flags (N, Z). If isNonZero turns out to be true, because any non zero value in the accumulator will set isNonZero to true and keep it there, we set the zero flag in the status register to false. Otherwise we sat the zero flag to true. The negative flag is checked by looking at the sign bit of the accumulator and if the sign bit is true, set the negative flag to true, otherwise set it to false. if (isNonZero == false) status.set(true, ZERO); else status.set(false, ZERO); if (A.getQ(NEGATIVE) == true) status.set(true, NEGATIVE); else status.set(false, NEGATIVE); return; } ====== Code Examples ====== Here's a fairly primitive loop. There are much better ways but this is what I've discovered so far. .ORG $8000 ;Directive so the assembler knows where to start your program LDA #$00 ;Load 0x00 into the accumulator STA $50 ;Store the accumulator contents into memory location 0x50 LDA #$0A ;Load 0x0A into the accumulator STA $51 ;Store the accumulator contents into memory location 0x51 LOOP INC $50 ;Create a lable "LOOP" & Increment memory location 0x50 by one DEC $51 ;Decrement memory location 0x51 by one BNE LOOP ;Check zero flag and if not zero branch to LOOP BRK ;Stop program execution The problem with this is that the execution of the loop depends on whether or not the zero flag is tripped and not whether you've actually counted up to $0A. I'll see what other things I can figure out/dig up. ======References====== * http://www.visual6502.org/ * http://www.6502.org/ * http://www.youtube.com/watch?v=HW9AWBFH1sA - Reverse Engineering the 6502 * [[http://www.obelisk.demon.co.uk/6502/|6502 Introductions]] * http://www.wikipedia.org/wiki/MOS_Technology_6502 * http://www.6502.org/documents/datasheets/mos/ * http://www.6502.org/documents/datasheets/rockwell/ * http://nesdev.parodius.com/6502guid.txt - Some good assembly info * http://e-tradition.net/bytes/6502/6502_instruction_set.html * http://www.llx.com/~nparker/a2/opcodes.html * http://homepage.ntlworld.com/cyborgsystems/CS_Main/6502/6502.htm#BY_TBL