OSDN Git Service

adc bug fixed!!!!
[motonesemu/motonesemu.git] / emulator / 6502core.c
index 2dab6fb..4e9ecf3 100644 (file)
@@ -2,17 +2,23 @@
 #include <string.h>
 #include <libio.h>
 #include "tools.h"
+#include "6502core.h"
 
+/*
+ * 6502 little endian
+ * hi bit > low bit order
+ * but gcc generates low > hi order for bit field
+ * */
 struct status_reg {
-    unsigned int negative       :1;
-    unsigned int overflow       :1;
-    unsigned int researved      :1;
-    unsigned int break_mode     :1;
-    unsigned int decimal        :1;
-    unsigned int irq_disable    :1;
-    unsigned int zero           :1;
     unsigned int carry          :1;
-};
+    unsigned int zero           :1;
+    unsigned int irq_disable    :1;
+    unsigned int decimal        :1;
+    unsigned int break_mode     :1;
+    unsigned int researved      :1;
+    unsigned int overflow       :1;
+    unsigned int negative       :1;
+} __attribute__ ((packed));
 
 struct cpu_6502 {
     unsigned char acc;
@@ -45,22 +51,20 @@ typedef int (handler_6502_t) (void);
  * 
  **/
 
-#define ADDR_MODE_ZP        0
-#define ADDR_MODE_ZP_X      1
-#define ADDR_MODE_ZP_Y      2
-#define ADDR_MODE_ABS       3
-#define ADDR_MODE_ABS_X     4
-#define ADDR_MODE_ABS_Y     5
-#define ADDR_MODE_IND       6
-#define ADDR_MODE_IMP       7
-#define ADDR_MODE_ACC       8
-#define ADDR_MODE_IMM       9
-#define ADDR_MODE_REL       10
-#define ADDR_MODE_INDEX_INDIR       11
-#define ADDR_MODE_INDIR_INDEX       12
-
-#define N_BIT8      0x80
-#define N_BIT16     0x8000
+#define N_BIT      0x80
+#define V_BIT      0x40
+
+//stack pointer base address
+#define STACK_BASE  0x100
+
+
+/* cycle check must be cleared on release  */
+#define cycle_check
+
+
+#define NMI_VECTOR        0xFFFA
+#define RESET_VECTOR      0xFFFC
+#define IRQ_VECTOR        0xFFFE
 
 struct opcode_map {
     unsigned char   opcode;
@@ -74,16 +78,23 @@ struct opcode_map {
 
 static struct cpu_6502 cpu_reg;
 static struct opcode_map *current_inst;
+
+//exec index is the cycle consumed for execution only.
+//cpu total cycle is current_exec_index + 1 because 
+//fetch cycle uses 1 cycle prior to the execution.
 static int current_exec_index;
 static int exec_done;
+static int intr_done;
 
 unsigned char load_memory(unsigned short addr);
 unsigned short load_addr(unsigned short addr, int cycle);
+void store_memory(unsigned short addr, unsigned char data);
 
 unsigned char get_cpu_data_buf(void);
 void set_cpu_data_buf(unsigned char data);
 unsigned short get_cpu_addr_buf(void);
 void set_cpu_addr_buf(unsigned short addr);
+int get_clock_cnt(void);
 
 int func_ADC(void);
 int func_AND(void);
@@ -154,11 +165,17 @@ struct opcode_map opcode_list [255] = {
 
 
 
-static int addr_mode_exec(int *done) {
+/*
+ * load from memory in various addressing mode.
+ * cpu_data_buf has the output value.
+ *
+ *
+ * */
+static int load_addr_mode(int *done) {
     switch (current_inst->addr_mode) {
         case ADDR_MODE_ACC:
         case ADDR_MODE_IMP:
-            //accumulator/implied doesn't need memory operation.
+            //not supported.
             return FALSE;
 
         case ADDR_MODE_IMM:
@@ -185,6 +202,14 @@ static int addr_mode_exec(int *done) {
             }
             break;
 
+            /*
+             * Indexed zero page
+             * Wraparound is used when performing the addition so 
+             * the address of the data will always be
+             * in zero page. For example, if the operand is $FF and 
+             * the X register contains $01 the address
+             * of the data will be $0000, not $0100.
+             * */
         case ADDR_MODE_ZP_X:
             //zp indexed with x takes three cycles.
             if (current_exec_index == 0) {
@@ -198,7 +223,7 @@ static int addr_mode_exec(int *done) {
                 return TRUE;
             }
             else if (current_exec_index == 2) {
-                unsigned short zp = get_cpu_data_buf();
+                unsigned char zp = get_cpu_data_buf();
                 load_memory(zp);
                 goto addr_mode_done;
             }
@@ -217,7 +242,7 @@ static int addr_mode_exec(int *done) {
                 return TRUE;
             }
             else if (current_exec_index == 2) {
-                unsigned short zp = get_cpu_data_buf();
+                unsigned char zp = get_cpu_data_buf();
                 load_memory(zp);
                 goto addr_mode_done;
             }
@@ -260,9 +285,9 @@ static int addr_mode_exec(int *done) {
                     unsigned short addr = get_cpu_addr_buf();
                     unsigned short hi_8, added_hi_8;
 
-                    hi_8 = addr >> 8;
+                    hi_8 = (addr >> 8);
                     addr += cpu_reg.x;
-                    added_hi_8 = addr >> 8;
+                    added_hi_8 = (addr >> 8);
 
                     if (hi_8 == added_hi_8) {
                         load_memory(addr);
@@ -270,7 +295,7 @@ static int addr_mode_exec(int *done) {
                     }
 
                     //load value in the next cycle.
-                    set_cpu_data_buf(addr);
+                    set_cpu_addr_buf(addr);
                     return TRUE;
                 }
                 else {
@@ -282,7 +307,7 @@ static int addr_mode_exec(int *done) {
             }
             else if (current_exec_index == 3) {
                 if (current_inst->cycle_aux) {
-                    unsigned short addr = get_cpu_data_buf();
+                    unsigned short addr = get_cpu_addr_buf();
                     load_memory(addr);
                     goto addr_mode_done;
                 }
@@ -308,9 +333,9 @@ static int addr_mode_exec(int *done) {
                     unsigned short addr = get_cpu_addr_buf();
                     unsigned short hi_8, added_hi_8;
 
-                    hi_8 = addr >> 8;
+                    hi_8 = (addr >> 8);
                     addr += cpu_reg.y;
-                    added_hi_8 = addr >> 8;
+                    added_hi_8 = (addr >> 8);
 
                     if (hi_8 == added_hi_8) {
                         load_memory(addr);
@@ -318,7 +343,7 @@ static int addr_mode_exec(int *done) {
                     }
 
                     //load value in the next cycle.
-                    set_cpu_data_buf(addr);
+                    set_cpu_addr_buf(addr);
                     return TRUE;
                 }
                 else {
@@ -330,7 +355,7 @@ static int addr_mode_exec(int *done) {
             }
             else if (current_exec_index == 3) {
                 if (current_inst->cycle_aux) {
-                    unsigned short addr = get_cpu_data_buf();
+                    unsigned short addr = get_cpu_addr_buf();
                     load_memory(addr);
                     goto addr_mode_done;
                 }
@@ -338,8 +363,89 @@ static int addr_mode_exec(int *done) {
             break;
 
         case ADDR_MODE_INDEX_INDIR:
+            //Zero Page Indexed Indirect: (zp,x) takes 5 cycles
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char zp = get_cpu_data_buf();
+                zp += cpu_reg.x;
+                set_cpu_data_buf(zp);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr, 1);
+                //save addr in the temporary buffer.
+                set_cpu_data_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr + 1, 2);
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                load_memory(get_cpu_addr_buf());
+                goto addr_mode_done;
+            }
+            break;
+
         case ADDR_MODE_INDIR_INDEX:
-#warning zp indexed indirect, zp indirect indexed must be reworked!!
+            //Zero Page Indirect Indexed with Y: (zp),y takes 4 cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr, 1);
+                //save addr in the temporary buffer.
+                set_cpu_data_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr + 1, 2);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                if (current_inst->cycle_aux) {
+                    //Add one cycle if indexing across page boundary
+                    unsigned short addr = get_cpu_addr_buf();
+                    unsigned short hi_8, added_hi_8;
+
+                    hi_8 = (addr >> 8);
+                    addr += cpu_reg.y;
+                    added_hi_8 = (addr >> 8);
+
+                    if (hi_8 == added_hi_8) {
+                        load_memory(addr);
+                        goto addr_mode_done;
+                    }
+
+                    //load value in the next cycle.
+                    set_cpu_addr_buf(addr);
+                    return TRUE;
+                }
+                else {
+                    unsigned short addr = get_cpu_addr_buf();
+                    addr += cpu_reg.y;
+                    load_memory(addr);
+                    goto addr_mode_done;
+                }
+            }
+            else if (current_exec_index == 4) {
+                if (current_inst->cycle_aux) {
+                    unsigned short addr = get_cpu_addr_buf();
+                    load_memory(addr);
+                    goto addr_mode_done;
+                }
+            }
+            break;
 
         default:
             return FALSE;
@@ -351,236 +457,1634 @@ addr_mode_done:
     return TRUE;
 }
 
-int func_ADC(void) {
-    return FALSE;
-}
-
-int func_AND(void) {
-    return FALSE;
-}
-
-int func_ASL(void) {
-    return FALSE;
-}
-
-int func_BCC(void) {
-    return FALSE;
-}
+/*
+ * store into memory in various addressing mode.
+ * */
+static int store_addr_mode(unsigned char data, int *done) {
+    switch (current_inst->addr_mode) {
+        case ADDR_MODE_ACC:
+        case ADDR_MODE_IMP:
+        case ADDR_MODE_IMM:
+        case ADDR_MODE_REL:
+            //not supported.
+            return FALSE;
 
-int func_BCS(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ZP:
+            //zp takes two cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned short zp = get_cpu_data_buf();
+                store_memory(zp, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BEQ(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ZP_X:
+            //zp indexed with x takes three cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char imm = get_cpu_data_buf();
+                set_cpu_data_buf(imm + cpu_reg.x);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char zp = get_cpu_data_buf();
+                store_memory(zp, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BIT(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ZP_Y:
+            //zp indexed with y takes three cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char imm = get_cpu_data_buf();
+                set_cpu_data_buf(imm + cpu_reg.y);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char zp = get_cpu_data_buf();
+                store_memory(zp, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BMI(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ABS:
+            //takes three cycles.
+            if (current_exec_index == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned short addr = get_cpu_addr_buf();
+                store_memory(addr, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BNE(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ABS_X:
+            //abs indexed with x takes 4 cycles.
+            if (current_exec_index == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned short addr = get_cpu_addr_buf();
+                addr += cpu_reg.x;
+                set_cpu_addr_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned short addr = get_cpu_addr_buf();
+                store_memory(addr, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BPL(void) {
-    return FALSE;
-}
+        case ADDR_MODE_ABS_Y:
+            if (current_exec_index == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned short addr = get_cpu_addr_buf();
+                addr += cpu_reg.y;
+                set_cpu_addr_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned short addr = get_cpu_addr_buf();
+                store_memory(addr, data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BRK(void) {
-    return FALSE;
-}
+        case ADDR_MODE_INDEX_INDIR:
+            //Zero Page Indexed Indirect: (zp,x) takes 5 cycles
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char zp = get_cpu_data_buf();
+                zp += cpu_reg.x;
+                set_cpu_data_buf(zp);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr, 1);
+                //save addr in the temporary buffer.
+                set_cpu_data_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr + 1, 2);
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                store_memory(get_cpu_addr_buf(), data);
+                goto addr_mode_done;
+            }
+            break;
 
-int func_BVC(void) {
-    return FALSE;
-}
+        case ADDR_MODE_INDIR_INDEX:
+            //Zero Page Indirect Indexed with Y: (zp),y takes 5 cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr, 1);
+                //save addr in the temporary buffer.
+                set_cpu_data_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char addr = get_cpu_data_buf();
+                load_addr(addr + 1, 2);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned short addr = get_cpu_addr_buf();
+                addr += cpu_reg.y;
+                set_cpu_addr_buf(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                unsigned short addr = get_cpu_addr_buf();
+                store_memory(addr, data);
+                goto addr_mode_done;
+            }
+            break;
+
+        default:
+            return FALSE;
+    }
+    return FALSE;
+
+addr_mode_done:
+    *done = TRUE;
+    return TRUE;
+}
+
+/*
+ * for inc/dec/shift operation
+ * */
+static int memory_to_memory(int *do_operation, int *done) {
+    switch (current_inst->addr_mode) {
+        case ADDR_MODE_ACC:
+        case ADDR_MODE_IMP:
+        case ADDR_MODE_IMM:
+        case ADDR_MODE_REL:
+        case ADDR_MODE_ZP_Y:
+        case ADDR_MODE_ABS_Y:
+        case ADDR_MODE_INDEX_INDIR:
+        case ADDR_MODE_INDIR_INDEX:
+            //not supported.
+            return FALSE;
+
+        case ADDR_MODE_ZP:
+            //zp takes 4 cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned short zp = get_cpu_data_buf();
+                load_memory(zp);
+                set_cpu_addr_buf(zp);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                //arithmetic operation here!!
+                //result is set in cpu_data_buf
+                *do_operation = TRUE;
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned short zp = get_cpu_addr_buf();
+                unsigned short data = get_cpu_data_buf();
+                store_memory(zp, data);
+                goto mm_done;
+            }
+            break;
+
+        case ADDR_MODE_ZP_X:
+            //zp indexed with x takes 5 cycles.
+            if (current_exec_index == 0) {
+                load_memory(cpu_reg.pc);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                unsigned char imm = get_cpu_data_buf();
+                set_cpu_data_buf(imm + cpu_reg.x);
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned char zp = get_cpu_data_buf();
+                load_memory(zp);
+                set_cpu_addr_buf(zp);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                //arithmetic operation here!!
+                //result is set in cpu_data_buf
+                *do_operation = TRUE;
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                unsigned char zp = get_cpu_addr_buf();
+                unsigned short data = get_cpu_data_buf();
+                store_memory(zp, data);
+                goto mm_done;
+            }
+            break;
+
+        case ADDR_MODE_ABS:
+            //takes 5 cycles.
+            if (current_exec_index == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned short addr = get_cpu_addr_buf();
+                load_memory(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                //arithmetic operation here!!
+                //result is set in cpu_data_buf
+                *do_operation = TRUE;
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                unsigned short addr = get_cpu_addr_buf();
+                unsigned short data = get_cpu_data_buf();
+                store_memory(addr, data);
+                goto mm_done;
+            }
+            break;
+
+        case ADDR_MODE_ABS_X:
+            //abs indexed with x takes 6 cycles.
+            if (current_exec_index == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (current_exec_index == 2) {
+                unsigned short abs = get_cpu_addr_buf();
+                set_cpu_addr_buf(abs + cpu_reg.x);
+                return TRUE;
+            }
+            else if (current_exec_index == 3) {
+                unsigned short addr = get_cpu_addr_buf();
+                load_memory(addr);
+                return TRUE;
+            }
+            else if (current_exec_index == 4) {
+                //arithmetic operation here!!
+                //result is set in cpu_data_buf
+                *do_operation = TRUE;
+                return TRUE;
+            }
+            else if (current_exec_index == 5) {
+                unsigned short addr = get_cpu_addr_buf();
+                unsigned short data = get_cpu_data_buf();
+                store_memory(addr, data);
+                goto mm_done;
+            }
+            break;
+
+        default:
+            return FALSE;
+    }
+    return FALSE;
+
+mm_done:
+    *done = TRUE;
+    return TRUE;
+}
+
+
+/*-------------   flag operation..  ---------------------*/
+
+static void set_zero(unsigned char data) {
+    if (data == 0)
+        cpu_reg.status.zero = 1;
+    else
+        cpu_reg.status.zero = 0;
+}
+
+static void set_negative(unsigned char data) {
+    if (data & N_BIT)
+        cpu_reg.status.negative = 1;
+    else
+        cpu_reg.status.negative = 0;
+}
+
+static void set_CMP_carry(unsigned char data, unsigned char cmp) {
+    if (data >= cmp)
+        cpu_reg.status.carry = 1;
+    else
+        cpu_reg.status.carry = 0;
+}
+
+static void set_BIT_overflow(unsigned char data) {
+    if (data & V_BIT)
+        cpu_reg.status.overflow = 1;
+    else
+        cpu_reg.status.overflow = 0;
+}
+
+/*
+ * c Set if unsigned overflow; cleared if valid unsigned result.
+ * */
+static void set_ADD_carry(unsigned char d1, unsigned char d2, unsigned char d3) {
+    unsigned short d1_short = d1;
+    unsigned short d2_short = d2;
+    unsigned short d3_short = d3;
+    if (d1_short + d2_short + d3_short > 0xff)
+        cpu_reg.status.carry = 1;
+    else
+        cpu_reg.status.carry = 0;
+}
+
+/*
+ * v Set if signed overflow; cleared if valid signed result.
+ * */
+static void set_ADD_overflow(char d1, char d2, char d3) {
+    short d1_short = d1;
+    short d2_short = d2;
+    short d3_short = d3;
+    if (d1_short + d2_short + d3_short > +127 || d1_short + d2_short + d3_short < -128 )
+        cpu_reg.status.overflow = 1;
+    else
+        cpu_reg.status.overflow = 0;
+}
+
+/*
+ * c Set if unsigned borrow not required; cleared if unsigned borrow.
+ * */
+static void set_SUB_carry(unsigned char d1, unsigned char d2, unsigned char d3) {
+    unsigned short d1_short = d1;
+    unsigned short d2_short = d2;
+    unsigned short d3_short = d3;
+
+    if (d1_short >=  d2_short + d3_short)
+        cpu_reg.status.carry = 1;
+    else
+        cpu_reg.status.carry = 0;
+}
+
+/*
+ * v Set if signed borrow not required; cleared if signed borrow.
+ * */
+static void set_SUB_overflow(char d1, char d2, char d3) {
+    short d1_short = d1;
+    short d2_short = d2;
+    short d3_short = d3;
+    if (d1_short >= d2_short + d3_short)
+        cpu_reg.status.overflow = 1;
+    else
+        cpu_reg.status.overflow = 0;
+}
+
+/*-------------   stack operation..  ---------------------*/
+//stack operation takes two cycles.
+static void push(unsigned char data) {
+    store_memory(STACK_BASE + cpu_reg.sp, data);
+    cpu_reg.sp--;
+}
+
+static unsigned char pop(void) {
+    cpu_reg.sp++;
+    return load_memory(STACK_BASE + cpu_reg.sp);
+}
+
+/*---------- instruction implementations.   -----------------*/
+
+/*
+ * Add Memory to Accumulator with Carry: ADC
+ * A + M + C -> A
+ * Flags: N, V, Z, C
+ * */
+int func_ADC(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char data;
+    unsigned char old_carry;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    data = get_cpu_data_buf();
+    old_carry = cpu_reg.status.carry;
+    //signed, unsigned overflow check.
+    set_ADD_carry(cpu_reg.acc, cpu_reg.status.carry, data);
+    set_ADD_overflow(cpu_reg.acc, cpu_reg.status.carry, data);
+    //add data with carry to accumurator.
+    cpu_reg.acc += data + old_carry;
+
+    // N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * AND Memory with Accumulator: AND
+ * A & M -> A
+ * Flags: N, Z
+ * */
+int func_AND(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char data;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    data = get_cpu_data_buf();
+    cpu_reg.acc = cpu_reg.acc & data;
+    //N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Arithmetic Shift Left One Bit: ASL
+ * C <- 7 6 5 4 3 2 1 0 <- 0
+ * Flags: N, Z, C
+ * */
+int func_ASL(void) {
+    int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
+    int ret;
+
+    if (current_inst->addr_mode == ADDR_MODE_ACC) {
+        unsigned char op_data = cpu_reg.acc;
+        //set carry flag from the pre-opration value.
+        cpu_reg.status.carry = ((op_data & 0x80) != 0);
+        cpu_reg.acc = (op_data << 1);
+        set_negative(cpu_reg.acc);
+        set_zero(cpu_reg.acc);
+        goto acc_done;
+    }
+    else {
+        ret = memory_to_memory(&operation, &done);
+        if (!ret)
+            return FALSE;
+
+        if (operation) {
+            unsigned char op_data = get_cpu_data_buf();
+            //set carry flag from the pre-opration value.
+            cpu_reg.status.carry = ((op_data & 0x80) != 0);
+            op_data = (op_data << 1);
+            set_cpu_data_buf(op_data);
+            return TRUE;
+        }
+    }
+
+    if (!done) 
+        return TRUE;
+
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+acc_done:
+    exec_done = TRUE;
+    return TRUE;
+}
+
+static int branch(unsigned char condition) {
+
+    if (current_exec_index == 0) {
+        int done = FALSE;
+        int ret;
+        ret = load_addr_mode(&done);
+        if (!ret)
+            return FALSE;
+
+        if (!condition) {
+            exec_done = TRUE;
+        }
+        return TRUE;
+    }
+    else if (current_exec_index == 1) {
+        //case branch
+        char rel = get_cpu_data_buf();
+        unsigned short addr = cpu_reg.pc;
+        unsigned short br_addr = cpu_reg.pc + rel;
+
+        if ((addr >> 8) == (br_addr >> 8)) {
+            //in page branch.
+            cpu_reg.pc = br_addr;
+            exec_done = TRUE;
+        }
+        else {
+            set_cpu_addr_buf(br_addr);
+        }
+        return TRUE;
+    }
+    else if (current_exec_index == 2) {
+        //cross page branch.
+        unsigned short br_addr = get_cpu_addr_buf();
+        cpu_reg.pc = br_addr;
+        exec_done = TRUE;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * Branch on Carry Clear: BCC
+ * Branch if C = 0
+ * Flags: none
+ * */
+int func_BCC(void) {
+    return branch(cpu_reg.status.carry == 0);
+}
+
+/*
+ *  Branch on Carry Set: BCS
+ *  Branch if C = 1
+ *  Flags: none
+ * */
+int func_BCS(void) {
+    return branch(cpu_reg.status.carry == 1);
+}
+
+/*
+ *  Branch on Result Zero: BEQ
+ *  Branch if Z = 1
+ *  Flags: none
+ * */
+int func_BEQ(void) {
+    return branch(cpu_reg.status.zero == 1);
+}
+
+/*
+ * Test Bits in Memory with Accumulator: BIT
+ * A & M
+ * Flags: N = M7, V = M6, Z
+ * */
+int func_BIT(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char cmp;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cmp = get_cpu_data_buf();
+    //cmp N/V/Z flags set.
+    /*
+     * The BIT instruction really performs two distinct operations.
+     * First, it directly transfers the highest and next to highest 
+     * bits of the memory operand (that is, seven and six if m
+     * = 1, or fifteen and fourteen if m = 0) to the n and v flags. 
+     * It does this without modifying the value in the
+     * accumulator, making it useful for testing the sign of a value 
+     * in memory without loading it into one of the
+     * registers. An exception to this is the case where the immediate 
+     * addressing mode is used with the BIT
+     * instruction: since it serves no purpose to test the bits of a 
+     * constant value, the n and v flags are left unchanged in
+     * this one case.
+     * BITs second operation is to logically AND the value of the 
+     * memory operand with the value in the
+     * accumulator, conditioning the z flag in the status register to 
+     * reflect whether or not the result of the ANDing was
+     * zero or not, but without storing the result in the accumulator
+     * */
+    //6502 core supports zp and abs only
+    set_negative(cmp);
+    set_BIT_overflow(cmp);
+    set_zero(cpu_reg.acc & cmp);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ *  Branch on Result Minus: BMI
+ *  Branch if N = 1
+ *  Flags: none
+ * */
+int func_BMI(void) {
+    return branch(cpu_reg.status.negative == 1);
+}
+
+/*
+ * Branch on Result not Zero: BNE
+ * Branch if Z = 0
+ * Flags: none
+ * */
+int func_BNE(void) {
+    return branch(cpu_reg.status.zero == 0);
+}
+
+/*
+ * Branch on Result Plus: BPL
+ * Branch if N = 0
+ * Flags: none
+ * */
+int func_BPL(void) {
+    return branch(cpu_reg.status.negative == 0);
+}
+
+int func_BRK(void) {
+    return FALSE;
+}
+
+/*
+ * Branch on Overflow Clear: BVC
+ * Branch if V = 0
+ * Flags: none
+ * */
+int func_BVC(void) {
+    return branch(cpu_reg.status.overflow == 0);
+}
+
+/*
+ * Branch on Overflow Set: BVS
+ * Branch if V = 1
+ * Flags: none
+ * */
+int func_BVS(void) {
+    return branch(cpu_reg.status.overflow == 1);
+}
+
+/*
+ * Clear Carry Flag: CLC
+ * 0 -> C
+ * Flags: C = 0
+ * */
+int func_CLC(void) {
+    cpu_reg.status.carry = 0;
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Clear Decimal Mode: CLD
+ * 0 -> D
+ * Flags: D = 0
+ *
+ * NOTE: Decimal mode is not implemented on NES core.
+ * */
+int func_CLD(void) {
+    cpu_reg.status.decimal = 0;
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Clear Interrupt Disable Status: CLI
+ * 0 -> I
+ * Flags: I = 0
+ * */
+int func_CLI(void) {
+    cpu_reg.status.irq_disable = 0;
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Clear Overflow Flag: CLV
+ * 0 -> V
+ * Flags: V = 0
+ * */
+int func_CLV(void) {
+    cpu_reg.status.overflow = 0;
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Compare Memory and Accumulator: CMP
+ * A - M
+ * Flags: N, Z, C
+ * */
+int func_CMP(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char cmp;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cmp = get_cpu_data_buf();
+    //cmp C/N/Z flags set.
+    set_CMP_carry(cpu_reg.acc, cmp);
+    set_negative(cpu_reg.acc - cmp);
+    set_zero(cpu_reg.acc - cmp);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Compare Memory and Index X: CPX
+ * X - M
+ * Flags: N, Z, C
+ * */
+int func_CPX(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char cmp;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cmp = get_cpu_data_buf();
+    //cmp C/N/Z flags set.
+    set_CMP_carry(cpu_reg.x, cmp);
+    set_negative(cpu_reg.x - cmp);
+    set_zero(cpu_reg.x - cmp);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Compare Memory with Index Y: CPY
+ * Y - M
+ * Flags: N, Z, C
+ * */
+int func_CPY(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char cmp;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cmp = get_cpu_data_buf();
+    //cmp C/N/Z flags set.
+
+/*
+ * Subtract the data located at the effective address specified 
+ * by the operand from the contents of the Y
+ * register, setting the carry, zero, and negative flags based 
+ * on the result, but without altering the contents of either
+ * the memory location or the register. The comparison is of 
+ * unsigned values only (expect for signed comparison
+ * for equality).
+ * */
+    set_CMP_carry(cpu_reg.y, cmp);
+    set_negative(cpu_reg.y - cmp);
+    set_zero(cpu_reg.y - cmp);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * Decrement Memory by One: DEC
+ * M - 1 -> M
+ * Flags: N, Z
+ * */
+int func_DEC(void) {
+    int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
+    int ret;
+
+    ret = memory_to_memory(&operation, &done);
+    if (!ret)
+        return FALSE;
+
+    if (operation) {
+        unsigned char op_data = get_cpu_data_buf();
+        op_data--;
+        set_cpu_data_buf(op_data);
+        return TRUE;
+    }
+
+    if (!done) 
+        return TRUE;
+
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+    exec_done = TRUE;
+    return TRUE;
+}
 
-int func_BVS(void) {
-    return FALSE;
+/*
+ * Decrement Index X by One: DEX
+ * X - 1 -> X
+ * Flags: N, Z
+ * */
+int func_DEX(void) {
+    cpu_reg.x--;
+
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.x);
+    set_zero(cpu_reg.x);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CLC(void) {
-    return FALSE;
+/*
+ * Decrement Index Y by One: DEY
+ * Y - 1 -> Y
+ * Flags: N, Z
+ * */
+int func_DEY(void) {
+    cpu_reg.y--;
+
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.y);
+    set_zero(cpu_reg.y);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CLD(void) {
-    return FALSE;
+/*
+ * Exclusive-OR Memory with Accumulator: EOR
+ * A ^ M -> A
+ * Flags: N, Z
+ * */
+int func_EOR(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char data;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    data = get_cpu_data_buf();
+    cpu_reg.acc = cpu_reg.acc ^ data;
+    //N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CLI(void) {
-    return FALSE;
+/*
+ * Increment Memory by One: INC
+ * M + 1 -> M
+ * Flags: N, Z
+ * */
+int func_INC(void) {
+    int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
+    int ret;
+
+    ret = memory_to_memory(&operation, &done);
+    if (!ret)
+        return FALSE;
+
+    if (operation) {
+        unsigned char op_data = get_cpu_data_buf();
+        op_data++;
+        set_cpu_data_buf(op_data);
+        return TRUE;
+    }
+
+    if (!done) 
+        return TRUE;
+
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CLV(void) {
-    return FALSE;
+/*
+ * Increment Index X by One: INX
+ * X + 1 -> X
+ * Flags: N, Z
+ * */
+int func_INX(void) {
+    cpu_reg.x++;
+
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.x);
+    set_zero(cpu_reg.x);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CMP(void) {
-    return FALSE;
+int func_INY(void) {
+    cpu_reg.y++;
+
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.y);
+    set_zero(cpu_reg.y);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_CPX(void) {
+static int jmp(int cycle, int *done) {
+    switch (current_inst->addr_mode) {
+        case ADDR_MODE_ABS:
+            //takes 2 cycles.
+            if (cycle == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (cycle == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                *done = TRUE;
+                return TRUE;
+            }
+            break;
+
+        case ADDR_MODE_IND:
+            //takes 4 cycles.
+            if (cycle == 0) {
+                load_addr(cpu_reg.pc, 1);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (cycle == 1) {
+                load_addr(cpu_reg.pc, 2);
+                cpu_reg.pc++;
+                return TRUE;
+            }
+            else if (cycle == 2) {
+                load_memory(get_cpu_addr_buf());
+                return TRUE;
+            }
+            else if (cycle == 3) {
+                unsigned char low, hi;
+                unsigned short addr;
+                low = get_cpu_data_buf();
+                hi = load_memory(get_cpu_addr_buf() + 1);
+                addr = (hi << 8) | low;
+                set_cpu_addr_buf(addr);
+                *done = TRUE;
+                return TRUE;
+            }
+            break;
+
+        default:
+            return FALSE;
+    }
     return FALSE;
 }
 
-int func_CPY(void) {
-    return FALSE;
+/*
+ * Jump to New Location: JMP
+ * Jump to new location
+ * Flags: none
+ * */
+int func_JMP(void) {
+    int done = FALSE;
+    int ret;
+
+    ret = jmp(current_exec_index, &done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cpu_reg.pc = get_cpu_addr_buf();
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_DEC(void) {
+/*
+ * Jump to New Location Saving Return Address: JSR
+ * Jump to Subroutine
+ * Flags: none
+ * */
+int func_JSR(void) {
+    int done = FALSE;
+
+    //cycle 1
+    if (current_exec_index == 0) {
+        //save return addr(-1) hi.
+        //pc + 1 => jsr abslo abshi - 1
+        push((cpu_reg.pc + 1) >> 8);
+        return TRUE;
+    }
+    //cycle 2
+    else if (current_exec_index == 1) {
+        //save return addr(-1) low.
+        push(cpu_reg.pc + 1);
+        return TRUE;
+    }
+    //cycle 3,4
+    else if (current_exec_index < 4) {
+        return jmp(current_exec_index - 2, &done);
+    }
+    //cycle 5
+    else if (current_exec_index == 4) {
+        cpu_reg.pc = get_cpu_addr_buf();
+        exec_done = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
 
-int func_DEX(void) {
-    return FALSE;
+/*
+ * Load Accumulator with Memory: LDA
+ * M -> A
+ * Flags: N, Z
+ * */
+int func_LDA(void) {
+    int done = FALSE;
+    int ret;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cpu_reg.acc = get_cpu_data_buf();
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_DEY(void) {
-    return FALSE;
+/*
+ * Load Index X with Memory: LDX
+ * M -> X
+ * Flags: N, Z
+ * */
+int func_LDX(void) {
+    int done = FALSE;
+    int ret;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cpu_reg.x = get_cpu_data_buf();
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.x);
+    set_zero(cpu_reg.x);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_EOR(void) {
-    return FALSE;
+/*
+ * Load Index Y with Memory: LDY
+ * M -> Y
+ * Flags: N, Z
+ * */
+int func_LDY(void) {
+    int done = FALSE;
+    int ret;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    cpu_reg.y = get_cpu_data_buf();
+    //ldx N/Z flags set.
+    set_negative(cpu_reg.y);
+    set_zero(cpu_reg.y);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_INC(void) {
-    return FALSE;
+/*
+ * Logical Shift Right One Bit: LSR
+ * 0 -> 7 6 5 4 3 2 1 0 -> C
+ * Flags: N, Z, C
+ * */
+int func_LSR(void) {
+    int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
+    int ret;
+
+    if (current_inst->addr_mode == ADDR_MODE_ACC) {
+        unsigned char op_data = cpu_reg.acc;
+        //set carry flag from the pre-opration value.
+        cpu_reg.status.carry = (op_data & 0x01);
+        cpu_reg.acc = (op_data >> 1);
+        set_negative(cpu_reg.acc);
+        set_zero(cpu_reg.acc);
+        goto acc_done;
+    }
+    else {
+        ret = memory_to_memory(&operation, &done);
+        if (!ret)
+            return FALSE;
+
+        if (operation) {
+            unsigned char op_data = get_cpu_data_buf();
+            //set carry flag from the pre-opration value.
+            cpu_reg.status.carry = (op_data & 0x01);
+            op_data = (op_data >> 1);
+            set_cpu_data_buf(op_data);
+            return TRUE;
+        }
+    }
+
+    if (!done) 
+        return TRUE;
+
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+acc_done:
+    exec_done = TRUE;
+    return TRUE;
 }
 
-int func_INX(void) {
+int func_NOP(void) {
     return FALSE;
 }
 
-int func_INY(void) {
+/*
+ * OR Memory with Accumulator: ORA
+ * A | M -> A
+ * Flags: N, Z
+ * */
+int func_ORA(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char data;
+
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    data = get_cpu_data_buf();
+    cpu_reg.acc = cpu_reg.acc | data;
+    //N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
+}
+
+/*
+ * push takes 2 cycles.
+ * */
+static int push_op(unsigned char data, int *done) {
+    //cycle 1
+    if (current_exec_index == 0) {
+        push(data);
+        return TRUE;
+    }
+    //cycle 2
+    else if (current_exec_index == 1) {
+        //cycle 2 doesn't do enything.
+        *done = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
 
-int func_JMP(void) {
+/*
+ * Push Accumulator on Stack: PHA
+ * A -> S
+ * Flags: none
+ * */
+int func_PHA(void) {
+    int ret;
+    int done = FALSE;
+    ret = push_op(cpu_reg.acc, &done);
+    exec_done = done;
+    return ret;
+}
+
+int func_PHP(void) {
     return FALSE;
 }
 
-int func_JSR(void) {
+/*
+ * pull takes 3 cycles.
+ * */
+static int pull_op(int *done) {
+    //cycle 1
+    if (current_exec_index == 0) {
+        pop();
+        return TRUE;
+    }
+    //cycle 2
+    else if (current_exec_index == 1) {
+        //cycle 2 doesn't do enything.
+        return TRUE;
+    }
+    //cycle 3
+    else if (current_exec_index == 2) {
+        //cycle 3 caller must xfer data from cpu data buf
+        *done = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
 
 /*
- * Load Accumulator with Memory: LDA
- * M -> A
+ * Pull Accumulator from Stack: PLA
+ * S -> A
  * Flags: N, Z
  * */
-int func_LDA(void) {
+int func_PLA(void) {
+    int ret;
+    int done = FALSE;
+    ret = pull_op(&done);
+    if (done) {
+        cpu_reg.acc = get_cpu_data_buf();
+        set_negative(cpu_reg.acc);
+        set_zero(cpu_reg.acc);
+    }
+    exec_done = done;
+    return ret;
+}
+
+int func_PLP(void) {
     return FALSE;
 }
 
 /*
- * Load Index X with Memory: LDX
- * M -> X
- * Flags: N, Z
+ * Rotate Left One Bit: ROL
+ * C <- 7 6 5 4 3 2 1 0 <- C
+ * Flags: N, Z, C
  * */
-int func_LDX(void) {
+int func_ROL(void) {
     int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
     int ret;
 
-    ret = addr_mode_exec(&done);
-    if (!ret)
-        return FALSE;
+    if (current_inst->addr_mode == ADDR_MODE_ACC) {
+        unsigned char op_data = cpu_reg.acc;
+        unsigned char old_carry = cpu_reg.status.carry;
+
+        //set carry flag from the pre-opration value.
+        cpu_reg.status.carry = ((op_data & 0x80) != 0);
+        cpu_reg.acc = (op_data << 1);
+        if (old_carry)
+            cpu_reg.acc |= 0x01;
+        set_negative(cpu_reg.acc);
+        set_zero(cpu_reg.acc);
+        goto acc_done;
+    }
+    else {
+        ret = memory_to_memory(&operation, &done);
+        if (!ret)
+            return FALSE;
+
+        if (operation) {
+            unsigned char op_data = get_cpu_data_buf();
+            unsigned char old_carry = cpu_reg.status.carry;
+
+            //set carry flag from the pre-opration value.
+            cpu_reg.status.carry = ((op_data & 0x80) != 0);
+            op_data = (op_data << 1);
+            if (old_carry)
+                op_data |= 0x01;
+            set_cpu_data_buf(op_data);
+            return TRUE;
+        }
+    }
 
     if (!done) 
         return TRUE;
 
-    cpu_reg.x = get_cpu_data_buf();
-    //ldx N/Z flags set.
-    if (cpu_reg.x == 0)
-        cpu_reg.status.zero = 1;
-    if (cpu_reg.x & N_BIT8)
-        cpu_reg.status.negative = 1;
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+acc_done:
     exec_done = TRUE;
     return TRUE;
 }
 
 /*
- * Load Index Y with Memory: LDY
- * M -> Y
- * Flags: N, Z
+ * Rotate Right One Bit: ROR
+ * C -> 7 6 5 4 3 2 1 0 -> C
+ * Flags: N, Z, C
  * */
-int func_LDY(void) {
+int func_ROR(void) {
     int done = FALSE;
+    int operation = FALSE;
+    unsigned char data;
     int ret;
 
-    ret = addr_mode_exec(&done);
-    if (!ret)
-        return FALSE;
+    if (current_inst->addr_mode == ADDR_MODE_ACC) {
+        unsigned char op_data = cpu_reg.acc;
+        unsigned char old_carry = cpu_reg.status.carry;
+
+        //set carry flag from the pre-opration value.
+        cpu_reg.status.carry = (op_data & 0x01);
+        cpu_reg.acc = (op_data >> 1);
+        if (old_carry)
+            cpu_reg.acc |= 0x80;
+        set_negative(cpu_reg.acc);
+        set_zero(cpu_reg.acc);
+        goto acc_done;
+    }
+    else {
+        ret = memory_to_memory(&operation, &done);
+        if (!ret)
+            return FALSE;
+
+        if (operation) {
+            unsigned char op_data = get_cpu_data_buf();
+            unsigned char old_carry = cpu_reg.status.carry;
+
+            //set carry flag from the pre-opration value.
+            cpu_reg.status.carry = (op_data & 0x01);
+            op_data = (op_data >> 1);
+            if (old_carry)
+                op_data |= 0x80;
+            set_cpu_data_buf(op_data);
+            return TRUE;
+        }
+    }
 
     if (!done) 
         return TRUE;
 
-    cpu_reg.y = get_cpu_data_buf();
-    //ldx N/Z flags set.
-    if (cpu_reg.y == 0)
-        cpu_reg.status.zero = 1;
-    if (cpu_reg.y & N_BIT8)
-        cpu_reg.status.negative = 1;
+    // N/Z flags set.
+    data = get_cpu_data_buf();
+    set_negative(data);
+    set_zero(data);
+
+acc_done:
     exec_done = TRUE;
     return TRUE;
 }
 
-int func_LSR(void) {
-    return FALSE;
-}
-
-int func_NOP(void) {
+/*
+ * Return from Interrupt: RTI
+ * Return from Interrupt
+ * Flags: all
+ * */
+int func_RTI(void) {
+    //cycle 1
+    if (current_exec_index == 0) {
+        //pop statu reg.
+        pop();
+        return TRUE;
+    }
+    //cycle 2 
+    else if (current_exec_index == 1) {
+        unsigned char data;
+        //set status reg
+        data = get_cpu_data_buf();
+        memcpy(&cpu_reg.status, &data, sizeof(data));
+        //pop return addr low.
+        pop();
+        return TRUE;
+    }
+    //cycle 3
+    else if (current_exec_index == 2) {
+        //set return addr low.
+        set_cpu_addr_buf(get_cpu_data_buf());
+        //pop return addr hi.
+        pop();
+        return TRUE;
+    }
+    //cycle 4
+    else if (current_exec_index == 3) {
+        unsigned char hi, lo;
+        unsigned short addr;
+
+        //set return addr hi
+        lo = get_cpu_addr_buf();
+        hi = get_cpu_data_buf();
+        addr = (hi << 8) | lo;
+        set_cpu_addr_buf(addr);
+        return TRUE;
+    }
+    //cycle 5
+    else if (current_exec_index == 4) {
+        //set pc = addr
+        cpu_reg.pc = get_cpu_addr_buf();
+        exec_done = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
 
-int func_ORA(void) {
-    return FALSE;
-}
+/*
+ * Return from Subroutine: RTS
+ * Return from Subroutine
+ * Flags: none
+ * */
+int func_RTS(void) {
 
-int func_PHA(void) {
+    //cycle 1
+    if (current_exec_index == 0) {
+        //pop return addr low.
+        pop();
+        return TRUE;
+    }
+    //cycle 2 
+    else if (current_exec_index == 1) {
+        //set return addr low.
+        set_cpu_addr_buf(get_cpu_data_buf());
+        return TRUE;
+    }
+    //cycle 3
+    else if (current_exec_index == 2) {
+        //pop return addr hi.
+        pop();
+        return TRUE;
+    }
+    //cycle 4
+    else if (current_exec_index == 3) {
+        unsigned char hi, lo;
+        unsigned short addr;
+
+        //set return addr hi
+        lo = get_cpu_addr_buf();
+        hi = get_cpu_data_buf();
+        addr = (hi << 8) | lo;
+        set_cpu_addr_buf(addr);
+        return TRUE;
+    }
+    //cycle 5
+    else if (current_exec_index == 4) {
+        //set pc = addr + 1
+        cpu_reg.pc = get_cpu_addr_buf() + 1;
+        exec_done = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
 
-int func_PHP(void) {
-    return FALSE;
-}
+/*
+ * Subtract Memory from Accumulator with Borrow: SBC
+ * A - M - ~C -> A
+ * Flags: N, V, Z, C
+ * */
+int func_SBC(void) {
+    int done = FALSE;
+    int ret;
+    unsigned char data;
+    unsigned char c_comp;
 
-int func_PLA(void) {
-    return FALSE;
-}
+    ret = load_addr_mode(&done);
+    if (!ret)
+        return FALSE;
 
-int func_PLP(void) {
-    return FALSE;
-}
+    if (!done) 
+        return TRUE;
 
-int func_ROL(void) {
-    return FALSE;
-}
+    data = get_cpu_data_buf();
+    c_comp = (cpu_reg.status.carry == 0 ? 1 : 0);
 
-int func_ROR(void) {
-    return FALSE;
-}
+    //signed, unsigned overflow check.
+    set_SUB_carry(cpu_reg.acc, data, c_comp);
+    set_SUB_overflow(cpu_reg.acc, data, c_comp);
 
-int func_RTI(void) {
-    return FALSE;
-}
+    //subtract data with carry to accumurator.
+    cpu_reg.acc = cpu_reg.acc - data - c_comp;
 
-int func_RTS(void) {
-    return FALSE;
-}
+    // N/Z flags set.
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
 
-int func_SBC(void) {
-    return FALSE;
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Set Carry Flag: SEC
+ * 1 -> C
+ * Flags: C = 1
+ * */
 int func_SEC(void) {
-    return FALSE;
+    cpu_reg.status.carry = 1;
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Set Decimal Mode: SED
+ * 1 -> D
+ * Flags: D = 1
+ *
+ * NOTE: decimal mode is not supported on NES core
+ * */
 int func_SED(void) {
+    cpu_reg.status.decimal = 1;
+    fprintf(stderr, "decimal mode is not supported!!!\n");
     return FALSE;
 }
 
@@ -593,24 +2097,94 @@ int func_SEI(void) {
     return TRUE;
 }
 
+/*
+ * Store Accumulator in Memory: STA
+ * A -> M
+ * Flags: none
+ * */
 int func_STA(void) {
-    return FALSE;
+    int done = FALSE;
+    int ret;
+
+    ret = store_addr_mode(cpu_reg.acc, &done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Store Index X in Memory: STX
+ * X -> M
+ * Flags: none
+ * */
 int func_STX(void) {
-    return FALSE;
+    int done = FALSE;
+    int ret;
+
+    ret = store_addr_mode(cpu_reg.x, &done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Store Index Y in Memory: STY
+ * Y -> M
+ * Flags: none
+ * */
 int func_STY(void) {
-    return FALSE;
+    int done = FALSE;
+    int ret;
+
+    ret = store_addr_mode(cpu_reg.y, &done);
+    if (!ret)
+        return FALSE;
+
+    if (!done) 
+        return TRUE;
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Transfer Accumulator to Index X: TAX
+ * A -> X
+ * Flags: N, Z
+ * */
 int func_TAX(void) {
-    return FALSE;
+    cpu_reg.x = cpu_reg.acc;
+
+    set_negative(cpu_reg.x);
+    set_zero(cpu_reg.x);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/*
+ * Transfer Accumulator to Index Y: TAY
+ * A -> Y
+ * Flags: N, Z
+ * */
 int func_TAY(void) {
-    return FALSE;
+    cpu_reg.y = cpu_reg.acc;
+
+    set_negative(cpu_reg.y);
+    set_zero(cpu_reg.y);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
 /*
@@ -621,17 +2195,26 @@ int func_TAY(void) {
 int func_TSX(void) {
     cpu_reg.x = cpu_reg.sp;
 
-    if (cpu_reg.x & N_BIT8)
-        cpu_reg.status.negative = 1;
-    if (cpu_reg.x == 0)
-        cpu_reg.status.zero = 1;
+    set_negative(cpu_reg.x);
+    set_zero(cpu_reg.x);
 
     exec_done = TRUE;
     return TRUE;
 }
 
+/*
+ * Transfer Index X to Accumulator: TXA
+ * X -> A
+ * Flags: N, Z
+ * */
 int func_TXA(void) {
-    return FALSE;
+    cpu_reg.acc = cpu_reg.x;
+
+    set_negative(cpu_reg.acc);
+    set_zero(cpu_reg.acc);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
 /*
@@ -642,19 +2225,30 @@ int func_TXA(void) {
 int func_TXS(void) {
     cpu_reg.sp = cpu_reg.x;
 
-    if (cpu_reg.sp & N_BIT8)
-        cpu_reg.status.negative = 1;
-    if (cpu_reg.sp == 0)
-        cpu_reg.status.zero = 1;
+    set_negative(cpu_reg.sp);
+    set_zero(cpu_reg.sp);
 
     exec_done = TRUE;
     return TRUE;
 }
 
+/*
+ * Transfer Index Y to Accumulator: TYA
+ * Y -> A
+ * Flags: N, Z
+ * */
 int func_TYA(void) {
-    return FALSE;
+    cpu_reg.acc = cpu_reg.y;
+
+    set_negative(cpu_reg.y);
+    set_zero(cpu_reg.y);
+
+    exec_done = TRUE;
+    return TRUE;
 }
 
+/* ------------------     6502 execution..      ------------------- */
+
 /*
  * decode6502:
  * return execution cycle count
@@ -666,19 +2260,22 @@ int decode6502(unsigned char inst) {
         return FALSE;
     }
 
-    dprint("decode inst: %02x > %s, %d cycle, %d len\n", 
+    /*dprint("decode inst: %02x > %s, %d cycle, %d len\n", 
             inst, omap->mnemonic, omap->cycle, omap->inst_len);
+*/
 
     current_inst = omap;
-    current_exec_index = 0;
 
     return TRUE;
 }
+
 int test_and_set_exec(void) {
     int ret;
     ret = exec_done;
-    if (exec_done)
+    if (exec_done) {
         exec_done = FALSE;
+        current_exec_index = 0;
+    }
     return ret;
 }
 
@@ -686,6 +2283,107 @@ int execute6502(void) {
     int ret;
     ret = current_inst->func();
     current_exec_index++;
+
+#ifdef cycle_check
+    if (exec_done && (current_inst->cycle - 1 != current_exec_index)) {
+        if (current_inst->cycle_aux && (
+                    current_inst->addr_mode == ADDR_MODE_ABS_X ||
+                    current_inst->addr_mode == ADDR_MODE_ABS_Y ||
+                    current_inst->addr_mode == ADDR_MODE_INDIR_INDEX ) && 
+                (current_inst->cycle == current_exec_index)) {
+            ;
+        }
+        else if ((current_inst->addr_mode == ADDR_MODE_REL) && 
+                ((current_inst->cycle == current_exec_index) ||
+                (current_inst->cycle + 1 == current_exec_index ))) {
+            ;
+        }
+        else {
+            fprintf(stderr, "instruction cycle check error!!\n");
+            return FALSE;
+        }
+    }
+#endif
+
+    return ret;
+}
+
+int reset_exec6502(void) {
+    switch (current_exec_index++) {
+        case 0:
+            //step 1: load intvec low.
+            load_addr(RESET_VECTOR, 1);
+            return TRUE;
+        case 1:
+            //step 2: load intvec hi.
+            load_addr(RESET_VECTOR + 1, 2);
+            return TRUE;
+        case 2:
+            //step 3: set pc
+            cpu_reg.pc = get_cpu_addr_buf();
+            //set status flag
+            cpu_reg.status.decimal = 0;
+            cpu_reg.status.irq_disable = 1;
+            intr_done = TRUE;
+            return TRUE;
+    }
+    return FALSE;
+}
+
+int reset6502(void) {
+    current_exec_index = 0;
+    return reset_exec6502();
+}
+
+int nmi6502(void) {
+    dprint("nmi...\n");
+
+    //nmi6502 is always called when current instruction execution is done.
+    switch (current_exec_index++) {
+        case 0:
+            //first: push pc hi.
+            push(cpu_reg.pc >> 8);
+            return TRUE;
+        case 1:
+            //second: push pc low.
+            push(cpu_reg.pc);
+            return TRUE;
+        case 2:
+            {
+                //step 3, push status_reg
+                unsigned char stat;
+                memcpy(&stat, &cpu_reg.status, sizeof(stat));
+                push(stat);
+                return TRUE;
+            }
+        case 3:
+            //step 4: load intvec low.
+            load_addr(NMI_VECTOR, 1);
+            return TRUE;
+        case 4:
+            //step 5: load intvec hi.
+            load_addr(NMI_VECTOR + 1, 2);
+            return TRUE;
+        case 5:
+            //step 6: set pc
+            cpu_reg.pc = get_cpu_addr_buf();
+            //set status flag
+            cpu_reg.status.decimal = 0;
+            cpu_reg.status.irq_disable = 1;
+
+            intr_done = TRUE;
+            return TRUE;
+    }
+    return FALSE;
+}
+
+int test_and_set_intr(void) {
+    int ret;
+    ret = intr_done;
+    if (ret) {
+        current_exec_index = 0;
+        intr_done = FALSE;
+    }
     return ret;
 }
 
@@ -701,35 +2399,57 @@ void pc_move(int offset) {
     cpu_reg.pc += offset;
 }
 
+int init_6502core(void) {
+    memset(&cpu_reg, 0, sizeof(struct cpu_6502));
+    current_inst = NULL;
+    current_exec_index = 0;
+    exec_done = FALSE;
+    intr_done = FALSE;
+    return TRUE;
+}
+
+/* for debug.c */
+
 void dump_6502(int full) {
+    printf("\nclock: %09d\n", get_clock_cnt());
     if (full) 
         printf("6502 CPU registers:\n");
 
-    printf("pc:     %04x\n", cpu_reg.pc);
+    printf(" pc:     %04x\n", cpu_reg.pc);
     if (full) {
-        printf("acc:    %02x\n", cpu_reg.acc);
-        printf("x:      %02x\n", cpu_reg.x);
-        printf("y:      %02x\n", cpu_reg.y);
-        printf("sp:     %02x\n", cpu_reg.sp);
-        printf("status:\n");
-        printf(" negative:   %d\n", cpu_reg.status.negative);
-        printf(" overflow:   %d\n", cpu_reg.status.overflow);
-        printf(" break:      %d\n", cpu_reg.status.break_mode);
-        printf(" decimal:    %d\n", cpu_reg.status.decimal);
-        printf(" irq:        %d\n", cpu_reg.status.irq_disable);
-        printf(" zero:       %d\n", cpu_reg.status.zero);
-        printf(" carry:      %d\n", cpu_reg.status.carry);
+        printf(" acc:    %02x\n", cpu_reg.acc);
+        printf(" x:      %02x\n", cpu_reg.x);
+        printf(" y:      %02x\n", cpu_reg.y);
+        printf(" sp:     %02x\n", cpu_reg.sp);
+        printf(" status:\n");
+        printf("  negative:   %d\n", cpu_reg.status.negative);
+        printf("  overflow:   %d\n", cpu_reg.status.overflow);
+        printf("  break:      %d\n", cpu_reg.status.break_mode);
+        printf("  decimal:    %d\n", cpu_reg.status.decimal);
+        printf("  irq:        %d\n", cpu_reg.status.irq_disable);
+        printf("  zero:       %d\n", cpu_reg.status.zero);
+        printf("  carry:      %d\n", cpu_reg.status.carry);
         printf("-------------------\n");
     }
     //printf("data:     %02x\n", cpu_data_buffer);
 }
 
 
-int init_6502core(void) {
-    memset(&cpu_reg, 0, sizeof(struct cpu_6502));
-    current_inst = NULL;
-    current_exec_index = 0;
-    exec_done = FALSE;
-    return TRUE;
+int disas_inst(unsigned short addr) {
+    unsigned char inst;
+    unsigned char dbg_get_byte(unsigned short addr);
+    void disasm(const char* mnemonic, int addr_mode, unsigned short pc);
+
+    inst = dbg_get_byte(addr);
+    struct opcode_map * omap = &opcode_list[inst];
+    
+    disasm(omap->mnemonic, omap->addr_mode, addr);
+    return omap->inst_len;
+}
+
+void report_exec_err(void) {
+    fprintf(stderr, "cpu execute instruction failure @0x%04x.\n", pc_get());
+    fprintf(stderr, "error instruction: %s, cycle:%d\n", 
+            current_inst->mnemonic, current_exec_index - 1);
 }