একটি এমওএস 6502 সিপিইউ অনুকরণ করুন


29

এটি এখানে থাকা ইন্টেল ৮০ challenge challenge চ্যালেঞ্জ দ্বারা অনুপ্রাণিত, তবে আমি ভেবেছিলাম একটি 50৫০২ চ্যালেঞ্জটিও আকর্ষণীয় হবে।

চ্যালেঞ্জ

আমি ভেবেছিলাম ফলাফলগুলি দেখতে এটি মজাদার হবে। এই এক স্পষ্টরূপে আরও উন্নত দিকে দিকে স্পষ্টতই। চ্যালেঞ্জটি আপনার নিজের 6502 সিপিইউ এমুলেটর লিখতে হবে। এটি অবশ্যই এর নির্দেশিকা সেট এবং এর এনকোডিং ফর্ম্যাটটি বোঝে। এর নীচে সংস্থানগুলি সংযুক্ত রয়েছে। 6502 অনুকরণ করার জন্য অন্যতম সহজ রিয়েল-ওয়ার্ল্ড প্রসেসর। এই চ্যালেঞ্জের উদ্দেশ্যে, আপনি না চাইলে আপনার চক্রের সময় সম্পর্কে চিন্তা করার দরকার নেই - তবে এটি সর্বদা অন্তর্ভুক্ত করার জন্য একটি প্লাস!

অন্য কোনও শরীরের কোডটি কপি করবেন না !! অবশ্যই, আপনি বুঝতে অন্য ইমুলেটরগুলিতে অবশ্যই উঁকি দিতে পারেন, তবে কোনও অনুলিপি এবং আটকানো নেই! :)

আপনার কোডটি একবার কাজ করে গেলে, আপনি যদি পছন্দ করেন এবং এ্যাপেল II এমুলেটর, বা NES, C64, VIC-20 বা অন্য কোনও বিলিয়ন বিলিয়ন বিলিয়ন 6502-ভিত্তিক সিস্টেমে ফিরে যান তবে আপনি সর্বদা অতিরিক্ত মাইল যেতে পারেন।

আপনার এমুলেটর পরীক্ষা করা হচ্ছে

আমি একটি 6502 পরীক্ষার স্যুট সংকলন করেছি যা আমি এখানে উত্স কোডটি পেয়েছি: http://code.google.com/p/hmc-6502/source/browse/trunk/emu/testvectors/AllSuiteA.asm

আমার সংকলিত সংস্করণটি এখানে ডাউনলোড করা যেতে পারে: http://rubbermallet.org/AllSuiteA.zip

আপনার এমুলেটরটির মেমরি স্পেসে 48 কেবি বাইনারি লোড করুন 4000 ডলারে, যা এর নীচে ১ 16 কেবি পঠন-লিখনের র‌্যাম ফেলে। যখন পরীক্ষাটি সম্পাদন শেষ হয়ে যায়, আপনার সিপিইউ পাস হলে $ 0210 ঠিকানার মান $ এফএফ হওয়া উচিত। আপনি জানবেন যে প্রোগ্রামের কাউন্টার (পিসি) ঠিকানা address 45C0 এ পৌঁছে গেলে পরীক্ষা শেষ হয়।

অন্যান্য পরীক্ষাগুলি এখানেও পাওয়া যায়: http://visual6502.org/wiki/index.php?title=6502 টেস্টপ্রোগ্রাম

এটির সাথে আরও ইন্টারঅ্যাকটিভ কিছু করছেন

আপনার সিপিইউ একবার কাজ করার পরে, আপনি সম্ভবত পরীক্ষার আউটপুটকে দেখার চেয়ে আরও মজাদার কিছু করতে চাইবেন! আমি 6502-র জন্য বর্ধিত বেসিকের একটি রম চিত্র সংকলন করেছি It এটি 16 কেবি, সুতরাং আপনার এটি আপনার এমুলেটেড মেমরি স্পেসের $ C000 এ লোড করা উচিত, আপনার ভার্চুয়াল 6502 পুনরায় সেট করুন এবং সম্পাদন শুরু করুন।

এই জিপটি ডাউনলোড করুন, এতে ebbasic.bin রয়েছে: http://rubbermallet.org/ehbasic.zip

যেভাবে EhBASIC ইনপুট / আউটপুট পরিচালনা করে তা খুব সহজ। এটি কনসোলে একটি অক্ষর লিখতে চাইলে, এটি মেমরির অবস্থানটিতে বাইট লিখে দেয় $ F001। সুতরাং, যখন আপনার এমুলেটর 6502 লোকটি লিখতে চেষ্টা করে দেখবে, কেবল সেই অক্ষরটির মানটি একটি প্রিন্টফের ("% সি", মান) দিয়ে কনসোলে মুদ্রণ করুন ; অথবা অন্যথায় আপনি চান। (এই চ্যালেঞ্জটি অবশ্যই সি এর মধ্যে সীমাবদ্ধ নয়)

এটি যখন কনসোল থেকে কোনও অক্ষরকে ইনপুট করার জন্য পোল করে, এটি বেশ মিল। এটি মেমরির অবস্থান $ F004 থেকে পড়া চালিয়ে যায়, যেখানে পড়ার অপেক্ষায় কীবোর্ড থেকে আপনার পরবর্তী ASCII অক্ষর মান থাকা উচিত। যদি পড়ার জন্য আর কোনও ইনপুট না থাকে তবে এটির শূন্যের মান ফেরত দেওয়া উচিত।

এহব্যাসিক সেই স্থানে মানটি শূন্য-না হওয়া পর্যন্ত পোল করে, যা এটি জানতে দেয় যে বাইটটি বৈধ কীবোর্ড ইনপুট। এজন্য যদি পড়ার মতো আরও কোনও ইনপুট না থাকে তবে এমুলেটরটি সেখানে শূন্য ফিরে আসতে হবে। ইনপুটটির সন্ধান করার সময় এহব্যাসিক পরবর্তী বৈধ কী পর্যন্ত এটিতে স্পিন করবে।

আপনি যদি সর্বশেষ কী মানটি পড়ার পরে যদি সেই মানটি শূন্যের সাথে সাফ না করেন তবে এটি পুনরাবৃত্তি ঘটায় যেন আপনি কীটি চেপে ধরেছেন, তাই সঠিকভাবে এটি করতে সাবধান হন!

যদি আপনার এমুলেটরটি সঠিকভাবে কাজ করে তবে এটি আপনার কনসোলে মুদ্রিত দেখতে পাবেন যখন এটি রম চিত্রটি কার্যকর করে:

6502 EhBASIC [C]old/[W]arm ?

সি টিপুন, তারপরে এন্টার টিপুন এবং আপনার দেখতে হবে:

Memory size ?

31999 Bytes free

Enhanced BASIC 2.22

Ready

বাইটগুলি বিনামূল্যে আপনার জন্য আলাদা হতে পারে তবে আমার এমুলেটরটিতে আমি লিখিত মেমরির অঞ্চলটি 32 কেবি ক্যাপের মধ্যে সীমাবদ্ধ রেখেছি। আপনি যেখানেই ROM শুরু করেন সেখানে পৌঁছতে সত্যই যেতে পারেন, যা 48 কেবি চিহ্ন।

6502 সিপিইউ রিসোর্স লিঙ্কগুলি

এখানে কিছু সংস্থান রয়েছে যা আপনাকে কাজ করার জন্য পর্যাপ্ত তথ্য দেবে:

http://www.obelisk.demon.co.uk/6502/instructions.html

http://www.e-tradition.net/bytes/6502/6502_instruction_set.html

http://www.llx.com/~nparker/a2/opcodes.html <- এই একটিতে খুব আকর্ষণীয় তথ্য রয়েছে

http://en.wikipedia.org/wiki/MOS_Technology_6502

আপনার যদি প্রশ্ন থাকে বা আরও প্রযুক্তিগত তথ্যের প্রয়োজন হয় তবে নির্দ্বিধায় আমাকে জিজ্ঞাসা করুন। ওয়েবে 6502 অন্যান্য তথ্যেরও বিস্তৃত সম্পদ রয়েছে। গুগল আপনার বন্ধু!


এই বাক্যে একটি বৈষম্য বলে মনে হচ্ছে: "যদি আরও পড়ার জন্য কোনও ইনপুট না থাকে তবে এটির শূন্যের মান ফেরত দেওয়া উচিত This এর ফলে এহব্যাসিক ভোটগ্রহণ অ- জেরো না হওয়া অবধি রাখে " "
ইগবি লার্জম্যান

এর, আমার ভুল আমি এটি ভালভাবে ব্যাখ্যা করিনি। আমি বোঝাতে চেয়েছিলাম যে এহব্যাসিক সেই স্থানে মানটি শূন্য-পূর্ব না হওয়া পর্যন্ত পোল করে, যা এটি বাইটকে বৈধ কীবোর্ড ইনপুট বলে জানতে দেয়। এজন্য যদি পড়ার মতো আরও কোনও ইনপুট না থাকে তবে এমুলেটরটি সেখানে শূন্য ফিরে আসতে হবে। আমি এটি সম্পাদনা করব।
মাইক সি

আমি শেষ পর্যন্ত আমার নিজের 6502 কোর পোস্ট করতে পারি তবে আমি প্রথমে অন্যের কাছ থেকে কয়েকটি প্রবেশের জন্য অপেক্ষা করতে যাচ্ছি। আশা করি কেউ এই চ্যালেঞ্জকে একটি শট দেয়। 8086 চ্যালেঞ্জের বেশ কয়েকটি সমাধান ছিল, সুতরাং স্পষ্টতই এখানে এটি করার জন্য যথেষ্ট স্মার্ট লোক রয়েছে। 8086 অনেক বেশি কঠিন!
মাইক সি

1
আমি এটি চেষ্টা করতে চাই, যদিও কোনও প্রতিযোগিতামূলক অর্থে নয়। আমার জন্য সমস্যাটি সময় সন্ধান করছে। আমি মনে করি এটি ভাল হবে যদি আপনি আরও একটি পরীক্ষা প্রোগ্রাম সরবরাহ করতে পারেন যা এমুলেটরটি ভালভাবে অনুশীলন করবে এবং কিছু সহজে যাচাইযোগ্য আউটপুট তৈরি করবে, যা 8086 চ্যালেঞ্জের জন্য করা হয়েছিল তার অনুরূপ।
ইগবি লার্জম্যান

2
কে জিতবে তা আপনি কীভাবে নির্ধারণ করবেন? (সেখানে বিজয়ী হতে হবে)

উত্তর:


22

ভেবেছিলাম আমি এগিয়ে গিয়ে নিজের বাস্তবায়ন পোস্ট করব। এটি সম্পূর্ণরূপে অসম্পূর্ণ, তবে এটি সম্পূর্ণ বাস্তবায়ন।

  • সি এর 668 লাইন (ফাঁকা রেখা বা কেবলমাত্র মন্তব্য দিয়ে রেখা গণনা করা হচ্ছে না)
  • সমর্থন করে (আমি মনে করি) সমস্ত অনাবৃত নির্দেশাবলী।
  • বিসিডি সমর্থন করে।
  • সিপিইউ ক্লক সাইকেলের সময়। (নির্দিষ্ট পৃষ্ঠার সীমানা মোড়কের সামঞ্জস্য সহ)
  • একক-পদক্ষেপ দ্বারা বা টিক্সের সংখ্যা নির্দিষ্ট করে নির্দেশাবলী কার্যকর করতে পারে।
  • প্রতিটি নির্দেশ সন্ধানের পরে বলা বাহ্যিক ফাংশন হুকিং সমর্থন করে। এটি কারণ এটি মূলত কোনও এনইএস এমুলেটরের জন্য ছিল এবং আমি এটি অডিও টাইমিংয়ের জন্য ব্যবহার করি।
/ * Fake6502 সিপিইউ এমুলেটর কোর v1.1 *******************
 * (গ) ২০১১-২০১৩ মাইক চেম্বারস *
 ************************************************** *** /

# অন্তর্ভুক্ত <stdio.h>
# অন্তর্ভুক্ত <stdint.h>

// বাহ্যিকভাবে সরবরাহিত ফাংশন
বাহ্যিক uint8_t পড়া 6502 (uint16_t ঠিকানা);
বাহ্যিক শূন্য রাইটিং 6502 (uint16_t ঠিকানা, uint8_t মান);

// 6502 সংজ্ঞা দেয়
# নির্ধারিত অন্তর্ভুক্ত // যখন এটি সংজ্ঞায়িত করা হয়, নিবন্ধিত অপকডগুলি পরিচালনা করা হয়।
                     // অন্যথায়, এগুলিকে কেবল এনওপি হিসাবে বিবেচনা করা হয়।

// # NES_CPU সংজ্ঞায়িত করুন // যখন এটি সংজ্ঞায়িত করা হয়, বাইনারি-কোডিং দশমিক (বিসিডি)
                     // স্ট্যাটাস পতাকা এডিসি এবং এসবিসি দ্বারা সম্মানিত হয় না। 2A03
                     // নিন্টেন্ডো বিনোদন সিস্টেমের সিপিইউ করে না
                     // সমর্থন বিসিডি অপারেশন।

# নির্ধারিত FLAG_CARRY 0x01
# নির্ধারিত FLAG_ZERO 0x02
# নির্ধারিত FLAG_INTERRUPT 0x04
# নির্ধারিত FLAG_DECIMAL 0x08
# নির্ধারিত FLAG_BREAK 0x10
# নির্ধারিত FLAG_CONSTANT 0x20
# নির্ধারিত FLAG_OVERFLOW 0x40
# নির্ধারিত FLAG_SIGN 0x80

# নির্ধারিত BASE_STACK 0x100

# ডিফাইন সেভাক্কাম (এন) এ = (ইউএনটি 8_ টি) ((এন) এবং 0x00FF)


// ফ্ল্যাগ মডিফায়ার ম্যাক্রোগুলি
# নির্ধারিত সেটকারি () স্থিতি | = FLAG_CARRY
# নির্ধারিত ক্লিয়ারারি () স্থিতি & = (LA FLAG_CARRY)
# নির্ধারিত সেটজারো () স্থিতি | = FLAG_ZERO
# নির্ধারিত ক্লিয়ারজারো () স্থিতি & = (LA FLAG_ZERO)
# ডিফাইন সেটাইনটারপ্রেট () স্থিতি | = FLAG_INTERRUPT
# নির্ধারিত ক্লিয়ারিন্টার () স্থিতি & = (LA FLAG_INTERRUPT)
# নির্ধারিত সেটডিসিমাল () স্থিতি | = FLAG_DECIMAL
# নির্ধারিত ক্লিয়ারসিডিমাল () স্থিতি & = (~ FLAG_DECIMAL)
# নির্ধারিত সেটওভারফ্লো () স্থিতি | = FLAG_OVERFLOW
# নির্ধারিত ক্লিয়ারওভারফ্লো () স্থিতি & = (LA FLAG_OVERFLOW)
# নির্দিষ্ট সেটাইন () স্থিতি | = FLAG_SIGN N
# নির্ধারিত ছাড়পত্র () স্থিতি & = (LA FLAG_SIGN)


// পতাকা গণনা ম্যাক্রো
# নির্ধারিত জিরোকাল্ক (এন) {\
    যদি ((এন) এবং 0x00FF) ক্লিয়ারজারো ();
        অন্য সেটজারো ();
}

# নির্দিষ্ট সংকেত (এন) {\
    যদি ((এন) এবং 0x0080) সেট সাইন (); \
        অন্যথায় সাফ করুন ();
}

# ডিফাইন ক্যারিকল্ক (এন) {
    যদি ((এন) এবং 0xFF00) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
}

# নির্ধারিত ওভারফ্লোকাল্যাক (এন, এম, ও) {/ * এন = ফলাফল, এম = সঞ্চালক, ও = মেমরি * / \
    যদি (((এন) ^ (uint16_t) (এম)) & ((এন) ^ (ও)) এবং 0x0080) সেটওভারফ্লো ();
        অন্যথায় ক্লিয়ারওভারফ্লো ();
}


// 6502 সিপিইউ নিবন্ধন করে
uint16_t পিসি;
uint8_t এসপি, এ, এক্স, ওয়াই, স্থিতি = FLAG_CONSTANT;


// সহায়ক ভেরিয়েবল
uint64_t নির্দেশাবলী = 0; // কার্যকর করা মোট নির্দেশাবলী ট্র্যাক রাখুন
uint32_t ক্লকটিক্স 6502 = 0, ক্লকগোয়াল6502 = 0;
uint16_t oldpc, ea, reladdr, মান, ফলাফল;
uint8_t অপকোড, ওল্ডস্ট্যাটাস;

// অন্যান্য বিভিন্ন ফাংশন দ্বারা ব্যবহৃত কয়েকটি সাধারণ ফাংশন
অকার্যকর পুশ 16 (uint16_t পুশওয়াল) {
    write6502 (BASE_STACK + এসপি, (পুশভাল >> 8) এবং 0xFF);
    write6502 (BASE_STACK + ((এসপি - 1) এবং 0xFF), পুশভাল & 0xFF);
    এসপি - = 2;
}

অকার্যকর পুশ 8 (uint8_t পুশওয়াল) {
    write6502 (BASE_STACK + sp--, পুশওয়াল);
}

uint16_t পুল 16 () {
    uint16_t টেমপ্লেট 16;
    temp16 = পঠন 6502 (BASE_STACK + ((এসপি + 1) এবং 0xFF)) | ((uint16_t) Read6502 (BASE_STACK + ((এসপি + 2) এবং 0xFF)) << 8);
    এসপি + = 2;
    ফিরুন (temp16);
}

uint8_t pull8 () {
    রিটার্ন (রিড 6502 (BASE_STACK +++ এসপি));
}

অকার্যকর রিসেট 6502 () {
    pc = (uint16_t) read6502 (0xFFFC) | ((uint16_t) পড়া 6502 (0xFFFD) << 8);
    a = 0;
    x = 0;
    y = 0;
    এসপি = 0 এক্সএফডি;
    স্থিতি | = FLAG_CONSTANT;
}


স্ট্যাটিক অকার্যকর (* সংযোজনযোগ্য [256]) ();
স্ট্যাটিক অকার্যকর (* অপ্টেবল [256]) ();
uint8_t পেনাল্টিপ, পেনাল্টিড্ডার;

// অ্যাড্রেসিং মোড ফাংশন, কার্যকর ঠিকানা গণনা করে
স্ট্যাটিক অকার্যকর imp () imp // নিহিত
}

স্ট্যাটিক অকার্যকর acc () accum // সঞ্চয়ী
}

স্থির অকার্যকর ইমি () immediate // তাত্ক্ষণিক
    ea = pc ++;
}

স্ট্যাটিক অকার্যকর zp () zero // শূন্য পৃষ্ঠা
    ea = (uint16_t) read6502 ((uint16_t) পিসি ++);
}

স্ট্যাটিক অকার্যকর zpx () zero // শূন্য পৃষ্ঠা, এক্স
    ea = ((uint16_t) read6502 ((uint16_t) পিসি ++) + (uint16_t) x) & 0xFF; // শূন্য পৃষ্ঠার মোড়ক
}

স্ট্যাটিক শূন্য zpy () zero // শূন্য পৃষ্ঠা, ওয়াই
    ea = ((uint16_t) read6502 ((uint16_t) পিসি ++) + (uint16_t) y) & 0xFF; // শূন্য পৃষ্ঠার মোড়ক
}

স্ট্যাটিক অকার্যকর rel () branch // শাখা অপের জন্য আপেক্ষিক (8-বিট অবিলম্বে মান, সাইন-এক্সটেন্ডেড)
    reladdr = (uint16_t) read6502 (পিসি ++);
    if (reladdr & 0x80) reladdr | = 0xFF00;
}

স্ট্যাটিক অকার্যকর abso () {// পরম
    ea = (uint16_t) read6502 (পিসি) | ((uint16_t) পড়া 6502 (পিসি + 1) << 8);
    পিসি + = 2;
}

স্ট্যাটিক অকার্যকর অ্যাবএক্স () absolute // পরম, এক্স
    uint16_t প্রারম্ভকালীন;
    ea = ((uint16_t) read6502 (pc) | ((uint16_t) read6502 (পিসি + 1) << 8));
    সূচনা পৃষ্ঠা = ইএ এবং 0xFF00;
    ea + = (uint16_t) x;

    যদি (প্রারম্ভকালীন! = (ইএ এবং 0xFF00)) op // কিছু ওপকোডে পৃষ্ঠা-ক্রসিংয়ের জন্য একটি চক্রের পেনাল্টি
        পেনাল্টিডেডিআর = 1;
    }

    পিসি + = 2;
}

স্থির অকার্যকর (ab) পরম, ওয়াই
    uint16_t প্রারম্ভকালীন;
    ea = ((uint16_t) read6502 (pc) | ((uint16_t) read6502 (পিসি + 1) << 8));
    সূচনা পৃষ্ঠা = ইএ এবং 0xFF00;
    ea + = (uint16_t) y;

    যদি (প্রারম্ভকালীন! = (ইএ এবং 0xFF00)) op // কিছু ওপকোডে পৃষ্ঠা-ক্রসিংয়ের জন্য একটি চক্রের পেনাল্টি
        পেনাল্টিডেডিআর = 1;
    }

    পিসি + = 2;
}

স্ট্যাটিক অকার্যকর ind () ind // পরোক্ষ
    uint16_t eahelp, eahelp2;
    eahelp = (uint16_t) read6502 (পিসি) | (uint16_t) ((uint16_t) read6502 (পিসি + 1) << 8);
    eahelp2 = (eahelp & 0xFF00) | ((এহেল্প + 1) & 0x00FF); // 6502 পৃষ্ঠার সীমানা wraparound বাগ প্রতিলিপি
    ea = (uint16_t) read6502 (eahelp) | ((uint16_t) পড়া 6502 (eahelp2) << 8);
    পিসি + = 2;
}

স্ট্যাটিক অকার্যকর ইনডেক্স () {// (অপ্রত্যক্ষ, এক্স)
    uint16_t eahelp;
    eahelp = (uint16_t) (((uint16_t) read6502 (পিসি ++) + (uint16_t) x) & 0xFF); টেবিল পয়েন্টারের জন্য // শূন্য পৃষ্ঠার মোড়ক
    ea = (uint16_t) read6502 (eahelp & 0x00FF) | ((uint16_t) read6502 ((eahelp + 1) & 0x00FF) << 8);
}

স্ট্যাটিক শূন্য indy () {// (পরোক্ষ), ওয়াই
    uint16_t eahelp, eahelp2, সূচনা পৃষ্ঠা;
    eahelp = (uint16_t) read6502 (পিসি ++);
    eahelp2 = (eahelp & 0xFF00) | ((এহেল্প + 1) & 0x00FF); // শূন্য পৃষ্ঠার মোড়ক
    ea = (uint16_t) read6502 (eahelp) | ((uint16_t) পড়া 6502 (eahelp2) << 8);
    সূচনা পৃষ্ঠা = ইএ এবং 0xFF00;
    ea + = (uint16_t) y;

    যদি (প্রারম্ভকালীন! = (ইএ এবং 0xFF00)) op // কিছু ওপকোডে পৃষ্ঠা-ক্রসিংয়ের জন্য একটি চক্রের পেনাল্টি
        পেনাল্টিডেডিআর = 1;
    }
}

স্ট্যাটিক uint16_t getvalue () {
    যদি (সংযোজক [অপকোড] == টিসি) রিটার্ন ((uint16_t) ক);
        অন্যথায় ফিরে ((uint16_t) read6502 (ইএ));
}

স্ট্যাটিক অকার্যকর পুঁজি (uint16_t সেভাল) {
    যদি (সংযোজক [অপকোড] == টিসি) a = (uint8_t) (সেভাল & 0x00FF);
        অন্যটি লিখুন 6502 (ইএ, (সেভাল & 0x00FF));
}


// নির্দেশনা হ্যান্ডলার ফাংশন
স্ট্যাটিক অকার্যকর অ্যাডিসি () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    ফলাফল = (uint16_t) একটি + মান + (uint16_t) (স্থিতি এবং FLAG_CARRY);

    carrycalc (ফলাফলের);
    zerocalc (ফলাফলের);
    ওভারফ্লোক্যালক (ফলাফল, ক, মান);
    signcalc (ফলাফলের);

    #ifndef NES_CPU
    যদি (স্থিতি এবং FLAG_DECIMAL) {
        clearcarry ();

        যদি ((একটি এবং 0x0F)> 0x09)
            a + = 0x06;
        }
        যদি ((একটি এবং 0xF0)> 0x90)
            a + = 0x60;
            setcarry ();
        }

        clockticks6502 ++,;
    }
    #শেষ যদি

    saveaccum (ফলাফলের);
}

স্থির শূন্য এবং () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    ফলাফল = (uint16_t) একটি & মান;

    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    saveaccum (ফলাফলের);
}

স্ট্যাটিক অকার্যকর asl () {
    মান = getvalue ();
    ফলাফল = মান << 1;

    carrycalc (ফলাফলের);
    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্ট্যাটিক শূন্য বিসিসি () {
    যদি ((স্থিতি এবং FLAG_CARRY) == 0)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক শূন্য বিসিএস () {
    যদি ((স্থিতি এবং FLAG_CARRY) == FLAG_CARRY)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক অকার্যকর বেক () {
    যদি ((স্থিতি এবং FLAG_ZERO) == FLAG_ZERO)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্থির শূন্য বিট () {
    মান = getvalue ();
    ফলাফল = (uint16_t) একটি & মান;

    zerocalc (ফলাফলের);
    স্থিতি = (স্থিতি & 0x3F) | (uint8_t) (মান & 0xC0);
}

স্থির শূন্য বিএমআই () {
    যদি ((স্থিতি & FLAG_SIGN) == FLAG_SIGN)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক অকার্যকর Bne () {
    যদি ((স্থিতি এবং FLAG_ZERO) == 0)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক শূন্য বিপিএল () {
    যদি ((স্থিতি এবং FLAG_SIGN) == 0)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক অকার্যকর ব্র্যাক () {
    পিসি ++,;
    push16 (পিসি); // পরবর্তী নির্দেশিকার ঠিকানাটি স্ট্যাকের উপরে চাপুন
    পুশ 8 (স্ট্যাটাস | FLAG_BREAK); // স্ট্যাকের জন্য সিপিইউ স্থিতি চাপুন
    setinterrupt (); // সেট বিঘ্নিত পতাকা
    pc = (uint16_t) read6502 (0xFFFE) | ((uint16_t) পড়া 6502 (0xFFFF) << 8);
}

স্ট্যাটিক অকার্যকর বিভিসি () {
    যদি ((স্থিতি এবং FLAG_OVERFLOW) == 0)
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক অকার্যকর bvs () {
    যদি ((স্থিতি এবং FLAG_OVERFLOW) == FLAG_OVERFLOW) {
        oldpc = pc;
        পিসি + = রিলেডড্র;
        যদি ((ওল্ডপিসি এবং 0xFF00)! = (পিসি এবং 0xFF00)) ক্লকটিক্স 6502 + = 2; // লাফ কোনও পৃষ্ঠার সীমানা অতিক্রম করেছে কিনা তা পরীক্ষা করুন
            অন্য ক্লকটিক্স 6502 ++;
    }
}

স্ট্যাটিক অকার্যকর clc () {
    clearcarry ();
}

স্ট্যাটিক অকার্যকর সিএলডি () {
    cleardecimal ();
}

স্ট্যাটিক অকার্যকর ক্লিপ () {
    clearinterrupt ();
}

স্ট্যাটিক শূন্য clv () {
    clearoverflow ();
}

স্ট্যাটিক অকার্যকর সিএমপি () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    ফলাফল = (uint16_t) এ - মান;

    যদি (a> = (uint8_t) (মান & 0x00FF)) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
    যদি (a == (uint8_t) (মান & 0x00FF)) সেটজারো ();
        অন্য ক্লিয়ারজারো ();
    signcalc (ফলাফলের);
}

স্ট্যাটিক অকার্যকর সিপিএক্স () {
    মান = getvalue ();
    ফলাফল = (uint16_t) এক্স - মান;

    যদি (x> = (uint8_t) (মান & 0x00FF)) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
    যদি (x == (uint8_t) (মান & 0x00FF)) সেটজারো ();
        অন্য ক্লিয়ারজারো ();
    signcalc (ফলাফলের);
}

স্ট্যাটিক শূন্য সিপিপি () {
    মান = getvalue ();
    ফলাফল = (uint16_t) y - মান;

    যদি (y> ​​= (uint8_t) (মান & 0x00FF)) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
    যদি (y == (uint8_t) (মান & 0x00FF)) সেটজারো ();
        অন্য ক্লিয়ারজারো ();
    signcalc (ফলাফলের);
}

স্থির অকার্যকর সিদ্ধান্ত () {
    মান = getvalue ();
    ফলাফল = মান - 1;

    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্ট্যাটিক অকার্যকর ডেক্স () {
    এক্স--;

    zerocalc (x) এর;
    signcalc (x) এর;
}

স্ট্যাটিক শূন্য দে () {
    y--;

    zerocalc (Y);
    signcalc (Y);
}

স্ট্যাটিক অকার্যকর ইওর () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    ফলাফল = (uint16_t) a ^ মান;

    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    saveaccum (ফলাফলের);
}

স্ট্যাটিক শূন্য Inc () {
    মান = getvalue ();
    ফলাফল = মান + 1;

    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্ট্যাটিক অকার্যকর ইনক্স () {
    এক্স ++,;

    zerocalc (x) এর;
    signcalc (x) এর;
}

স্থির শূন্য iny () {
    Y ++,;

    zerocalc (Y);
    signcalc (Y);
}

স্ট্যাটিক অকার্যকর জ্যাম্প () {
    পিসি = ইএ;
}

স্ট্যাটিক অকার্যকর jsr () {
    পুশ 16 (পিসি - 1);
    পিসি = ইএ;
}

স্ট্যাটিক অকার্যকর lda () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    a = (uint8_t) (মান & 0x00FF);

    zerocalc (ক);
    signcalc (ক);
}

স্ট্যাটিক অকার্যকর ldx () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    x = (uint8_t) (মান & 0x00FF);

    zerocalc (x) এর;
    signcalc (x) এর;
}

স্ট্যাটিক অকার্যকর ldy () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    y = (uint8_t) (মান & 0x00FF);

    zerocalc (Y);
    signcalc (Y);
}

স্ট্যাটিক অকার্যকর lsr () {
    মান = getvalue ();
    ফলাফল = মান >> 1;

    যদি (মান & 1) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্ট্যাটিক অকার্যকর ন্যাপ () {
    স্যুইচ (অপকোড) {
        কেস 0x1 সি:
        কেস 0x3 সি:
        কেস 0x5 সি:
        কেস 0x7 সি:
        কেস 0 এক্সডিসি:
        কেস 0 এক্সএফসি:
            পেনাল্টিপ = 1;
            বিরতি;
    }
}

স্থির শূন্য ওরা () {
    পেনাল্টিপ = 1;
    মান = getvalue ();
    ফলাফল = (uint16_t) ক | মান;

    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    saveaccum (ফলাফলের);
}

স্থির শূন্য ফা () {
    push8 (ক);
}

স্ট্যাটিক অকার্যকর পিএইচপি () {
    পুশ 8 (স্ট্যাটাস | FLAG_BREAK);
}

স্ট্যাটিক শূন্য প্লা () {
    a = pull8 ();

    zerocalc (ক);
    signcalc (ক);
}

স্থির শূন্যতা plp () {
    অবস্থা = টান 8 () | FLAG_CONSTANT;
}

স্ট্যাটিক শূন্য রোল () {
    মান = getvalue ();
    ফলাফল = (মান << 1) | (স্থিতি & FLAG_CARRY);

    carrycalc (ফলাফলের);
    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্থির শূন্য রর () {
    মান = getvalue ();
    ফলাফল = (মান >> 1) | ((স্থিতি & FLAG_CARRY) << 7);

    যদি (মান & 1) সেটকারি ();
        অন্যথায় ক্লিয়ারকারি ();
    zerocalc (ফলাফলের);
    signcalc (ফলাফলের);

    putvalue (ফলাফলের);
}

স্থির শূন্য rti () {
    স্থিতি = pull8 ();
    মান = টানা 16 ();
    পিসি = মান;
}

স্থির শূন্য rts () {
    মান = টানা 16 ();
    পিসি = মান + 1;
}

স্ট্যাটিক অকার্যকর এসবিসি () {
    পেনাল্টিপ = 1;
    মান = getvalue () x 0x00FF;
    ফলাফল = (uint16_t) একটি + মান + (uint16_t) (স্থিতি এবং FLAG_CARRY);

    carrycalc (ফলাফলের);
    zerocalc (ফলাফলের);
    ওভারফ্লোক্যালক (ফলাফল, ক, মান);
    signcalc (ফলাফলের);

    #ifndef NES_CPU
    যদি (স্থিতি এবং FLAG_DECIMAL) {
        clearcarry ();

        a - = 0x66;
        যদি ((একটি এবং 0x0F)> 0x09)
            a + = 0x06;
        }
        যদি ((একটি এবং 0xF0)> 0x90)
            a + = 0x60;
            setcarry ();
        }

        clockticks6502 ++,;
    }
    #শেষ যদি

    saveaccum (ফলাফলের);
}

স্ট্যাটিক শূন্য সেকেন্ড () {
    setcarry ();
}

স্ট্যাটিক শূন্য সেড () {
    setdecimal ();
}

স্ট্যাটিক অকার্যকর সে () {
    setinterrupt ();
}

স্ট্যাটিক অকার্যকর স্টা () {
    putvalue (ক);
}

স্ট্যাটিক অকার্যকর স্টেক্স () {
    putvalue (x) এর;
}

স্ট্যাটিক শূন্য শৈলী () {
    putvalue (Y);
}

স্ট্যাটিক শূন্য কর () {
    x = ক;

    zerocalc (x) এর;
    signcalc (x) এর;
}

স্থির শূন্য তা () {
    y = a;

    zerocalc (Y);
    signcalc (Y);
}

স্ট্যাটিক শূন্য tsx () {
    x = এসপি;

    zerocalc (x) এর;
    signcalc (x) এর;
}

স্ট্যাটিক শূন্য txa () {
    a = x;

    zerocalc (ক);
    signcalc (ক);
}

স্ট্যাটিক শূন্য txs () {
    sp = x;
}

স্থির শূন্য টিয়া () {
    a = y;

    zerocalc (ক);
    signcalc (ক);
}

// অননুমোদিত নির্দেশাবলী
#ifdef অন্তর্ভুক্ত
    স্থির অকার্যকর শিথিল () {
        LDA ();
        ldx ();
    }

    স্ট্যাটিক অকার্যকর স্যাক্স () {
        Sta ();
        stx ();
        পুটভ্যালু (একটি & এক্স);
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্ট্যাটিক অকার্যকর ডিসিপি () {
        ডিসেম্বর ();
        CMP ();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্ট্যাটিক অকার্যকর isb () {
        Inc ();
        SBC ();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্ট্যাটিক শূন্য শ্লোক () {
        ASL ();
        Ora ();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্থির শূন্য rla () {
        Rol ();
        এবং();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্ট্যাটিক অকার্যকর sre () {
        lsr ();
        eor ();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }

    স্ট্যাটিক শূন্য rra () {
        Ror ();
        এডিসি ();
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502--;
    }
#else
    # নির্ধারণ লক্ষ
    # নির্ধারিত স্যাক্স ন্যাপ
    # নির্ধারিত ডিসিপি নো
    # নির্ধারিত
    # নির্ধারিত স্লো না
    # নির্ধারিত
    # ডেফাইন sre nop
    # নির্ধারিত
#শেষ যদি


স্ট্যাটিক অকার্যকর (* সংযোজনযোগ্য [256]) () = {
/ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ক | খ | সি | ডি | E | এফ | * /
/ * 0 * / ইমপি, ইনডেক্স, ইমপি, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, ইমপি, এমএম, এ্যাক, ইমি, আবসো, আবসো, আবসো, অ্যাবসো, / * 0 * /
/ * 1 * / রিল, ইন্ডি, ইমপি, ইনডি, জেডপেক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স, / * 1 * /
/ * 2 * / অ্যাবসো, ইনডেক্স, ইমপি, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, ইমপি, এমএম, এ্যাক, ইমি, আবসো, আবসো, অ্যাবসো, অ্যাবসো, / * 2 * /
/ * 3 * / rel, ইন্ডি, ছাপ, ইনডি, জেডপিএক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স, / * 3 * /
/ * 4 * / ইমপি, ইনডেক্স, ইমপি, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, ইমপি, এমএম, এ্যাক, ইমি, আবসো, আবসো, আবসো, আবসো, / * 4 * /
/ * 5 * / রিল, ইন্ডি, ছাপ, ইন্ডি, জেডপেক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, আইএমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স, / * 5 * /
/ * 6 * / ইমপি, ইনডেক্স, আইএমপি, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, ইমপি, এমএম, এ্যাক, ইমি, ইন্ড, আবসো, আবসো, আবসো, / * 6 * /
/ * 7 * / rel, ইন্ডি, ছাপ, ইনডি, জেডপিএক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স, / * 7 * /
/ * 8 * / ইমি, ইনডেক্স, ইমিড, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, আইএমপি, ইম্প, ইমপি, ইমি, আবসো, আবসো, আবসো, আবসো, / * 8 * /
/ * 9 * / রিল, ইন্ডি, ইমপি, ইনডি, জেডপেক্স, জেডপেক্স, জেপি, জেপি, ইমপি, অ্যাজেসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবেসি, অ্যাবসি, / * 9 * /
/ * এ * / ইমি, ইনডেক্স, ইমিড, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, আইএমপি, ইম্প, ইমপি, ইমি, আবসো, আবসো, আবসো, অ্যাবসো, / * এ * /
/ * বি * / রিল, ইন্ডি, ইমপি, ইনডি, জেডপেক্স, জেডপেক্স, জেডপি, জেপি, ইমপি, অ্যাজেসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবেসি, অ্যাজে, / * বি * /
/ * সি * / ইমি, ইনডেক্স, ইমিড, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, আইএমপি, ইম্প, ইমপি, ইমি, আবসো, আবসো, আবসো, অ্যাবসো, / * সি * /
/ * ডি * / রিল, ইন্ডি, ইমপি, ইনডি, জেডপেক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স, / * ডি * /
/ * ই * / ইমি, ইনডেক্স, ইমিডেক্স, ইনডেক্স, জেপি, জেপি, জেপি, জেপি, আইএমপি, ইম্প, ইমপি, ইমি, আবসো, আবসো, আবসো, আবসো, / * ই * /
/ * এফ * / রিল, ইন্ডি, ছাপ, ইন্ডি, জেডপেক্স, জেডপেক্স, জেডপেক্স, জেডপেক্স, ইমপি, অ্যাবসি, ইমপি, অ্যাবসি, অ্যাবএক্স, অ্যাবএক্স, অ্যাবএক্স / * এফ * /
};

স্ট্যাটিক অকার্যকর (* অপ্টেবল [256]) () = {
/ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ক | খ | সি | ডি | E | এফ | * /
/ * 0 * / ব্রেকে, ওরা, নোপ, স্লো, নোপ, ওরা, এসএল, স্লো, পিএইচপি, ওরা, এসএল, নোপ, ন্যাপ, ওরা, এসএল, স্লো, / * 0 * /
/ * 1 * / বিপিএল, ওরা, নোপ, স্লো, নপ, ওরা, এসএল, স্লো, সিএলসি, ওরা, নোপ, স্লো, নোপ, ওরা, এসএল, স্লো, / * 1 * /
/ * 2 * / জেএসআর, এবং, নোপ, আরলা, বিট, এবং রোল, আরএলএ, পিএলপি, এবং, রোল, নোপ, বিট, এবং, রোল, আরএল, / * 2 * /
/ * 3 * / বিএমআই, এবং, নোপ, আরএলএ, নোপ, এবং, রোল, আরএলএ, সেকেন্ড, এবং, নোং, আরএলএ, নোপ, এবং, রোল, আরএলএ, / * 3 * /
/ * 4 * / আরটিআই, ইওর, নোপ, সেরি, নোপ, ইওর, এলএসআর, সেরে, ফা, ইওর, এলএসআর, এনওপি, জেএমপি, ইওর, এলএসআর, সেরি, / * 4 * /
/ * 5 * / বিভিসি, ইওর, এনওপি, সেরি, নোপ, ইওর, এলএসআর, সেরি, ক্লিপ, ইওর, নোপ, সের, নোপ, ইওর, এলএসআর, সেরি, / * 5 * /
/ * 6 * / আরটিএস, অ্যাডিসি, নোপ, আরআর, এনওপি, অ্যাডিসি, রর, আরএ, পিএল, এডিসি, রর, নোপ, জেএমপি, অ্যাডিসি, রোর, আরএ, / * 6 * /
/ * 7 * / বিভিএস, অ্যাডিসি, নোপ, আরএ, এনওপি, অ্যাডিসি, রর, আরএ, এসই, এডিসি, নোপ, আরএ, নোপ, অ্যাডিসি, রর, আরএ, / * 7 * /
/ * 8 * / এনওপি, স্টা, নোপ, স্যাক্স, স্টাইল, স্টা, স্টেক্স, স্যাক্স, ডি, নপ, টেক্সা, নোপ, স্টাইল, স্টা, স্ট্যাক্স, স্যাক্স, / * 8 * /
/ * 9 * / বিসিসি, স্টা, নোপ, নোপ, স্টাইল, স্টা, স্টেক্স, স্যাক্স, ট্যায়া, স্টা, টিএক্সএস, নোপ, নোপ, স্টা, নোপ, / "9 * /
/ * এ * / এলডি, এলডিএ, এলডিএক্স, ল্যাক্স, এলডি, এলডিএ, এলডিএক্স, ল্যাক্স, তাই, এলডিএ, ট্যাক্স, এনওপি, এলডি, এলডিএ, এলডিএক্স, ল্যাক্স, / * এ * /
/ * বি * / বিসিএস, এলডিএ, নোপ, ল্যাক্স, এলডি, এলডিএ, এলডিএক্স, ল্যাক্স, সিভিভি, এলডিএ, টিএসএক্স, ল্যাক্স, এলডি, এলডিএ, এলডিএক্স, ল্যাক্স, / * বি * /
/ * সি * / সিপিপি, সিএমপি, এনওপি, ডিসিপি, সিপিপি, সিএমপি, ডিস, ডিসিপি, ইনি, সিএমপি, ডেক্স, এনওপি, সিপি, সিএমপি, ডিসি, ডিসিপি, / * সি * /
/ * ডি * / বেনি, সিএমপি, এনওপি, ডিসিপি, নোপ, সিএমপি, ডেস্ক, ডিসিপি, সিলিডি, সিএমপি, নোপ, ডিসিপি, নোপ, সিএমপি, ডিসি, ডিসিপি, / * ডি * /
/ * ই * / সিপিএক্স, এসবিসি, নোপ, আইএসবি, সিপিএক্স, এসবিসি, ইনক, আইএসবি, ইনেক্স, এসবিসি, নোপ, এসবিসি, সিপিএক্স, এসবিসি, ইনক, আইএসবি, / * ই * /
/ * এফ * / বীক, এসবিসি, নোপ, আইএসবি, নোপ, এসবিসি, ইনক, আইএসবি, সেড, এসবিসি, নোপ, ইসবি, নোপ, এসবিসি, ইনক, আইএসবি / * এফ * /
};

স্ট্যাটিক কনস্ট uint32_t টিকটেবল [256] = {
/ * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ক | খ | সি | ডি | E | এফ | * /
/ * 0 * / 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, / * 0 * /
/ * 1 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 1 * /
/ * 2 * / 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, / * 2 * /
/ * 3 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 3 * /
/ * 4 * / 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, / * 4 * /
/ * 5 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 5 * /
/ * 6 * / 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, / * 6 * /
/ * 7 * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * 7 * /
/ * 8 * / 2, 6, 2, 6, 3, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, / * 8 * /
/ * 9 * / 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, / * 9 * /
/ * এ * / 2, 6, 2, 6, 3, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, / * এ * /
/ * বি * / 2, 5, 2, 5, 4, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, / * বি * /
/ * সি * / 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, / * সি * /
/ * ডি * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, / * ডি * /
/ * ই * / 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, / * ই * /
/ * এফ * / 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 / * এফ * /
};


অকার্যকর nmi6502 () {
    push16 (পিসি);
    push8 (অবস্থা);
    স্থিতি | = FLAG_INTERRUPT;
    pc = (uint16_t) read6502 (0xFFFA) | ((uint16_t) পড়া 6502 (0xFFFB) << 8);
}

অকার্যকর irq6502 () {
    push16 (পিসি);
    push8 (অবস্থা);
    স্থিতি | = FLAG_INTERRUPT;
    pc = (uint16_t) read6502 (0xFFFE) | ((uint16_t) পড়া 6502 (0xFFFF) << 8);
}

uint8_t ক্যালেক্সার্নাল = 0;
অকার্যকর (* লুপেক্সটার্নাল) ();

অকার্যকর কার্যকর 6502 (uint32_t টিককাউন্ট) {
    ক্লকগোয়াল6502 + = টিককাউন্ট;

    যখন (ক্লকটিক্স 6502 <ক্লকগোয়াল6502) {
        অপকোড = রিড6502 (পিসি ++);

        পেনাল্টিপ = 0;
        জরিমানা = 0;

        (* Addrtable [opcode]) ();
        (* Optable [opcode]) ();
        ঘড়িঘড়ি 6502 + = টিকটবল [ওপকোড];
        যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502 ++;

        নির্দেশাবলীর ++,;

        যদি (কালেক্সেন্টাল) (* লুপেক্সটার্নাল) ();
    }

}

অকার্যকর পদক্ষেপ 6502 () {
    অপকোড = রিড6502 (পিসি ++);

    পেনাল্টিপ = 0;
    জরিমানা = 0;

    (* Addrtable [opcode]) ();
    (* Optable [opcode]) ();
    ঘড়িঘড়ি 6502 + = টিকটবল [ওপকোড];
    যদি (পেনাল্টিপ && পেনাল্টিএডিআর) ক্লকটিক্স 6502 ++;
    ক্লকগোয়াল6502 = ক্লকটিক্স 6502;

    নির্দেশাবলীর ++,;

    যদি (কালেক্সেন্টাল) (* লুপেক্সটার্নাল) ();
}

অকার্যকর hookextern (শূন্য * মজাদার) {
    যদি (ফানকપ્টার! = (শূন্য *) নাল) {
        লুপেক্সটার্নাল = ফানকপিটার;
        ক্যালেক্সার্নাল = 1;
    cal অন্যদিকে কলম্বীয় = 0;
}

এফওয়াইআই, আপনি যদি মার্কিং কোডের কোডডাউন পদ্ধতিটি ব্যবহার করেন (চারটি স্পেস দ্বারা ইন্ডেন্ট), এটি একটি স্ক্রিন-আকারের স্ক্রোলযোগ্য অঞ্চলে হবে; এবং আপনাকে এইচটিএমএল-আইজ <ট্যাগ বন্ধনী> করার দরকার নেই। ... তবে এই উত্তরের জন্য, আমি আসলে এটির মতো আরও ভাল বলে মনে করি। একটি রেফারেন্স-বাস্তবায়ন হিসাবে, এটি খুব ভাল ব্যবহারে লাগে এমন জায়গা রাখে। ... যদি / যখন আরও উত্তর আসে, আপনি 4-স্পেস ইনডেন্টে স্যুইচ করতে চাইতে পারেন যাতে এটি পৃষ্ঠায় আধিপত্য বজায় না করে। $ 0.02 ... প্রশ্নটি ভালবাসুন ... +1 +1 +1! আমি আমার উপর কাজ করছি, আপনি চিন্তা করবেন না! :)
luser droog

21

হাস্কেলের একটি এমওএস 6502 এমুলেটর। বৈশিষ্ট্য অন্তর্ভুক্ত:

  • সূক্ষ্ম পি রেজিস্টার হ্যান্ডলিং এবং ইনডেক্সিং এবং ইন্ডিরিয়েশনের সময় পৃষ্ঠা মোড়ানো সহ কিছুটা যথাযথ বাস্তবায়ন
  • স্পিন লুপ সনাক্তকরণ সহ স্মৃতি ম্যাপ করা আইও (সুতরাং ইনপুটটির জন্য অপেক্ষা করার সময় হোস্ট সিপিইউ প্যাগ করে না)
  • থামানো সনাক্তকরণ (ঝাঁপ / শাখা নিজে থেকে)
  • সিপিইউ ঠিক 200 লাইন এবং কোডের 6502 টি অক্ষরে প্রয়োগ করা হয়েছে
  • সিপিইউ বাস্তবায়ন খাঁটি রাষ্ট্র মনাদ

এটি সম্পূর্ণ বাস্তবায়নের কিছুটা গল্ফ'আর সংস্করণ (আরও বৈশিষ্ট্য সহ) আমি এই চ্যালেঞ্জটির জন্য করেছি যা আমি পরে পোস্ট করব। গল্ফ সত্ত্বেও, কোডটি এখনও সরাসরি এগিয়ে রয়েছে। কেবল পরিচিত অনুপস্থিত বৈশিষ্ট্য হ'ল বিসিডি মোড (আসছে ...)

এহবাসাসিক কোড চালায়:

& ghc -O2 -o z6502min -Wall -fwarn-tabs -fno-warn-missing-signatures Z6502.hs
[1 of 1] Compiling Main             ( Z6502.hs, Z6502.o )

Z6502.hs:173:1: Warning: Defined but not used: `nmi'

Z6502.hs:174:1: Warning: Defined but not used: `irq'
Linking z6502min ...

& ./z6502min ehbasic.bin 
6502 EhBASIC [C]old/[W]arm ?

Memory size ? 

48383 Bytes free

Enhanced BASIC 2.22

Ready
PRINT "Hello World"
Hello World

Ready
10 FOR I = 1 TO 10
20 FOR J = 1 TO I
30 PRINT J;
40 NEXT J
50 PRINT
60 NEXT I
RUN
 1
 1 2
 1 2 3
 1 2 3 4
 1 2 3 4 5
 1 2 3 4 5 6
 1 2 3 4 5 6 7
 1 2 3 4 5 6 7 8
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9 10

Ready

এবং কোডটি 300 টি লাইনের নিচে থাকবে:

-- Z6502: a 6502 emulator
-- by Mark Lentczner

module Main (main) where

import Control.Applicative
import Control.Monad
import Control.Monad.State.Strict
import Data.Bits
import qualified Data.ByteString as B
import Data.List
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import Data.Word
import System.Environment
import System.IO

{- === CPU: 200 lines, 6502 characters === -}
type Addr = Word16
toAd = fromIntegral :: Int -> Addr
addr :: Word8 -> Word8 -> Addr
addr lo hi = fromIntegral hi `shiftL` 8 .|. fromIntegral lo
lohi ad = (fromIntegral ad, fromIntegral $ ad `shiftR` 8)
zeroPage v = addr v 0
index ad idx = ad + fromIntegral (idx :: Word8)
relativeAddr ad off = index ad off - if off > 0x7f then 256 else 0

data Page = Missing | ROM !B.ByteString | RAM !(VU.Vector Word8)
type Memory = V.Vector Page
emptyMemory = V.replicate 256 Missing

fetchByte ad mv = case mv V.! hi of
    ROM bs -> B.index bs lo
    RAM vs -> vs VU.! lo
    _ -> 0
  where (hi,lo) = fromIntegral ad `divMod` 256
storeByte ad v mv = case mv V.! hi of
    RAM vs -> mv V.// [(hi, RAM $ vs VU.// [(lo, v)])]
    _ -> mv
  where (hi,lo) = fromIntegral ad `divMod` 256

data S = S { rA, rX, rY, rP, rS :: !Word8, rPC :: !Addr
           , mem :: !Memory, busR,busW :: Maybe Addr }
powerOnState = S 0 0 0 0 0 0 emptyMemory Nothing Nothing

[bitN, bitV, bitX, bitB, bitD, bitI, bitZ, bitC] = [7,6..0]
toBit b t v = (if t then setBit else clearBit) v b
toZ v = toBit bitZ (v == 0)
toZN v = toBit bitZ (v == 0) . toBit bitN (testBit v 7)
to67 v = toBit bitV (testBit v 6) . toBit bitN (testBit v 7)

setZN v = modify $ \s -> s { rP = toZN v $ rP s }
setAZN v = modify $ \s -> s { rA = v, rP=toZN v $ rP s }
setXZN v = modify $ \s -> s { rX = v, rP=toZN v $ rP s }
setYZN v = modify $ \s -> s { rY = v, rP=toZN v $ rP s }
setZVNbit (a,v) = modify $ \s -> s { rP = toZ (a .&. v) $ to67 v $ rP s }
setACZVN (c,v,a) = modify $ \s ->
    s { rA = a, rP = toBit bitC c $ toBit bitV v $ toZN a $ rP s }
setCZN (c,v) = modify $ \s -> s { rP = toBit bitC c $ toZN v $ rP s }

fetch a = state $ \s -> (fetchByte a $ mem s, s { busR = Just a })
fetchIndirectAddr a0 = do
    m <- gets mem
    let (lo,hi) = lohi a0
        a1 = addr (lo+1) hi
        bLo = fetchByte a0 m
        bHi = fetchByte a1 m
    return $ addr bLo bHi
store a v = modify $ \s -> s { mem = storeByte a v $ mem s, busW = Just a }

clearBus = modify $ \s -> s { busR = Nothing, busW = Nothing }
nextPC = state $ \s -> (rPC s, s { rPC = rPC s + 1 })
fetchPC = nextPC >>= \a -> gets mem >>= return . fetchByte a

adjSP n m = state $ \s -> (addr (rS s + m) 1, s { rS = rS s + n })
push v = adjSP (-1) 0 >>= flip store v
pull = adjSP 1 1 >>= fetch
pushAddr a = let (lo, hi) = lohi a in push hi >> push lo
pullAddr = addr <$> pull <*> pull
pushP fromSW = gets rP >>= push . toBit bitX True . toBit bitB fromSW
pullP = pull >>= \v -> modify $ \s -> s { rP = v .&. 0xCF }

indexX a = gets rX >>= return . index a
indexY a = gets rY >>= return . index a
aImm=nextPC
aZero=zeroPage<$>fetchPC
aZeroX=zeroPage<$>((+)<$>fetchPC<*>gets rX)
aZeroY=zeroPage<$>((+)<$>fetchPC<*>gets rY)
aRel=flip relativeAddr<$>fetchPC<*>gets rPC
aAbs=addr<$>fetchPC<*>fetchPC
aAbsX=aAbs>>=indexX
aAbsY=aAbs>>=indexY
aInd=aAbs>>=fetchIndirectAddr
aIndIdx=aZeroX>>=fetchIndirectAddr
aIdxInd=aZero>>=fetchIndirectAddr>>=indexY

decode = V.fromList $ concat $ transpose
 [[iBRK,iBPL,iJSR&aAbs,iBMI,iRTI,iBVC,iRTS,iBVS
  ,iErr,iBCC,iLDY&aImm,iBCS,iCPY&aImm,iBNE,iCPX&aImm,iBEQ]
 ,cAlu aIndIdx aIdxInd
 ,cErr//(10,iLDX&aImm)
 ,cErr
 ,[iErr,iErr,iBIT&aZero,iErr,iErr,iErr,iErr,iErr
  ,iSTY&aZero,iSTY&aZeroX,iLDY&aZero,iLDY&aZeroX,iCPY&aZero,iErr,iCPX&aZero,iErr]
 ,cAlu aZero aZeroX
 ,cBit aZero aZeroX//(9,iSTX&aZeroY)//(11,iLDX&aZeroY)
 ,cErr
 ,[iPHP,iCLC,iPLP,iSEC,iPHA,iCLI,iPLA,iSEI,iDEY,iTYA,iTAY,iCLV,iINY,iCLD,iINX,iSED]
 ,cAlu aImm aAbsY//(8,iErr)
 ,[iASLa,iErr,iROLa,iErr,iLSRa,iErr,iRORa,iErr
  ,iTXA,iTXS,iTAX,iTSX,iDEX,iErr,iNOP,iErr ]
 ,cErr
 ,[iErr,iErr,iBIT&aAbs,iErr,iJMP&aAbs,iErr,iJMP&aInd,iErr
  ,iSTY&aAbs,iErr,iLDY&aAbs,iLDY&aAbsX,iCPY&aAbs,iErr,iCPX&aAbs,iErr]
 ,cAlu aAbs aAbsX
 ,cBit aAbs aAbsX//(9,iErr)//(11,iLDX&aAbsY)
 ,cErr
 ]
cAlt is e o = is >>= (\i->[i&e,i&o])
cAlu = cAlt [iORA,iAND,iEOR,iADC,iSTA,iLDA,iCMP,iSBC]
cBit = cAlt [iASL,iROL,iLSR,iROR,iSTX,iLDX,iDEC,iINC]
cErr = replicate 16 iErr
is//(n,j) = let (f,_:h) = splitAt n is in f++j:h
i&a=a>>=i

loadIns l a = fetch a >>= l
storeIns f a = f >>= store a

aluIns set op ad = do
    v <- fetch ad
    a <- gets rA
    set $ op a v

modIns op a = fetch a >>= op >>= store a
modAccIns op = gets rA >>= op >>= \v -> modify $ \s -> s { rA = v }

stIns b op = modify $ \s -> s { rP = op (rP s) b }

jump a = modify $ \s -> s { rPC = a }
brIns b t = do
    a <- aRel
    p <- gets rP
    when (testBit p b == t) $ jump a

adcOp a b cIn = (cOut, v, s)
  where
    h = b + (if cIn then 1 else 0)
    s = a + h
    cOut = h < b || s < a
    v = testBit (a `xor` s .&. b `xor` s) 7
sbcOp a b cIn = adcOp a (complement b) cIn
carryOp f = gets rP >>= setACZVN . f . flip testBit bitC

cmpOp a b = (a >= b, a - b)

shiftOp shifter isRot inBit outBit v = do
    s <- get
    let newC = testBit v outBit
        bitIn = toBit inBit $ isRot && testBit (rP s) bitC
        v' = bitIn $ shifter v 1
    put s { rP = toBit bitC newC $ toZN v' $ rP s }
    return v'

vector a = fetchIndirectAddr a >>= jump

interrupt isBrk pcOffset a = do
    gets rPC >>= pushAddr . flip index pcOffset
    pushP isBrk
    iSEI
    vector a

reset = vector $ toAd 0xFFFC
nmi = interrupt False 0 $ toAd 0xFFFA
irq = interrupt False 0 $ toAd 0xFFFE

[iORA,iAND,iEOR]=aluIns setAZN<$>[(.|.),(.&.),xor]
[iADC,iSBC]=aluIns carryOp<$>[adcOp,sbcOp]
iSTA=storeIns$gets rA
iLDA=loadIns setAZN
iCMP=aluIns setCZN cmpOp

[iSTX,iSTY]=storeIns.gets<$>[rX,rY]
[iLDX,iLDY]=loadIns<$>[setXZN,setYZN]
[iCPX,iCPY]=(\r a->gets r>>= \v->fetch a>>=setCZN.cmpOp v)<$>[rX,rY]
[iDEC,iINC]=modIns.(\i v->setZN(v+i)>>return(v+i))<$>[-1,1]
[iDEX,iINX]=(gets rX>>=).(setXZN.).(+)<$>[-1,1]
[iDEY,iINY]=(gets rY>>=).(setYZN.).(+)<$>[-1,1]

shOps=[shiftOp d r b(7-b)|(d,b)<-[(shiftL,0),(shiftR,7)],r<-[False,True]]
[iASL,iROL,iLSR,iROR]=modIns<$>shOps
[iASLa,iROLa,iLSRa,iRORa]=modAccIns<$>shOps

iBIT=aluIns setZVNbit(,)
iJMP=jump

[iBPL,iBMI,iBVC,iBVS,iBCC,iBCS,iBNE,iBEQ]=brIns<$>[bitN,bitV,bitC,bitZ]<*>[False,True]
[iCLC,iSEC,iCLI,iSEI,iCLV,_,iCLD,iSED]=stIns<$>[bitC,bitI,bitV,bitD]<*>[clearBit,setBit]

iBRK=interrupt True 1 $ toAd 0xFFFE
iJSR a=gets rPC>>=pushAddr.(-1+)>>jump a
iRTI=iPLP>>pullAddr>>=jump
iRTS=pullAddr>>=jump.(1+)

iPHP=pushP True
iPLP=pullP
iPHA=gets rA>>=push
iPLA=pull>>=setAZN

iNOP=return ()

[iTAX,iTAY]=(gets rA>>=)<$>[setXZN,setYZN]
[iTXA,iTYA]=(>>=setAZN).gets<$>[rX,rY]
iTXS=modify $ \s -> s { rS=rX s }
iTSX=gets rS>>=setXZN

iErr=gets rPC>>=jump.(-1+)

executeOne = clearBus >> fetchPC >>= (decode V.!) . fromIntegral
{- === END OF CPU === -}


{- === MOTHERBOARD === -}
buildMemory rom =
    loadRAM 0xF0 1 $ loadRAM 0x00 ramSize $ loadROM romStart rom $ emptyMemory
  where
    ramSize = 256 - (B.length rom `div` 256)
    romStart = fromIntegral ramSize

    loadRAM p0 n = (V.// zip [p0..] (map RAM $ replicate n ramPage))
    ramPage = VU.replicate 256 0

    loadROM p0 bs = (V.// zip [p0..] (map ROM $ romPages bs))
    romPages b = case B.length b of
        l | l == 0    -> []
          | l < 256   -> [b `B.append` B.replicate (256 - l) 0]
          | l == 256  -> [b]
          | otherwise -> let (b0,bn) = B.splitAt 256 b in b0 : romPages bn

main = getArgs >>= go
  where
    go [romFile] = B.readFile romFile >>= exec . buildState . buildMemory
    go _ = putStrLn "agument should be a single ROM file"

    buildState m = execState reset (powerOnState { mem = m })

    exec s0 = do
        stopIO <- startIO
        loop (0 :: Int) s0
        stopIO

    loop n s = do
        let pcsp = (rPC s, rS s)
        (n',s') <- processIO n (execState executeOne s)
        let pcsp' = (rPC s', rS s')
        if pcsp /= pcsp'
            then (loop $! n') $! s'
            else do
                putStrLn $ "Execution snagged at " ++ show (fst pcsp')

    startIO = do
        ibuf <- hGetBuffering stdin
        obuf <- hGetBuffering stdout
        iecho <- hGetEcho stdin
        hSetBuffering stdin NoBuffering
        hSetBuffering stdout NoBuffering
        hSetEcho stdin False
        return $ do
            hSetEcho stdin iecho
            hSetBuffering stdin ibuf
            hSetBuffering stdout obuf
            putStr "\n\n"

    processIO n s = do
        when (busW s == Just outPortAddr) $ do
            let c = fetchByte outPortAddr $ mem s
            when (c /= 0) $ hPutChar stdout $ toEnum $ fromIntegral c
        if (busR s == Just inPortAddr)
            then do
                r <- if n < 16
                        then hWaitForInput stdin 50
                        else hReady stdin
                c <- if r then (fromIntegral . fromEnum) <$> hGetChar stdin else return 0
                let c' = if c == 0xA then 0xD else c
                let s' = s { mem = storeByte inPortAddr c' $ mem s }
                return (0,s')
            else return (n+1,s)

    inPortAddr = toAd 0xF004
    outPortAddr = toAd 0xF001

5
ভাল কাজ! খুব ছোট. আমি হাস্কেলকে চিনি না, সম্ভবত আমার শেখা উচিত। আমি সত্য যে এটি 6502 অক্ষর ভালবাসে। :)
মাইক সি

6

আগ্রহী যে কারও জন্য আমি ভেবেছিলাম যে আমি সি # তে আমার বাস্তবায়নটি 6502 ভাগ করে নেব। এখানে অন্যান্য পোস্টগুলির মতো এটি সম্পূর্ণরূপে নিয়মিত নয় তবে এটি সম্পূর্ণরূপে বৈশিষ্ট্যযুক্ত।

  • এনএমওএস এবং সিএমওএস সমর্থন করে
  • উপরে ইউনিট টেস্ট হিসাবে অলসুইট পরীক্ষা সহ বেশ কয়েকটি পরীক্ষার প্রোগ্রাম অন্তর্ভুক্ত।
  • বিসিডি সমর্থন করে

আমি যখন সিপিইউ সম্পর্কে প্রথম শিখছিলাম তখন আমি নির্দেশগুলির একটি স্প্রেডশিট তৈরি করে এই প্রকল্পটি শুরু করেছি। আমি বুঝতে পেরেছি যে আমি নিজের স্পর্শপত্রকে কিছু টাইপ করে সংরক্ষণ করতে পারি। আমি এটিকে এমন একটি পাঠ্য ফাইল সারণিতে পরিণত করেছি যা চক্র গণনা করতে সহায়তা করার জন্য এবং সহজে বিযুক্ত করার আউটপুট জন্য এমুলেটর লোড করে।

সমস্ত প্রকল্প গিথুব https://github.com/amensch/e6502 এ উপলব্ধ

/*
 * e6502: A complete 6502 CPU emulator.
 * Copyright 2016 Adam Mensch
 */

using System;

namespace e6502CPU
{
    public enum e6502Type
    {
        CMOS,
        NMOS
    };

    public class e6502
    {
        // Main Register
        public byte A;

        // Index Registers
        public byte X;
        public byte Y;

        // Program Counter
        public ushort PC;

        // Stack Pointer
        // Memory location is hard coded to 0x01xx
        // Stack is descending (decrement on push, increment on pop)
        // 6502 is an empty stack so SP points to where next value is stored
        public byte SP;

        // Status Registers (in order bit 7 to 0)
        public bool NF;    // negative flag (N)
        public bool VF;    // overflow flag (V)
                           // bit 5 is unused
                           // bit 4 is the break flag however it is not a physical flag in the CPU
        public bool DF;    // binary coded decimal flag (D)
        public bool IF;    // interrupt flag (I)
        public bool ZF;    // zero flag (Z)
        public bool CF;    // carry flag (C)

        // RAM - 16 bit address bus means 64KB of addressable memory
        public byte[] memory;

        // List of op codes and their attributes
        private OpCodeTable _opCodeTable;

        // The current opcode
        private OpCodeRecord _currentOP;

        // Clock cycles to adjust due to page boundaries being crossed, branches taken, or NMOS/CMOS differences
        private int _extraCycles;

        // Flag for hardware interrupt (IRQ)
        public bool IRQWaiting { get; set; }

        // Flag for non maskable interrupt (NMI)
        public bool NMIWaiting { get; set; }

        public e6502Type _cpuType { get; set; }

        public e6502(e6502Type type)
        {
            memory = new byte[0x10000];
            _opCodeTable = new OpCodeTable();

            // Set these on instantiation so they are known values when using this object in testing.
            // Real programs should explicitly load these values before using them.
            A = 0;
            X = 0;
            Y = 0;
            SP = 0;
            PC = 0;
            NF = false;
            VF = false;
            DF = false;
            IF = true;
            ZF = false;
            CF = false;
            NMIWaiting = false;
            IRQWaiting = false;
            _cpuType = type;
        }

        public void Boot()
        {
            // On reset the addresses 0xfffc and 0xfffd are read and PC is loaded with this value.
            // It is expected that the initial program loaded will have these values set to something.
            // Most 6502 systems contain ROM in the upper region (around 0xe000-0xffff)
            PC = GetWordFromMemory(0xfffc);

            // interrupt disabled is set on powerup
            IF = true;

            NMIWaiting = false;
            IRQWaiting = false;
        }

        public void LoadProgram(ushort startingAddress, byte[] program)
        {
            program.CopyTo(memory, startingAddress);
            PC = startingAddress;
        }

        public string DasmNextInstruction()
        {
            OpCodeRecord oprec = _opCodeTable.OpCodes[ memory[PC] ];
            if (oprec.Bytes == 3)
                return oprec.Dasm( GetImmWord() );
            else
                return oprec.Dasm( GetImmByte() );
        }

        // returns # of clock cycles needed to execute the instruction
        public int ExecuteNext()
        {
            _extraCycles = 0;

            // Check for non maskable interrupt (has higher priority over IRQ)
            if (NMIWaiting)
            {
                DoIRQ(0xfffa);
                NMIWaiting = false;
                _extraCycles += 6;
            }
            // Check for hardware interrupt, if enabled
            else if (!IF)
            {
                if(IRQWaiting)
                {
                    DoIRQ(0xfffe);
                    IRQWaiting = false;
                    _extraCycles += 6;
                }
            }

            _currentOP = _opCodeTable.OpCodes[memory[PC]];

            ExecuteInstruction();

            return _currentOP.Cycles + _extraCycles;
        }

        private void ExecuteInstruction()
        {
            int result;
            int oper = GetOperand(_currentOP.AddressMode);

            switch (_currentOP.OpCode)
            {
                // ADC - add memory to accumulator with carry
                // A+M+C -> A,C (NZCV)
                case 0x61:
                case 0x65:
                case 0x69:
                case 0x6d:
                case 0x71:
                case 0x72:
                case 0x75:
                case 0x79:
                case 0x7d:

                    if (DF)
                    {
                        result = HexToBCD(A) + HexToBCD((byte)oper);
                        if (CF) result++;

                        CF = (result > 99);

                        if (result > 99 )
                        {
                            result -= 100;
                        }
                        ZF = (result == 0);

                        // convert decimal result to hex BCD result
                        A = BCDToHex(result);

                        // Unlike ZF and CF, the NF flag represents the MSB after conversion
                        // to BCD.
                        NF = (A > 0x7f);

                        // extra clock cycle on CMOS in decimal mode
                        if (_cpuType == e6502Type.CMOS)
                            _extraCycles++;
                    }
                    else
                    {
                        ADC((byte)oper);
                    }
                    PC += _currentOP.Bytes;
                    break;

                // AND - and memory with accumulator
                // A AND M -> A (NZ)
                case 0x21:
                case 0x25:
                case 0x29:
                case 0x2d:
                case 0x31:
                case 0x32:
                case 0x35:
                case 0x39:
                case 0x3d:
                    result = A & oper;

                    NF = ((result & 0x80) == 0x80);
                    ZF = ((result & 0xff) == 0x00);

                    A = (byte)result;
                    PC += _currentOP.Bytes;
                    break;

                // ASL - shift left one bit (NZC)
                // C <- (76543210) <- 0

                case 0x06:
                case 0x16:
                case 0x0a:
                case 0x0e:
                case 0x1e:

                    // On 65C02 (abs,X) takes one less clock cycle (but still add back 1 if page boundary crossed)
                    if (_currentOP.OpCode == 0x1e && _cpuType == e6502Type.CMOS)
                        _extraCycles--;

                    // shift bit 7 into carry
                    CF = (oper >= 0x80);

                    // shift operand
                    result = oper << 1;

                    NF = ((result & 0x80) == 0x80);
                    ZF = ((result & 0xff) == 0x00);

                    SaveOperand(_currentOP.AddressMode, result);
                    PC += _currentOP.Bytes;

                    break;

                // BBRx - test bit in memory (no flags)
                // Test the zero page location and branch of the specified bit is clear
                // These instructions are only available on Rockwell and WDC 65C02 chips.
                // Number of clock cycles is the same regardless if the branch is taken.
                case 0x0f:
                case 0x1f:
                case 0x2f:
                case 0x3f:
                case 0x4f:
                case 0x5f:
                case 0x6f:
                case 0x7f:

                    // upper nibble specifies the bit to check
                    byte check_bit = (byte)(_currentOP.OpCode >> 4);
                    byte check_value = 0x01;
                    for( int ii=0; ii < check_bit; ii++)
                    {
                        check_value = (byte)(check_value << 1);
                    }

                    // if the specified bit is 0 then branch
                    byte offset = memory[PC + 2];
                    PC += _currentOP.Bytes;

                    if ((oper & check_value) == 0x00)
                        PC += offset;

                    break;

                // BBSx - test bit in memory (no flags)
                // Test the zero page location and branch of the specified bit is set
                // These instructions are only available on Rockwell and WDC 65C02 chips.
                // Number of clock cycles is the same regardless if the branch is taken.
                case 0x8f:
                case 0x9f:
                case 0xaf:
                case 0xbf:
                case 0xcf:
                case 0xdf:
                case 0xef:
                case 0xff:

                    // upper nibble specifies the bit to check (but ignore bit 7)
                    check_bit = (byte)((_currentOP.OpCode & 0x70) >> 4);
                    check_value = 0x01;
                    for (int ii = 0; ii < check_bit; ii++)
                    {
                        check_value = (byte)(check_value << 1);
                    }

                    // if the specified bit is 1 then branch
                    offset = memory[PC + 2];
                    PC += _currentOP.Bytes;

                    if ((oper & check_value) == check_value)
                        PC += offset;

                    break;

                // BCC - branch on carry clear
                case 0x90:
                    PC += _currentOP.Bytes;
                    CheckBranch(!CF, oper);
                    break;

                // BCS - branch on carry set
                case 0xb0:
                    PC += _currentOP.Bytes;
                    CheckBranch(CF, oper);
                    break;

                // BEQ - branch on zero
                case 0xf0:
                    PC += _currentOP.Bytes;
                    CheckBranch(ZF, oper);
                    break;

                // BIT - test bits in memory with accumulator (NZV)
                // bits 7 and 6 of oper are transferred to bits 7 and 6 of conditional register (N and V)
                // the zero flag is set to the result of oper AND accumulator
                case 0x24:
                case 0x2c:
                // added by 65C02
                case 0x34:
                case 0x3c:
                case 0x89:
                    result = A & oper;

                    // The WDC programming manual for 65C02 indicates NV are unaffected in immediate mode.
                    // The extended op code test program reflects this.
                    if (_currentOP.AddressMode != AddressModes.Immediate)
                    {
                        NF = ((oper & 0x80) == 0x80);
                        VF = ((oper & 0x40) == 0x40);
                    }

                    ZF = ((result & 0xff) == 0x00);

                    PC += _currentOP.Bytes;
                    break;

                // BMI - branch on negative
                case 0x30:
                    PC += _currentOP.Bytes;
                    CheckBranch(NF, oper);
                    break;

                // BNE - branch on non zero
                case 0xd0:
                    PC += _currentOP.Bytes;
                    CheckBranch(!ZF, oper);
                    break;

                // BPL - branch on non negative
                case 0x10:
                    PC += _currentOP.Bytes;
                    CheckBranch(!NF, oper);
                    break;

                // BRA - unconditional branch to immediate address
                // NOTE: In OpcodeList.txt the number of clock cycles is one less than the documentation.
                // This is because CheckBranch() adds one when a branch is taken, which in this case is always.
                case 0x80:
                    PC += _currentOP.Bytes;
                    CheckBranch(true, oper);
                    break;

                // BRK - force break (I)
                case 0x00:

                    // This is a software interrupt (IRQ).  These events happen in a specific order.

                    // Processor adds two to the current PC
                    PC += 2;

                    // Call IRQ routine
                    DoIRQ(0xfffe, true);

                    // Whether or not the decimal flag is cleared depends on the type of 6502 CPU.
                    // The CMOS 65C02 clears this flag but the NMOS 6502 does not.
                    if( _cpuType == e6502Type.CMOS )
                        DF = false;

                    break;
                // BVC - branch on overflow clear
                case 0x50:
                    PC += _currentOP.Bytes;
                    CheckBranch(!VF, oper);
                    break;

                // BVS - branch on overflow set
                case 0x70:
                    PC += _currentOP.Bytes;
                    CheckBranch(VF, oper);
                    break;

                // CLC - clear carry flag
                case 0x18:
                    CF = false;
                    PC += _currentOP.Bytes;
                    break;

                // CLD - clear decimal mode
                case 0xd8:
                    DF = false;
                    PC += _currentOP.Bytes;
                    break;

                // CLI - clear interrupt disable bit
                case 0x58:
                    IF = false;
                    PC += _currentOP.Bytes;
                    break;

                // CLV - clear overflow flag
                case 0xb8:
                    VF = false;
                    PC += _currentOP.Bytes;
                    break;

                // CMP - compare memory with accumulator (NZC)
                // CMP, CPX and CPY are unsigned comparisions
                case 0xc5:
                case 0xc9:
                case 0xc1:
                case 0xcd:
                case 0xd1:
                case 0xd2:
                case 0xd5:
                case 0xd9:
                case 0xdd:

                    byte temp = (byte)(A - oper);

                    CF = A >= (byte)oper;
                    ZF = A == (byte)oper;
                    NF = ((temp & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;

                // CPX - compare memory and X (NZC)
                case 0xe0:
                case 0xe4:
                case 0xec:
                    temp = (byte)(X - oper);

                    CF = X >= (byte)oper;
                    ZF = X == (byte)oper;
                    NF = ((temp & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;

                // CPY - compare memory and Y (NZC)
                case 0xc0:
                case 0xc4:
                case 0xcc:
                    temp = (byte)(Y - oper);

                    CF = Y >= (byte)oper;
                    ZF = Y == (byte)oper;
                    NF = ((temp & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;

                // DEC - decrement memory by 1 (NZ)
                case 0xc6:
                case 0xce:
                case 0xd6:
                case 0xde:
                // added by 65C02
                case 0x3a:
                    result = oper - 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    SaveOperand(_currentOP.AddressMode, result);

                    PC += _currentOP.Bytes;
                    break;

                // DEX - decrement X by one (NZ)
                case 0xca:
                    result = X - 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    X = (byte)result;
                    PC += _currentOP.Bytes;
                    break;

                // DEY - decrement Y by one (NZ)
                case 0x88:
                    result = Y - 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    Y = (byte)result;
                    PC += _currentOP.Bytes;
                    break;

                // EOR - XOR memory with accumulator (NZ)
                case 0x41:
                case 0x45:
                case 0x49:
                case 0x4d:
                case 0x51:
                case 0x52:
                case 0x55:
                case 0x59:
                case 0x5d:
                    result = A ^ (byte)oper;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    A = (byte)result;

                    PC += _currentOP.Bytes;
                    break;

                // INC - increment memory by 1 (NZ)
                case 0xe6:
                case 0xee:
                case 0xf6:
                case 0xfe:
                // added by 65C02
                case 0x1a:
                    result = oper + 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    SaveOperand(_currentOP.AddressMode, result);

                    PC += _currentOP.Bytes;
                    break;

                // INX - increment X by one (NZ)
                case 0xe8:
                    result = X + 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    X = (byte)result;
                    PC += _currentOP.Bytes;
                    break;

                // INY - increment Y by one (NZ)
                case 0xc8:
                    result = Y + 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    Y = (byte)result;
                    PC += _currentOP.Bytes;
                    break;

                // JMP - jump to new location (two byte immediate)
                case 0x4c:
                case 0x6c:
                // added for 65C02
                case 0x7c:

                    if (_currentOP.AddressMode == AddressModes.Absolute)
                    {
                        PC = GetImmWord();
                    }
                    else if (_currentOP.AddressMode == AddressModes.Indirect)
                    {
                        PC = (ushort)(GetWordFromMemory(GetImmWord()));
                    }
                    else if( _currentOP.AddressMode == AddressModes.AbsoluteX)
                    {
                        PC = GetWordFromMemory((GetImmWord() + X));
                    }
                    else
                    {
                        throw new InvalidOperationException("This address mode is invalid with the JMP instruction");
                    }

                    // CMOS fixes a bug in this op code which results in an extra clock cycle
                    if (_currentOP.OpCode == 0x6c && _cpuType == e6502Type.CMOS)
                        _extraCycles++;
                    break;

                // JSR - jump to new location and save return address
                case 0x20:
                    // documentation says push PC+2 even though this is a 3 byte instruction
                    // When pulled via RTS 1 is added to the result
                    Push((ushort)(PC+2));  
                    PC = GetImmWord();
                    break;

                // LDA - load accumulator with memory (NZ)
                case 0xa1:
                case 0xa5:
                case 0xa9:
                case 0xad:
                case 0xb1:
                case 0xb2:
                case 0xb5:
                case 0xb9:
                case 0xbd:
                    A = (byte)oper;

                    ZF = ((A & 0xff) == 0x00);
                    NF = ((A & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;

                // LDX - load index X with memory (NZ)
                case 0xa2:
                case 0xa6:
                case 0xae:
                case 0xb6:
                case 0xbe:
                    X = (byte)oper;

                    ZF = ((X & 0xff) == 0x00);
                    NF = ((X & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;

                // LDY - load index Y with memory (NZ)
                case 0xa0:
                case 0xa4:
                case 0xac:
                case 0xb4:
                case 0xbc:
                    Y = (byte)oper;

                    ZF = ((Y & 0xff) == 0x00);
                    NF = ((Y & 0x80) == 0x80);

                    PC += _currentOP.Bytes;
                    break;


                // LSR - shift right one bit (NZC)
                // 0 -> (76543210) -> C
                case 0x46:
                case 0x4a:
                case 0x4e:
                case 0x56:
                case 0x5e:

                    // On 65C02 (abs,X) takes one less clock cycle (but still add back 1 if page boundary crossed)
                    if (_currentOP.OpCode == 0x5e && _cpuType == e6502Type.CMOS)
                        _extraCycles--;

                    // shift bit 0 into carry
                    CF = ((oper & 0x01) == 0x01);

                    // shift operand
                    result = oper >> 1;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    SaveOperand(_currentOP.AddressMode, result);

                    PC += _currentOP.Bytes;
                    break;

                // NOP - no operation
                case 0xea:
                    PC += _currentOP.Bytes;
                    break;

                // ORA - OR memory with accumulator (NZ)
                case 0x01:
                case 0x05:
                case 0x09:
                case 0x0d:
                case 0x11:
                case 0x12:
                case 0x15:
                case 0x19:
                case 0x1d:
                    result = A | (byte)oper;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);

                    A = (byte)result;

                    PC += _currentOP.Bytes;
                    break;

                // PHA - push accumulator on stack
                case 0x48:
                    Push(A);
                    PC += _currentOP.Bytes;
                    break;

                // PHP - push processor status on stack
                case 0x08:
                    int sr = 0x00;

                    if (NF) sr = sr | 0x80;
                    if (VF) sr = sr | 0x40;
                    sr = sr | 0x20; // bit 5 is always 1
                    sr = sr | 0x10; // bit 4 is always 1 for PHP
                    if (DF) sr = sr | 0x08;
                    if (IF) sr = sr | 0x04;
                    if (ZF) sr = sr | 0x02;
                    if (CF) sr = sr | 0x01;

                    Push((byte)sr);
                    PC += _currentOP.Bytes;
                    break;

                // PHX - push X on stack
                case 0xda:
                    Push(X);
                    PC += _currentOP.Bytes;
                    break;

                // PHY - push Y on stack
                case 0x5a:
                    Push(Y);
                    PC += _currentOP.Bytes;
                    break;

                // PLA - pull accumulator from stack (NZ)
                case 0x68:
                    A = PopByte();
                    NF = (A & 0x80) == 0x80;
                    ZF = (A & 0xff) == 0x00;
                    PC += _currentOP.Bytes;
                    break;

                // PLP - pull status from stack
                case 0x28:
                    sr = PopByte();

                    NF = (sr & 0x80) == 0x80;
                    VF = (sr & 0x40) == 0x40;
                    DF = (sr & 0x08) == 0x08;
                    IF = (sr & 0x04) == 0x04;
                    ZF = (sr & 0x02) == 0x02;
                    CF = (sr & 0x01) == 0x01;
                    PC += _currentOP.Bytes;
                    break;

                // PLX - pull X from stack (NZ)
                case 0xfa:
                    X = PopByte();
                    NF = (X & 0x80) == 0x80;
                    ZF = (X & 0xff) == 0x00;
                    PC += _currentOP.Bytes;
                    break;

                // PLY - pull Y from stack (NZ)
                case 0x7a:
                    Y = PopByte();
                    NF = (Y & 0x80) == 0x80;
                    ZF = (Y & 0xff) == 0x00;
                    PC += _currentOP.Bytes;
                    break;

                // RMBx - clear bit in memory (no flags)
                // Clear the zero page location of the specified bit
                // These instructions are only available on Rockwell and WDC 65C02 chips.
                case 0x07:
                case 0x17:
                case 0x27:
                case 0x37:
                case 0x47:
                case 0x57:
                case 0x67:
                case 0x77:

                    // upper nibble specifies the bit to check
                     check_bit = (byte)(_currentOP.OpCode >> 4);
                     check_value = 0x01;
                    for (int ii = 0; ii < check_bit; ii++)
                    {
                        check_value = (byte)(check_value << 1);
                    }
                    check_value = (byte)~check_value;
                    SaveOperand(_currentOP.AddressMode, oper & check_value);
                    PC += _currentOP.Bytes;
                    break;

                // SMBx - set bit in memory (no flags)
                // Set the zero page location of the specified bit
                // These instructions are only available on Rockwell and WDC 65C02 chips.
                case 0x87:
                case 0x97:
                case 0xa7:
                case 0xb7:
                case 0xc7:
                case 0xd7:
                case 0xe7:
                case 0xf7:

                    // upper nibble specifies the bit to check (but ignore bit 7)
                    check_bit = (byte)((_currentOP.OpCode & 0x70) >> 4);
                    check_value = 0x01;
                    for (int ii = 0; ii < check_bit; ii++)
                    {
                        check_value = (byte)(check_value << 1);
                    }
                    SaveOperand(_currentOP.AddressMode, oper | check_value);
                    PC += _currentOP.Bytes;
                    break;

                // ROL - rotate left one bit (NZC)
                // C <- 76543210 <- C
                case 0x26:
                case 0x2a:
                case 0x2e:
                case 0x36:
                case 0x3e:

                    // On 65C02 (abs,X) takes one less clock cycle (but still add back 1 if page boundary crossed)
                    if (_currentOP.OpCode == 0x3e && _cpuType == e6502Type.CMOS)
                        _extraCycles--;

                    // perserve existing cf value
                    bool old_cf = CF;

                    // shift bit 7 into carry flag
                    CF = (oper >= 0x80);

                    // shift operand
                    result = oper << 1;

                    // old carry flag goes to bit zero
                    if (old_cf) result = result | 0x01;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);
                    SaveOperand(_currentOP.AddressMode, result);

                    PC += _currentOP.Bytes;
                    break;

                // ROR - rotate right one bit (NZC)
                // C -> 76543210 -> C
                case 0x66:
                case 0x6a:
                case 0x6e:
                case 0x76:
                case 0x7e:

                    // On 65C02 (abs,X) takes one less clock cycle (but still add back 1 if page boundary crossed)
                    if (_currentOP.OpCode == 0x7e && _cpuType == e6502Type.CMOS)
                        _extraCycles--;

                    // perserve existing cf value
                    old_cf = CF;

                    // shift bit 0 into carry flag
                    CF = (oper & 0x01) == 0x01;

                    // shift operand
                    result = oper >> 1;

                    // old carry flag goes to bit 7
                    if (old_cf) result = result | 0x80;

                    ZF = ((result & 0xff) == 0x00);
                    NF = ((result & 0x80) == 0x80);
                    SaveOperand(_currentOP.AddressMode, result);

                    PC += _currentOP.Bytes;
                    break;

                // RTI - return from interrupt
                case 0x40:
                    // pull SR
                    sr = PopByte();

                    NF = (sr & 0x80) == 0x80;
                    VF = (sr & 0x40) == 0x40;
                    DF = (sr & 0x08) == 0x08;
                    IF = (sr & 0x04) == 0x04;
                    ZF = (sr & 0x02) == 0x02;
                    CF = (sr & 0x01) == 0x01;

                    // pull PC
                    PC = PopWord();

                    break;

                // RTS - return from subroutine
                case 0x60:
                    PC = (ushort)(PopWord() + 1);
                    break;

                // SBC - subtract memory from accumulator with borrow (NZCV)
                // A-M-C -> A (NZCV)
                case 0xe1:
                case 0xe5:
                case 0xe9:
                case 0xed:
                case 0xf1:
                case 0xf2:
                case 0xf5:
                case 0xf9:
                case 0xfd:

                    if (DF)
                    {
                        result = HexToBCD(A) - HexToBCD((byte)oper);
                        if (!CF) result--;

                        CF = (result >= 0);

                        // BCD numbers wrap around when subtraction is negative
                        if (result < 0)
                            result += 100;
                        ZF = (result == 0);

                        A = BCDToHex(result);

                        // Unlike ZF and CF, the NF flag represents the MSB after conversion
                        // to BCD.
                        NF = (A > 0x7f);

                        // extra clock cycle on CMOS in decimal mode
                        if (_cpuType == e6502Type.CMOS)
                            _extraCycles++;
                    }
                    else
                    {
                        ADC((byte)~oper);
                    }
                    PC += _currentOP.Bytes;

                    break;

                // SEC - set carry flag
                case 0x38:
                    CF = true;
                    PC += _currentOP.Bytes;
                    break;

                // SED - set decimal mode
                case 0xf8:
                    DF = true;
                    PC += _currentOP.Bytes;
                    break;

                // SEI - set interrupt disable bit
                case 0x78:
                    IF = true;
                    PC += _currentOP.Bytes;
                    break;

                // STA - store accumulator in memory
                case 0x81:
                case 0x85:
                case 0x8d:
                case 0x91:
                case 0x92:
                case 0x95:
                case 0x99:
                case 0x9d:
                    SaveOperand(_currentOP.AddressMode, A);
                    PC += _currentOP.Bytes;
                    break;

                // STX - store X in memory
                case 0x86:
                case 0x8e:
                case 0x96:
                    SaveOperand(_currentOP.AddressMode, X);
                    PC += _currentOP.Bytes;
                    break;

                // STY - store Y in memory
                case 0x84:
                case 0x8c:
                case 0x94:
                    SaveOperand(_currentOP.AddressMode, Y);
                    PC += _currentOP.Bytes;
                    break;

                // STZ - Store zero
                case 0x64:
                case 0x74:
                case 0x9c:
                case 0x9e:
                    SaveOperand(_currentOP.AddressMode, 0);
                    PC += _currentOP.Bytes;
                    break;

                // TAX - transfer accumulator to X (NZ)
                case 0xaa:
                    X = A;
                    ZF = ((X & 0xff) == 0x00);
                    NF = ((X & 0x80) == 0x80);
                    PC += _currentOP.Bytes;
                    break;

                // TAY - transfer accumulator to Y (NZ)
                case 0xa8:
                    Y = A;
                    ZF = ((Y & 0xff) == 0x00);
                    NF = ((Y & 0x80) == 0x80);
                    PC += _currentOP.Bytes;
                    break;

                // TRB - test and reset bits (Z)
                // Perform bitwise AND between accumulator and contents of memory
                case 0x14:
                case 0x1c:
                    SaveOperand(_currentOP.AddressMode, ~A & oper);
                    ZF = (A & oper) == 0x00;
                    PC += _currentOP.Bytes;
                    break;

                // TSB - test and set bits (Z)
                // Perform bitwise AND between accumulator and contents of memory
                case 0x04:
                case 0x0c:
                    SaveOperand(_currentOP.AddressMode, A | oper);
                    ZF = (A & oper) == 0x00;
                    PC += _currentOP.Bytes;
                    break;

                // TSX - transfer SP to X (NZ)
                case 0xba:
                    X = SP;
                    ZF = ((X & 0xff) == 0x00);
                    NF = ((X & 0x80) == 0x80);
                    PC += _currentOP.Bytes;
                    break;

                // TXA - transfer X to A (NZ)
                case 0x8a:
                    A = X;
                    ZF = ((A & 0xff) == 0x00);
                    NF = ((A & 0x80) == 0x80);
                    PC += _currentOP.Bytes;
                    break;

                // TXS - transfer X to SP (no flags -- some online docs are incorrect)
                case 0x9a:
                    SP = X;
                    PC += _currentOP.Bytes;
                    break;

                // TYA - transfer Y to A (NZ)
                case 0x98:
                    A = Y;
                    ZF = ((A & 0xff) == 0x00);
                    NF = ((A & 0x80) == 0x80);
                    PC += _currentOP.Bytes;
                    break;

                // The original 6502 has undocumented and erratic behavior if
                // undocumented op codes are invoked.  The 65C02 on the other hand
                // are guaranteed to be NOPs although they vary in number of bytes
                // and cycle counts.  These NOPs are listed in the OpcodeList.txt file
                // so the proper number of clock cycles are used.
                //
                // Instructions STP (0xdb) and WAI (0xcb) will reach this case.
                // For now these are treated as a NOP.
                default:
                    PC += _currentOP.Bytes;
                    break;
            }
        }

        private int GetOperand(AddressModes mode)
        {
            int oper = 0;
            switch (mode)
            {
                // Accumulator mode uses the value in the accumulator
                case AddressModes.Accumulator:
                    oper = A;
                    break;

                // Retrieves the byte at the specified memory location
                case AddressModes.Absolute:             
                    oper = memory[ GetImmWord() ];
                    break;

                // Indexed absolute retrieves the byte at the specified memory location
                case AddressModes.AbsoluteX:

                    ushort imm = GetImmWord();
                    ushort result = (ushort)(imm + X);

                    if (_currentOP.CheckPageBoundary)
                    {
                        if ((imm & 0xff00) != (result & 0xff00)) _extraCycles += 1;
                    }
                    oper = memory[ result ];
                    break;
                case AddressModes.AbsoluteY:
                    imm = GetImmWord();
                    result = (ushort)(imm + Y);

                    if (_currentOP.CheckPageBoundary)
                    {
                        if ((imm & 0xff00) != (result & 0xff00)) _extraCycles += 1;
                    }
                    oper = memory[result]; break;

                // Immediate mode uses the next byte in the instruction directly.
                case AddressModes.Immediate:
                    oper = GetImmByte();
                    break;

                // Implied or Implicit are single byte instructions that do not use
                // the next bytes for the operand.
                case AddressModes.Implied:
                    break;

                // Indirect mode uses the absolute address to get another address.
                // The immediate word is a memory location from which to retrieve
                // the 16 bit operand.
                case AddressModes.Indirect:
                    oper = GetWordFromMemory(GetImmWord());
                    break;

                // The indexed indirect modes uses the immediate byte rather than the
                // immediate word to get the memory location from which to retrieve
                // the 16 bit operand.  This is a combination of ZeroPage indexed and Indirect.
                case AddressModes.XIndirect:

                    /*
                     * 1) fetch immediate byte
                     * 2) add X to the byte
                     * 3) obtain word from this zero page address
                     * 4) return the byte located at the address specified by the word
                     */

                    oper = memory[GetWordFromMemory( (byte)(GetImmByte() + X))];
                    break;

                // The Indirect Indexed works a bit differently than above.
                // The Y register is added *after* the deferencing instead of before.
                case AddressModes.IndirectY:

                    /*
                        1) Fetch the address (word) at the immediate zero page location
                        2) Add Y to obtain the final target address
                        3)Load the byte at this address
                    */

                    ushort addr = GetWordFromMemory(GetImmByte());
                    oper = memory[addr + Y];

                    if (_currentOP.CheckPageBoundary)
                    {
                        if ((oper & 0xff00) != (addr & 0xff00)) _extraCycles++;
                    }
                    break;


                // Relative is used for branching, the immediate value is a
                // signed 8 bit value and used to offset the current PC.
                case AddressModes.Relative:
                    oper = SignExtend(GetImmByte());
                    break;

                // Zero Page mode is a fast way of accessing the first 256 bytes of memory.
                // Best programming practice is to place your variables in 0x00-0xff.
                // Retrieve the byte at the indicated memory location.
                case AddressModes.ZeroPage:
                    oper = memory[GetImmByte()];
                    break;
                case AddressModes.ZeroPageX:
                    oper = memory[(GetImmByte() + X) & 0xff];
                    break;
                case AddressModes.ZeroPageY:
                    oper = memory[(GetImmByte() + Y) & 0xff];
                    break;

                // this mode is from the 65C02 extended set
                // works like ZeroPageY when Y=0
                case AddressModes.ZeroPage0:
                    oper = memory[GetWordFromMemory((GetImmByte()) & 0xff)];
                    break;

                // for this mode do the same thing as ZeroPage
                case AddressModes.BranchExt:
                    oper = memory[GetImmByte()];
                    break;
                default:
                    break;
            }
            return oper;
        }

        private void SaveOperand(AddressModes mode, int data)
        {
            switch (mode)
            {
                // Accumulator mode uses the value in the accumulator
                case AddressModes.Accumulator:
                    A = (byte)data;
                    break;

                // Absolute mode retrieves the byte at the indicated memory location
                case AddressModes.Absolute:
                    memory[GetImmWord()] = (byte)data;
                    break;
                case AddressModes.AbsoluteX:
                    memory[GetImmWord() + X] = (byte)data;
                    break;
                case AddressModes.AbsoluteY:
                    memory[GetImmWord() + Y] = (byte)data;
                    break;

                // Immediate mode uses the next byte in the instruction directly.
                case AddressModes.Immediate:
                    throw new InvalidOperationException("Address mode " + mode.ToString() + " is not valid for this operation");

                // Implied or Implicit are single byte instructions that do not use
                // the next bytes for the operand.
                case AddressModes.Implied:
                    throw new InvalidOperationException("Address mode " + mode.ToString() + " is not valid for this operation");

                // Indirect mode uses the absolute address to get another address.
                // The immediate word is a memory location from which to retrieve
                // the 16 bit operand.
                case AddressModes.Indirect:
                    throw new InvalidOperationException("Address mode " + mode.ToString() + " is not valid for this operation");

                // The indexed indirect modes uses the immediate byte rather than the
                // immediate word to get the memory location from which to retrieve
                // the 16 bit operand.  This is a combination of ZeroPage indexed and Indirect.
                case AddressModes.XIndirect:
                    memory[GetWordFromMemory((byte)(GetImmByte() + X))] = (byte)data;
                    break;

                // The Indirect Indexed works a bit differently than above.
                // The Y register is added *after* the deferencing instead of before.
                case AddressModes.IndirectY:
                    memory[GetWordFromMemory(GetImmByte()) + Y] = (byte)data;
                    break;

                // Relative is used for branching, the immediate value is a
                // signed 8 bit value and used to offset the current PC.
                case AddressModes.Relative:
                    throw new InvalidOperationException("Address mode " + mode.ToString() + " is not valid for this operation");

                // Zero Page mode is a fast way of accessing the first 256 bytes of memory.
                // Best programming practice is to place your variables in 0x00-0xff.
                // Retrieve the byte at the indicated memory location.
                case AddressModes.ZeroPage:
                    memory[GetImmByte()] = (byte)data;
                    break;
                case AddressModes.ZeroPageX:
                    memory[(GetImmByte() + X) & 0xff] = (byte)data;
                    break;
                case AddressModes.ZeroPageY:
                    memory[(GetImmByte() + Y) & 0xff] = (byte)data;
                    break;
                case AddressModes.ZeroPage0:
                    memory[GetWordFromMemory((GetImmByte()) & 0xff)] = (byte)data;
                    break;

                // for this mode do the same thing as ZeroPage
                case AddressModes.BranchExt:
                    memory[GetImmByte()] = (byte)data;
                    break;

                default:
                    break;
            }
        }

        private ushort GetWordFromMemory(int address)
        {
            return (ushort)((memory[address + 1] << 8 | memory[address]) & 0xffff);
        }

        private ushort GetImmWord()
        {
            return (ushort)((memory[PC + 2] << 8 | memory[PC + 1]) & 0xffff);
        }

        private byte GetImmByte()
        {
            return memory[PC + 1];
        }

        private int SignExtend(int num)
        {
            if (num < 0x80)
                return num;
            else
                return (0xff << 8 | num) & 0xffff;
        }

        private void Push(byte data)
        {
            memory[(0x0100 | SP)] = data;
            SP--;
        }

        private void Push(ushort data)
        {
            // HI byte is in a higher address, LO byte is in the lower address
            memory[(0x0100 | SP)] = (byte)(data >> 8);
            memory[(0x0100 | (SP-1))] = (byte)(data & 0xff);
            SP -= 2;
        }

        private byte PopByte()
        {
            SP++;
            return memory[(0x0100 | SP)];
        }

        private ushort PopWord()
        {
            // HI byte is in a higher address, LO byte is in the lower address
            SP += 2;
            ushort idx = (ushort)(0x0100 | SP);
            return (ushort)((memory[idx] << 8 | memory[idx-1]) & 0xffff);
        }

        private void ADC(byte oper)
        {
            ushort answer = (ushort)(A + oper);
            if (CF) answer++;

            CF = (answer > 0xff);
            ZF = ((answer & 0xff) == 0x00);
            NF = (answer & 0x80) == 0x80;

            //ushort temp = (ushort)(~(A ^ oper) & (A ^ answer) & 0x80);
            VF = (~(A ^ oper) & (A ^ answer) & 0x80) != 0x00;

            A = (byte)answer;
        }

        private int HexToBCD(byte oper)
        {
            // validate input is valid packed BCD 
            if (oper > 0x99)
                throw new InvalidOperationException("Invalid BCD number: " + oper.ToString("X2"));
            if ((oper & 0x0f) > 0x09)
                throw new InvalidOperationException("Invalid BCD number: " + oper.ToString("X2"));

            return ((oper >> 4) * 10) + (oper & 0x0f);
        }

        private byte BCDToHex(int result)
        {
            if (result > 0xff)
                throw new InvalidOperationException("Invalid BCD to hex number: " + result.ToString());

            if (result <= 9)
                return (byte)result;
            else
                return (byte)(((result / 10) << 4) + (result % 10));

        }

        private void DoIRQ(ushort vector)
        {
            DoIRQ(vector, false);
        }

        private void DoIRQ(ushort vector, bool isBRK)
        {
            // Push the MSB of the PC
            Push((byte)(PC >> 8));

            // Push the LSB of the PC
            Push((byte)(PC & 0xff));

            // Push the status register
            int sr = 0x00;
            if (NF) sr = sr | 0x80;
            if (VF) sr = sr | 0x40;

            sr = sr | 0x20;             // bit 5 is unused and always 1

            if(isBRK)
                sr = sr | 0x10;         // software interrupt (BRK) pushes B flag as 1
                                        // hardware interrupt pushes B flag as 0
            if (DF) sr = sr | 0x08;
            if (IF) sr = sr | 0x04;
            if (ZF) sr = sr | 0x02;
            if (CF) sr = sr | 0x01;

            Push((byte)sr);

            // set interrupt disable flag
            IF = true;

            // On 65C02, IRQ, NMI, and RESET also clear the D flag (but not on BRK) after pushing the status register.
            if (_cpuType == e6502Type.CMOS && !isBRK)
                DF = false;

            // load program counter with the interrupt vector
            PC = GetWordFromMemory(vector);
        }

        private void CheckBranch(bool flag, int oper)
        {
            if (flag)
            {
                // extra cycle on branch taken
                _extraCycles++;

                // extra cycle if branch destination is a different page than
                // the next instruction
                if ((PC & 0xff00) != ((PC + oper) & 0xff00))
                    _extraCycles++;

                PC += (ushort)oper;
            }

        }
    }
}

কেউ আপনাকে পিপিসি-তে স্বাগত জানায়নি, আমার ধারণা আমি এই সুযোগটি নিয়ে যাব। এটি একটি দুর্দান্ত প্রথম উত্তর এবং আমি আপনাকে প্রায়শই প্রায়শই দেখতে আশা করি। আনন্দ কর!
স্টান স্ট্রাম

আপনাকে ধন্যবাদ @ স্ট্যানস্ট্রাম! কয়েক বছর আগে এটি একটি এসই পোস্টের পরে একটি 8086 এমুলেটর ছিল যা আমাকে অনুকরণে এবং এই ডিভাইসগুলি কীভাবে বাস্তবে কাজ করে তা শিখতে আগ্রহী করে তোলে। অনেক মজা হয়েছে। উপরের পাশাপাশি, আমার কাছে একটি সম্পূর্ণ 8080 এমুলেটর এবং একটি 8086 রয়েছে যা প্রায় 90% সম্পন্ন।
অ্যাডাম মেনশ

যে ভয়ঙ্কর, আমি একটি এমুলেটর এবং / অথবা midlevel প্রোগ্রামিং ভাষা উপার্জন আগ্রহী চলেছি, কিন্তু আমি সময়, ধৈর্য, অথবা বুদ্ধি তা করার নেই
স্ট্যান Strum
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.